krb5 commit: Add internal path expansion functions
Greg Hudson
ghudson at MIT.EDU
Tue Jul 24 16:36:28 EDT 2012
https://github.com/krb5/krb5/commit/7d07dc63a22bfdebc24f0368f969acc4b76d372c
commit 7d07dc63a22bfdebc24f0368f969acc4b76d372c
Author: Greg Hudson <ghudson at mit.edu>
Date: Tue Jul 24 16:26:27 2012 -0400
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.
src/lib/krb5/libkrb5.exports | 2 +
src/lib/krb5/os/Makefile.in | 23 ++-
src/lib/krb5/os/expand_path.c | 534 +++++++++++++++++++++++++++++++++++++++
src/lib/krb5/os/os-proto.h | 6 +
src/lib/krb5/os/t_expand_path.c | 16 ++
5 files changed, 577 insertions(+), 4 deletions(-)
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index e5acff2..28049e7 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 7d07480..2bbfb3f 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 0000000..3e0e7f1
--- /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 <shlobj.h>
+#include <sddl.h>
+
+/*
+ * 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 665187c..ec97545 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 0000000..b318ff9
--- /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;
+}
More information about the cvs-krb5
mailing list