From: Greg Hudson Date: Tue, 24 Jul 2012 20:26:27 +0000 (-0400) Subject: Add internal path expansion functions X-Git-Tag: krb5-1.11-alpha1~386 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7d07dc63a22bfdebc24f0368f969acc4b76d372c;p=thirdparty%2Fkrb5.git Add internal path expansion functions Add an adapted version of Heimdal's expand_path.c, defining k5_expand_path_tokens() and k5_expand_path_tokens_extra(). These functions allow template paths like %{TEMP}/krb5cc_%{uid} to be resolved. Also add a test program to exercise the path expansion code. --- diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index e5acff2d8a..28049e7879 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -101,6 +101,8 @@ k5_ccselect_free_context k5_copy_etypes k5_count_etypes k5_etypes_contains +k5_expand_path_tokens +k5_expand_path_tokens_extra k5_free_serverlist k5_kt_get_principal k5_locate_kdc diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in index 7d07480147..2bbfb3f26d 100644 --- a/src/lib/krb5/os/Makefile.in +++ b/src/lib/krb5/os/Makefile.in @@ -4,7 +4,8 @@ KRB5_RUN_ENV = @KRB5_RUN_ENV@ PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) DEFS= -DEFINES=-DLIBDIR=\"$(KRB5_LIBDIR)\" +DEFINES=-DLIBDIR=\"$(KRB5_LIBDIR)\" -DBINDIR=\"$(CLIENT_BINDIR)\" \ + -DSBINDIR=\"$(ADMIN_BINDIR)\" LOCALINCLUDES=-I$(top_srcdir)/util/profile ##DOS##BUILDTOP = ..\..\.. @@ -21,6 +22,7 @@ STLIBOBJS= \ cm.o \ dnsglue.o \ dnssrv.o \ + expand_path.o \ free_krbhs.o \ full_ipadr.o \ get_krbhst.o \ @@ -65,6 +67,7 @@ OBJS= \ $(OUTPRE)cm.$(OBJEXT) \ $(OUTPRE)dnsglue.$(OBJEXT) \ $(OUTPRE)dnssrv.$(OBJEXT) \ + $(OUTPRE)expand_path.$(OBJEXT) \ $(OUTPRE)free_krbhs.$(OBJEXT) \ $(OUTPRE)full_ipadr.$(OBJEXT) \ $(OUTPRE)get_krbhst.$(OBJEXT) \ @@ -109,6 +112,7 @@ SRCS= \ $(srcdir)/cm.c \ $(srcdir)/dnsglue.c \ $(srcdir)/dnssrv.c \ + $(srcdir)/expand_path.c \ $(srcdir)/free_krbhs.c \ $(srcdir)/full_ipadr.c \ $(srcdir)/get_krbhst.c \ @@ -144,7 +148,7 @@ SRCS= \ $(srcdir)/write_msg.c EXTRADEPSRCS = \ - t_an_to_ln.c t_gifconf.c t_locate_kdc.c \ + t_an_to_ln.c t_expand_path.c t_gifconf.c t_locate_kdc.c \ t_std_conf.c ##DOS##LIBOBJS = $(OBJS) @@ -155,7 +159,7 @@ clean-unix:: clean-libobjs shared: mkdir shared -TEST_PROGS= t_std_conf t_an_to_ln t_kuserok t_locate_kdc t_trace +TEST_PROGS= t_std_conf t_an_to_ln t_kuserok t_locate_kdc t_trace t_expand_path T_STD_CONF_OBJS= t_std_conf.o @@ -188,6 +192,9 @@ $(OUTPRE)t_locate_kdc.exe: $(OUTPRE)t_locate_kdc.obj \ t_trace: $(T_TRACE_OBJS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_trace $(T_TRACE_OBJS) $(KRB5_BASE_LIBS) +t_expand_path: t_expand_path.o $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $@ t_expand_path.o $(KRB5_BASE_LIBS) + LCLINT=lclint LCLINTOPTS= -warnposix \ -usedef +charintliteral +ignoresigns -predboolint +boolint \ @@ -198,7 +205,7 @@ lclint-localaddr: localaddr.c -DTEST $(srcdir)/localaddr.c check-unix:: check-unix-stdconf check-unix-locate check-unix-antoln \ - check-unix-trace t_kuserok + check-unix-trace check-unix-expand t_kuserok check-unix-stdconf:: t_std_conf KRB5_CONFIG=$(srcdir)/td_krb5.conf ; export KRB5_CONFIG ;\ @@ -267,6 +274,14 @@ check-unix-trace:: t_trace sed -e 's/^[^:]*: //' t_trace.out | cmp - $(srcdir)/t_trace.ref rm -f t_trace.out +check-unix-expand:: t_expand_path + $(KRB5_RUN_ENV) $(VALGRIND) ./t_expand_path '%{null}' '' + $(KRB5_RUN_ENV) $(VALGRIND) ./t_expand_path ' %{BINDIR}%{LIBDIR} ' \ + ' $(CLIENT_BINDIR)$(KRB5_LIBDIR) ' + $(KRB5_RUN_ENV) $(VALGRIND) ./t_expand_path \ + 'the %{animal}%{s} on the %{place}%{s}' \ + 'the frogs on the pads' + clean:: $(RM) $(TEST_PROGS) test.out t_std_conf.o t_an_to_ln.o t_locate_kdc.o $(RM) t_kuserok.o diff --git a/src/lib/krb5/os/expand_path.c b/src/lib/krb5/os/expand_path.c new file mode 100644 index 0000000000..3e0e7f10c4 --- /dev/null +++ b/src/lib/krb5/os/expand_path.c @@ -0,0 +1,534 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/os/expand_path.c - Parameterized path expansion facility */ +/* + * Copyright (c) 2009, Secure Endpoints Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "os-proto.h" + +typedef int PTYPE; + +#ifdef _WIN32 +#include +#include + +/* + * Expand a %{TEMP} token + * + * The %{TEMP} token expands to the temporary path for the current + * user as returned by GetTempPath(). + * + * @note: Since the GetTempPath() function relies on the TMP or TEMP + * environment variables, this function will failover to the system + * temporary directory until the user profile is loaded. In addition, + * the returned path may or may not exist. + */ +static krb5_error_code +expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, + char **ret) +{ + TCHAR tpath[MAX_PATH]; + size_t len; + + if (!GetTempPath(sizeof(tpath) / sizeof(tpath[0]), tpath)) { + krb5_set_error_message(context, EINVAL, + "Failed to get temporary path (GLE=%d)", + GetLastError()); + return EINVAL; + } + + len = strlen(tpath); + + if (len > 0 && tpath[len - 1] == '\\') + tpath[len - 1] = '\0'; + + *ret = strdup(tpath); + + if (*ret == NULL) + return ENOMEM; + + return 0; +} + +/* + * Expand a %{BINDIR} token + * + * This is also used to expand a few other tokens on Windows, since + * most of the executable binaries end up in the same directory. The + * "bin" directory is considered to be the directory in which the + * krb5.dll is located. + */ +static krb5_error_code +expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, + char **ret) +{ + TCHAR path[MAX_PATH]; + TCHAR *lastSlash; + DWORD nc; + + nc = GetModuleFileName(get_lib_instance(), path, + sizeof(path) / sizeof(path[0])); + if (nc == 0 || + nc == sizeof(path) / sizeof(path[0])) { + return EINVAL; + } + + lastSlash = strrchr(path, '\\'); + if (lastSlash != NULL) { + TCHAR *fslash = strrchr(lastSlash, '/'); + + if (fslash != NULL) + lastSlash = fslash; + + *lastSlash = '\0'; + } + + if (postfix) { + if (strlcat(path, postfix, sizeof(path) / sizeof(path[0])) >= + sizeof(path) / sizeof(path[0])) + return EINVAL; + } + + *ret = strdup(path); + if (*ret == NULL) + return ENOMEM; + + return 0; +} + +/* + * Expand a %{USERID} token + * + * The %{USERID} token expands to the string representation of the + * user's SID. The user account that will be used is the account + * corresponding to the current thread's security token. This means + * that: + * + * - If the current thread token has the anonymous impersonation + * level, the call will fail. + * + * - If the current thread is impersonating a token at + * SecurityIdentification level the call will fail. + * + */ +static krb5_error_code +expand_userid(krb5_context context, PTYPE param, const char *postfix, + char **ret) +{ + int rv = EINVAL; + HANDLE hThread = NULL; + HANDLE hToken = NULL; + PTOKEN_OWNER pOwner = NULL; + DWORD len = 0; + LPTSTR strSid = NULL; + + hThread = GetCurrentThread(); + + if (!OpenThreadToken(hThread, TOKEN_QUERY, + FALSE, /* Open the thread token as the + current thread user. */ + &hToken)) { + + DWORD le = GetLastError(); + + if (le == ERROR_NO_TOKEN) { + HANDLE hProcess = GetCurrentProcess(); + + le = 0; + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + le = GetLastError(); + } + + if (le != 0) { + krb5_set_error_message(context, rv, + "Can't open thread token (GLE=%d)", le); + goto cleanup; + } + } + + if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + krb5_set_error_message(context, rv, + "Unexpected error reading token " + "information (GLE=%d)", GetLastError()); + goto cleanup; + } + + if (len == 0) { + krb5_set_error_message(context, rv, "GetTokenInformation() " + "returned truncated buffer"); + goto cleanup; + } + + pOwner = malloc(len); + if (pOwner == NULL) { + rv = ENOMEM; + goto cleanup; + } + } else { + krb5_set_error_message(context, rv, "GetTokenInformation() returned " + "truncated buffer"); + goto cleanup; + } + + if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) { + krb5_set_error_message(context, rv, "GetTokenInformation() failed. " + "GLE=%d", GetLastError()); + goto cleanup; + } + + if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) { + krb5_set_error_message(context, rv, "Can't convert SID to string. " + "GLE=%d", GetLastError()); + goto cleanup; + } + + *ret = strdup(strSid); + if (*ret == NULL) { + rv = ENOMEM; + goto cleanup; + } + + rv = 0; + +cleanup: + if (hToken != NULL) + CloseHandle(hToken); + + if (pOwner != NULL) + free (pOwner); + + if (strSid != NULL) + LocalFree(strSid); + + return rv; +} + +/* + * Expand a folder identified by a CSIDL + */ +static krb5_error_code +expand_csidl(krb5_context context, PTYPE folder, const char *postfix, + char **ret) +{ + TCHAR path[MAX_PATH]; + size_t len; + + if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, + path) != S_OK) { + krb5_set_error_message(context, EINVAL, + "Unable to determine folder path"); + return EINVAL; + } + + len = strlen(path); + + if (len > 0 && path[len - 1] == '\\') + path[len - 1] = '\0'; + + if (postfix && + strlcat(path, postfix, sizeof(path) / sizeof(path[0])) >= + sizeof(path)/sizeof(path[0])) + return ENOMEM; + + *ret = strdup(path); + if (*ret == NULL) + return ENOMEM; + return 0; +} + +#else + +static krb5_error_code +expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret) +{ + *ret = strdup(postfix); + if (*ret == NULL) + return ENOMEM; + return 0; +} + +static krb5_error_code +expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, + char **ret) +{ + const char *p = NULL; + + if (context == NULL || !context->profile_secure) + p = getenv("TMPDIR"); + *ret = strdup((p != NULL) ? p : "/tmp"); + if (*ret == NULL) + return ENOMEM; + return 0; +} + +static krb5_error_code +expand_userid(krb5_context context, PTYPE param, const char *postfix, + char **str) +{ + if (asprintf(str, "%ld", (unsigned long)getuid()) < 0) + return ENOMEM; + return 0; +} + +static krb5_error_code +expand_euid(krb5_context context, PTYPE param, const char *postfix, char **str) +{ + if (asprintf(str, "%ld", (unsigned long)geteuid()) < 0) + return ENOMEM; + return 0; +} + +#endif /* not _WIN32 */ + +/* + * Expand an extra token + */ +static krb5_error_code +expand_extra_token(krb5_context context, const char *value, char **ret) +{ + *ret = strdup(value); + if (*ret == NULL) + return ENOMEM; + return 0; +} + +/* + * Expand a %{null} token + * + * The expansion of a %{null} token is always the empty string. + */ +static krb5_error_code +expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret) +{ + *ret = strdup(""); + if (*ret == NULL) + return ENOMEM; + return 0; +} + + +static const struct token { + const char *tok; + int ftype; +#define FTYPE_CSIDL 0 +#define FTYPE_SPECIAL 1 + + PTYPE param; + const char *postfix; + + int (*exp_func)(krb5_context, PTYPE, const char *, char **); + +#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f +#define SPECIAL(f) SPECIALP(f, NULL) + +} tokens[] = { +#ifdef _WIN32 +#define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl +#define CSIDL(C) CSIDLP(C, NULL) + + /* Roaming application data (for current user) */ + {"APPDATA", CSIDL(CSIDL_APPDATA)}, + /* Application data (all users) */ + {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, + /* Local application data (for current user) */ + {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, + /* Windows System folder (e.g. %WINDIR%\System32) */ + {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, + /* Windows folder */ + {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, + /* Per user MIT krb5 configuration file directory */ + {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\MIT\\Kerberos5")}, + /* Common MIT krb5 configuration file directory */ + {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\MIT\\Kerberos5")}, + {"LIBDIR", SPECIAL(expand_bin_dir)}, + {"BINDIR", SPECIAL(expand_bin_dir)}, + {"SBINDIR", SPECIAL(expand_bin_dir)}, + {"euid", SPECIAL(expand_userid)}, +#else + {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path}, + {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path}, + {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path}, + {"euid", SPECIAL(expand_euid)}, +#endif + {"TEMP", SPECIAL(expand_temp_folder)}, + {"USERID", SPECIAL(expand_userid)}, + {"uid", SPECIAL(expand_userid)}, + {"null", SPECIAL(expand_null)} +}; + +static krb5_error_code +expand_token(krb5_context context, const char *token, const char *token_end, + char **extra_tokens, char **ret) +{ + size_t i; + char **p; + + *ret = NULL; + + if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' || + token_end - token <= 2) { + krb5_set_error_message(context, EINVAL, _("Invalid token")); + return EINVAL; + } + + for (p = extra_tokens; p != NULL && *p != NULL; p += 2) { + if (strncmp(token + 2, *p, (token_end - token) - 2) == 0) + return expand_extra_token(context, p[1], ret); + } + + for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) { + if (!strncmp(token + 2, tokens[i].tok, (token_end - token) - 2)) { + return tokens[i].exp_func(context, tokens[i].param, + tokens[i].postfix, ret); + } + } + + krb5_set_error_message(context, EINVAL, _("Invalid token")); + return EINVAL; +} + +/* + * Expand tokens in path_in to produce *path_out. The caller should free + * *path_out with free(). + */ +krb5_error_code +k5_expand_path_tokens(krb5_context context, const char *path_in, + char **path_out) +{ + return k5_expand_path_tokens_extra(context, path_in, path_out, NULL); +} + +static void +free_extra_tokens(char **extra_tokens) +{ + char **p; + + for (p = extra_tokens; p != NULL && *p != NULL; p++) + free(*p); + free(extra_tokens); +} + +/* + * Expand tokens in path_in to produce *path_out. Arguments after path_out are + * pairs of extra token names and replacement values, terminated by a NULL. + * The caller should free *path_out with free(). + */ +krb5_error_code +k5_expand_path_tokens_extra(krb5_context context, const char *path_in, + char **path_out, ...) +{ + krb5_error_code ret; + struct k5buf buf; + char *tok_begin, *tok_end, *tok_val, *path, **extra_tokens = NULL; + const char *path_left; + size_t nargs = 0, i; + va_list ap; + + *path_out = NULL; + + krb5int_buf_init_dynamic(&buf); + + /* Count extra tokens. */ + va_start(ap, path_out); + while (va_arg(ap, const char *) != NULL) + nargs++; + va_end(ap); + if (nargs % 2 != 0) + return EINVAL; + + /* Get extra tokens. */ + if (nargs > 0) { + extra_tokens = k5alloc((nargs + 1) * sizeof(char *), &ret); + if (extra_tokens == NULL) + goto cleanup; + va_start(ap, path_out); + for (i = 0; i < nargs; i++) { + extra_tokens[i] = strdup(va_arg(ap, const char *)); + if (extra_tokens[i] == NULL) { + ret = ENOMEM; + goto cleanup; + } + } + va_end(ap); + } + + path_left = path_in; + while (TRUE) { + /* Find the next token in path_left and add the literal text up to it. + * If there are no more tokens, we can finish up. */ + tok_begin = strstr(path_left, "%{"); + if (tok_begin == NULL) { + krb5int_buf_add(&buf, path_left); + break; + } + krb5int_buf_add_len(&buf, path_left, tok_begin - path_left); + + /* Find the end of this token. */ + tok_end = strchr(tok_begin, '}'); + if (tok_end == NULL) { + ret = EINVAL; + krb5_set_error_message(context, ret, _("variable missing }")); + goto cleanup; + } + + /* Expand this token and add its value. */ + ret = expand_token(context, tok_begin, tok_end, extra_tokens, + &tok_val); + if (ret) + goto cleanup; + krb5int_buf_add(&buf, tok_val); + free(tok_val); + path_left = tok_end + 1; + } + + path = krb5int_buf_data(&buf); + if (path == NULL) { + ret = ENOMEM; + goto cleanup; + } +#ifdef _WIN32 + /* Also deal with slashes. */ + { + char *p; + for (p = path; *p != '\0'; p++) { + if (*p == '/') + *p = '\\'; + } + } +#endif + *path_out = path; + +cleanup: + if (*path_out == NULL) + krb5int_free_buf(&buf); + free_extra_tokens(extra_tokens); + return 0; +} diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h index 665187c548..ec97545523 100644 --- a/src/lib/krb5/os/os-proto.h +++ b/src/lib/krb5/os/os-proto.h @@ -107,6 +107,12 @@ int krb5int_net_writev (krb5_context, int, sg_buf *, int); int k5_getcurtime(struct timeval *tvp); +krb5_error_code k5_expand_path_tokens(krb5_context context, + const char *path_in, char **path_out); +krb5_error_code k5_expand_path_tokens_extra(krb5_context context, + const char *path_in, + char **path_out, ...); + #include "k5-thread.h" extern k5_mutex_t krb5int_us_time_mutex; diff --git a/src/lib/krb5/os/t_expand_path.c b/src/lib/krb5/os/t_expand_path.c new file mode 100644 index 0000000000..b318ff980a --- /dev/null +++ b/src/lib/krb5/os/t_expand_path.c @@ -0,0 +1,16 @@ +#include "k5-int.h" +#include "os-proto.h" + +int +main(int argc, char **argv) +{ + char *path; + + if (k5_expand_path_tokens_extra(NULL, argv[1], &path, "animal", "frog", + "place", "pad", "s", "s", NULL) != 0) + return 2; + if (strcmp(path, argv[2]) != 0) + return 1; + free(path); + return 0; +}