aboutsummaryrefslogtreecommitdiff
path: root/src/clients
diff options
context:
space:
mode:
Diffstat (limited to 'src/clients')
-rw-r--r--src/clients/Makefile.in7
-rw-r--r--src/clients/deps1
-rw-r--r--src/clients/kcpytkt/Makefile.in32
-rw-r--r--src/clients/kcpytkt/kcpytkt.c178
-rw-r--r--src/clients/kdeltkt/Makefile.in32
-rw-r--r--src/clients/kdeltkt/kdeltkt.c170
-rw-r--r--src/clients/kdestroy/Makefile.in35
-rw-r--r--src/clients/kdestroy/deps7
-rw-r--r--src/clients/kdestroy/kdestroy.c206
-rw-r--r--src/clients/kinit/Makefile.in34
-rw-r--r--src/clients/kinit/deps32
-rw-r--r--src/clients/kinit/extern.h33
-rw-r--r--src/clients/kinit/kinit.c958
-rw-r--r--src/clients/kinit/kinit_kdb.c77
-rw-r--r--src/clients/klist/Makefile.in35
-rw-r--r--src/clients/klist/deps14
-rw-r--r--src/clients/klist/klist.c874
-rw-r--r--src/clients/kpasswd/Makefile.in37
-rw-r--r--src/clients/kpasswd/deps11
-rw-r--r--src/clients/kpasswd/kpasswd.c176
-rw-r--r--src/clients/kpasswd/ksetpwd.c309
-rw-r--r--src/clients/ksu/Makefile.in35
-rw-r--r--src/clients/ksu/authorization.c727
-rw-r--r--src/clients/ksu/ccache.c742
-rw-r--r--src/clients/ksu/deps72
-rw-r--r--src/clients/ksu/heuristic.c750
-rw-r--r--src/clients/ksu/krb_auth_su.c313
-rw-r--r--src/clients/ksu/ksu.h247
-rw-r--r--src/clients/ksu/main.c1038
-rw-r--r--src/clients/ksu/setenv.c174
-rw-r--r--src/clients/ksu/xmalloc.c80
-rw-r--r--src/clients/kswitch/Makefile.in33
-rw-r--r--src/clients/kswitch/deps13
-rw-r--r--src/clients/kswitch/kswitch.c132
-rw-r--r--src/clients/kvno/Makefile.in36
-rw-r--r--src/clients/kvno/deps13
-rw-r--r--src/clients/kvno/kvno.c354
37 files changed, 8017 insertions, 0 deletions
diff --git a/src/clients/Makefile.in b/src/clients/Makefile.in
new file mode 100644
index 000000000000..4beb32a61894
--- /dev/null
+++ b/src/clients/Makefile.in
@@ -0,0 +1,7 @@
+mydir=clients
+BUILDTOP=$(REL)..
+
+SUBDIRS= klist kinit kdestroy kpasswd ksu kvno kcpytkt kdeltkt kswitch
+WINSUBDIRS= klist kinit kdestroy kpasswd kvno kcpytkt kdeltkt kswitch
+
+NO_OUTPRE=1
diff --git a/src/clients/deps b/src/clients/deps
new file mode 100644
index 000000000000..2feac3c9d388
--- /dev/null
+++ b/src/clients/deps
@@ -0,0 +1 @@
+# No dependencies here.
diff --git a/src/clients/kcpytkt/Makefile.in b/src/clients/kcpytkt/Makefile.in
new file mode 100644
index 000000000000..cad4dd413996
--- /dev/null
+++ b/src/clients/kcpytkt/Makefile.in
@@ -0,0 +1,32 @@
+mydir=kcpytkt
+BUILDTOP=$(REL)..$(S)..
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KCPYTKT=$(OUTPRE)kcpytkt.exe
+
+##WIN32##EXERES=$(KCPYTKT:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKCPYTKT_APP -fo $@ -r $**
+
+all-unix: kcpytkt
+##WIN32##all-windows: $(KCPYTKT)
+all-mac:
+
+kcpytkt: kcpytkt.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ kcpytkt.o $(KRB5_BASE_LIBS)
+
+##WIN32##$(KCPYTKT): $(OUTPRE)kcpytkt.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) /out:$@ $**
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) kcpytkt.o kcpytkt
+
+install-unix:
+ for f in kcpytkt; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/kcpytkt/kcpytkt.c b/src/clients/kcpytkt/kcpytkt.c
new file mode 100644
index 000000000000..47147cdc3207
--- /dev/null
+++ b/src/clients/kcpytkt/kcpytkt.c
@@ -0,0 +1,178 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <krb5.h>
+#include "k5-platform.h"
+
+static char *prog;
+
+static void xusage()
+{
+ fprintf(stderr, "xusage: %s [-c from_ccache] [-e etype] [-f flags] dest_ccache service1 service2 ...\n", prog);
+ exit(1);
+}
+
+int quiet = 0;
+
+static void do_kcpytkt (int argc, char *argv[], char *fromccachestr, char *etypestr, int flags);
+
+int main(int argc, char *argv[])
+{
+ int option;
+ char *etypestr = 0;
+ char *fromccachestr = 0;
+ int flags = 0;
+
+ prog = strrchr(argv[0], '/');
+ prog = prog ? (prog + 1) : argv[0];
+
+ while ((option = getopt(argc, argv, "c:e:f:hq")) != -1) {
+ switch (option) {
+ case 'c':
+ fromccachestr = optarg;
+ break;
+ case 'e':
+ etypestr = optarg;
+ break;
+ case 'f':
+ flags = atoi(optarg);
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ default:
+ xusage();
+ break;
+ }
+ }
+
+ if ((argc - optind) < 2)
+ xusage();
+
+ do_kcpytkt(argc - optind, argv + optind, fromccachestr, etypestr, flags);
+ return 0;
+}
+
+static void do_kcpytkt (int count, char *names[],
+ char *fromccachestr, char *etypestr, int flags)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, errors;
+ krb5_enctype etype;
+ krb5_ccache fromccache;
+ krb5_ccache destccache;
+ krb5_principal me;
+ krb5_creds in_creds, out_creds;
+ int retflags;
+ char *princ;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ com_err(prog, ret, "while initializing krb5 library");
+ exit(1);
+ }
+
+ if (etypestr) {
+ ret = krb5_string_to_enctype(etypestr, &etype);
+ if (ret) {
+ com_err(prog, ret, "while converting etype");
+ exit(1);
+ }
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES;
+ } else {
+ etype = 0;
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
+ }
+
+ if (fromccachestr)
+ ret = krb5_cc_resolve(context, fromccachestr, &fromccache);
+ else
+ ret = krb5_cc_default(context, &fromccache);
+ if (ret) {
+ com_err(prog, ret, "while opening source ccache");
+ exit(1);
+ }
+
+ ret = krb5_cc_get_principal(context, fromccache, &me);
+ if (ret) {
+ com_err(prog, ret, "while getting client principal name");
+ exit(1);
+ }
+
+ ret = krb5_cc_resolve(context, names[0], &destccache);
+ if (ret) {
+ com_err(prog, ret, "while opening destination cache");
+ exit(1);
+ }
+
+ errors = 0;
+
+ for (i = 1; i < count; i++) {
+ memset(&in_creds, 0, sizeof(in_creds));
+
+ in_creds.client = me;
+
+ ret = krb5_parse_name(context, names[i], &in_creds.server);
+ if (ret) {
+ if (!quiet)
+ fprintf(stderr, "%s: %s while parsing principal name\n",
+ names[i], error_message(ret));
+ errors++;
+ continue;
+ }
+
+ ret = krb5_unparse_name(context, in_creds.server, &princ);
+ if (ret) {
+ fprintf(stderr, "%s: %s while printing principal name\n",
+ names[i], error_message(ret));
+ errors++;
+ continue;
+ }
+
+ in_creds.keyblock.enctype = etype;
+
+ ret = krb5_cc_retrieve_cred(context, fromccache, retflags,
+ &in_creds, &out_creds);
+ if (ret) {
+ fprintf(stderr, "%s: %s while retrieving credentials\n",
+ princ, error_message(ret));
+
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ ret = krb5_cc_store_cred(context, destccache, &out_creds);
+
+ krb5_free_principal(context, in_creds.server);
+
+ if (ret) {
+ fprintf(stderr, "%s: %s while removing credentials\n",
+ princ, error_message(ret));
+
+ krb5_free_cred_contents(context, &out_creds);
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ krb5_free_unparsed_name(context, princ);
+ krb5_free_cred_contents(context, &out_creds);
+ }
+
+ krb5_free_principal(context, me);
+ krb5_cc_close(context, fromccache);
+ krb5_cc_close(context, destccache);
+ krb5_free_context(context);
+
+ if (errors)
+ exit(1);
+
+ exit(0);
+}
diff --git a/src/clients/kdeltkt/Makefile.in b/src/clients/kdeltkt/Makefile.in
new file mode 100644
index 000000000000..b7e0e73e6124
--- /dev/null
+++ b/src/clients/kdeltkt/Makefile.in
@@ -0,0 +1,32 @@
+mydir=kvno
+BUILDTOP=$(REL)..$(S)..
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KDELTKT=$(OUTPRE)kdeltkt.exe
+
+##WIN32##EXERES=$(KDELTKT:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKDELTKT_APP -fo $@ -r $**
+
+all-unix: kdeltkt
+##WIN32##all-windows: $(KDELTKT)
+all-mac:
+
+kdeltkt: kdeltkt.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ kdeltkt.o $(KRB5_BASE_LIBS)
+
+##WIN32##$(KDELTKT): $(OUTPRE)kdeltkt.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) /out:$@ $**
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) kdeltkt.o kdeltkt
+
+install-unix:
+ for f in kdeltkt; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/kdeltkt/kdeltkt.c b/src/clients/kdeltkt/kdeltkt.c
new file mode 100644
index 000000000000..9c7a549f970b
--- /dev/null
+++ b/src/clients/kdeltkt/kdeltkt.c
@@ -0,0 +1,170 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <krb5.h>
+#include "k5-platform.h"
+
+static char *prog;
+
+static void xusage()
+{
+ fprintf(stderr, "xusage: %s [-c ccache] [-e etype] [-f flags] service1 service2 ...\n", prog);
+ exit(1);
+}
+
+int quiet = 0;
+
+static void do_kdeltkt (int argc, char *argv[], char *ccachestr, char *etypestr, int flags);
+
+int main(int argc, char *argv[])
+{
+ int option;
+ char *etypestr = 0;
+ char *ccachestr = 0;
+ int flags = 0;
+
+ prog = strrchr(argv[0], '/');
+ prog = prog ? (prog + 1) : argv[0];
+
+ while ((option = getopt(argc, argv, "c:e:f:hq")) != -1) {
+ switch (option) {
+ case 'c':
+ ccachestr = optarg;
+ break;
+ case 'e':
+ etypestr = optarg;
+ break;
+ case 'f':
+ flags = atoi(optarg);
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ default:
+ xusage();
+ break;
+ }
+ }
+
+ if ((argc - optind) < 1)
+ xusage();
+
+ do_kdeltkt(argc - optind, argv + optind, ccachestr, etypestr, flags);
+ return 0;
+}
+
+static void do_kdeltkt (int count, char *names[],
+ char *ccachestr, char *etypestr, int flags)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, errors;
+ krb5_enctype etype;
+ krb5_ccache ccache;
+ krb5_principal me;
+ krb5_creds in_creds, out_creds;
+ int retflags;
+ char *princ;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ com_err(prog, ret, "while initializing krb5 library");
+ exit(1);
+ }
+
+ if (etypestr) {
+ ret = krb5_string_to_enctype(etypestr, &etype);
+ if (ret) {
+ com_err(prog, ret, "while converting etype");
+ exit(1);
+ }
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES;
+ } else {
+ etype = 0;
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
+ }
+
+ if (ccachestr)
+ ret = krb5_cc_resolve(context, ccachestr, &ccache);
+ else
+ ret = krb5_cc_default(context, &ccache);
+ if (ret) {
+ com_err(prog, ret, "while opening ccache");
+ exit(1);
+ }
+
+ ret = krb5_cc_get_principal(context, ccache, &me);
+ if (ret) {
+ com_err(prog, ret, "while getting client principal name");
+ exit(1);
+ }
+
+ errors = 0;
+
+ for (i = 0; i < count; i++) {
+ memset(&in_creds, 0, sizeof(in_creds));
+
+ in_creds.client = me;
+
+ ret = krb5_parse_name(context, names[i], &in_creds.server);
+ if (ret) {
+ if (!quiet)
+ fprintf(stderr, "%s: %s while parsing principal name\n",
+ names[i], error_message(ret));
+ errors++;
+ continue;
+ }
+
+ ret = krb5_unparse_name(context, in_creds.server, &princ);
+ if (ret) {
+ fprintf(stderr, "%s: %s while printing principal name\n",
+ names[i], error_message(ret));
+ errors++;
+ continue;
+ }
+
+ in_creds.keyblock.enctype = etype;
+
+ ret = krb5_cc_retrieve_cred(context, ccache, retflags,
+ &in_creds, &out_creds);
+ if (ret) {
+ fprintf(stderr, "%s: %s while retrieving credentials\n",
+ princ, error_message(ret));
+
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ ret = krb5_cc_remove_cred(context, ccache, flags, &out_creds);
+
+ krb5_free_principal(context, in_creds.server);
+
+ if (ret) {
+ fprintf(stderr, "%s: %s while removing credentials\n",
+ princ, error_message(ret));
+
+ krb5_free_cred_contents(context, &out_creds);
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ krb5_free_unparsed_name(context, princ);
+ krb5_free_cred_contents(context, &out_creds);
+ }
+
+ krb5_free_principal(context, me);
+ krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+
+ if (errors)
+ exit(1);
+
+ exit(0);
+}
diff --git a/src/clients/kdestroy/Makefile.in b/src/clients/kdestroy/Makefile.in
new file mode 100644
index 000000000000..beb7d6fa8b2c
--- /dev/null
+++ b/src/clients/kdestroy/Makefile.in
@@ -0,0 +1,35 @@
+mydir=clients$(S)kdestroy
+BUILDTOP=$(REL)..$(S)..
+
+##WIN32##LOCALINCLUDES=-I$(BUILDTOP)\util\windows\
+
+SRCS=kdestroy.c
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KDESTROY=$(OUTPRE)kdestroy.exe
+
+##WIN32##EXERES=$(KDESTROY:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKDESTROY_APP -fo $@ -r $**
+
+all-unix: kdestroy
+##WIN32##all-windows: $(KDESTROY)
+
+kdestroy: kdestroy.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ kdestroy.o $(KRB5_BASE_LIBS)
+
+##WIN32##$(KDESTROY): $(OUTPRE)kdestroy.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) -out:$@ $**
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) kdestroy.o kdestroy
+
+install-unix:
+ for f in kdestroy; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/kdestroy/deps b/src/clients/kdestroy/deps
new file mode 100644
index 000000000000..bfc94c4c98aa
--- /dev/null
+++ b/src/clients/kdestroy/deps
@@ -0,0 +1,7 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kdestroy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/krb5.h \
+ kdestroy.c
diff --git a/src/clients/kdestroy/kdestroy.c b/src/clients/kdestroy/kdestroy.c
new file mode 100644
index 000000000000..f95554903ece
--- /dev/null
+++ b/src/clients/kdestroy/kdestroy.c
@@ -0,0 +1,206 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/kdestroy/kdestroy.c - Destroy contents of credential cache */
+/*
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-platform.h"
+#include <krb5.h>
+#include <com_err.h>
+#include <locale.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef __STDC__
+#define BELL_CHAR '\a'
+#else
+#define BELL_CHAR '\007'
+#endif
+
+extern int optind;
+extern char *optarg;
+
+#ifndef _WIN32
+#define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
+#else
+#define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
+#endif
+
+char *progname;
+
+
+static void usage()
+{
+#define KRB_AVAIL_STRING(x) ((x)?"available":"not available")
+
+ fprintf(stderr, _("Usage: %s [-A] [-q] [-c cache_name]\n"), progname);
+ fprintf(stderr, _("\t-A destroy all credential caches in collection\n"));
+ fprintf(stderr, _("\t-q quiet mode\n"));
+ fprintf(stderr, _("\t-c specify name of credentials cache\n"));
+ exit(2);
+}
+
+/* Print a warning if there are still un-destroyed caches in the collection. */
+static void
+print_remaining_cc_warning(krb5_context context)
+{
+ krb5_error_code retval;
+ krb5_ccache cache;
+ krb5_cccol_cursor cursor;
+
+ retval = krb5_cccol_cursor_new(context, &cursor);
+ if (retval) {
+ com_err(progname, retval, _("while listing credential caches"));
+ exit(1);
+ }
+
+ retval = krb5_cccol_cursor_next(context, cursor, &cache);
+ if (retval == 0 && cache != NULL) {
+ fprintf(stderr,
+ _("Other credential caches present, use -A to destroy all\n"));
+ krb5_cc_close(context, cache);
+ }
+
+ krb5_cccol_cursor_free(context, &cursor);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ krb5_context kcontext;
+ krb5_error_code retval;
+ int c;
+ krb5_ccache cache = NULL;
+ krb5_cccol_cursor cursor;
+ char *cache_name = NULL;
+ int code = 0;
+ int errflg = 0;
+ int quiet = 0;
+ int all = 0;
+
+ setlocale(LC_ALL, "");
+ progname = GET_PROGNAME(argv[0]);
+
+ while ((c = getopt(argc, argv, "54Aqc:")) != -1) {
+ switch (c) {
+ case 'A':
+ all = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'c':
+ if (cache_name) {
+ fprintf(stderr, _("Only one -c option allowed\n"));
+ errflg++;
+ } else {
+ cache_name = optarg;
+ }
+ break;
+ case '4':
+ fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
+ exit(3);
+ break;
+ case '5':
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if (optind != argc)
+ errflg++;
+
+ if (errflg) {
+ usage();
+ }
+
+ retval = krb5_init_context(&kcontext);
+ if (retval) {
+ com_err(progname, retval, _("while initializing krb5"));
+ exit(1);
+ }
+
+ if (all) {
+ code = krb5_cccol_cursor_new(kcontext, &cursor);
+ if (code) {
+ com_err(progname, code, _("while listing credential caches"));
+ exit(1);
+ }
+ while ((code = krb5_cccol_cursor_next(kcontext, cursor,
+ &cache)) == 0 && cache != NULL) {
+ code = krb5_cc_get_full_name(kcontext, cache, &cache_name);
+ if (code) {
+ com_err(progname, code, _("composing ccache name"));
+ exit(1);
+ }
+ code = krb5_cc_destroy(kcontext, cache);
+ if (code && code != KRB5_FCC_NOFILE) {
+ com_err(progname, code, _("while destroying cache %s"),
+ cache_name);
+ }
+ krb5_free_string(kcontext, cache_name);
+ }
+ krb5_cccol_cursor_free(kcontext, &cursor);
+ krb5_free_context(kcontext);
+ return 0;
+ }
+
+ if (cache_name) {
+ code = krb5_cc_resolve (kcontext, cache_name, &cache);
+ if (code != 0) {
+ com_err(progname, code, _("while resolving %s"), cache_name);
+ exit(1);
+ }
+ } else {
+ code = krb5_cc_default(kcontext, &cache);
+ if (code) {
+ com_err(progname, code, _("while getting default ccache"));
+ exit(1);
+ }
+ }
+
+ code = krb5_cc_destroy (kcontext, cache);
+ if (code != 0) {
+ com_err (progname, code, _("while destroying cache"));
+ if (code != KRB5_FCC_NOFILE) {
+ if (quiet)
+ fprintf(stderr, _("Ticket cache NOT destroyed!\n"));
+ else {
+ fprintf(stderr, _("Ticket cache %cNOT%c destroyed!\n"),
+ BELL_CHAR, BELL_CHAR);
+ }
+ errflg = 1;
+ }
+ }
+
+ if (!quiet && !errflg)
+ print_remaining_cc_warning(kcontext);
+
+ krb5_free_context(kcontext);
+ return errflg;
+}
diff --git a/src/clients/kinit/Makefile.in b/src/clients/kinit/Makefile.in
new file mode 100644
index 000000000000..9d02174dc7da
--- /dev/null
+++ b/src/clients/kinit/Makefile.in
@@ -0,0 +1,34 @@
+mydir=clients$(S)kinit
+BUILDTOP=$(REL)..$(S)..
+
+SRCS=kinit.c kinit_kdb.c
+##WIN32##LOCALINCLUDES=-I$(BUILDTOP)\util\windows -I$(BUILDTOP)\util\support
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KINIT=$(OUTPRE)kinit.exe
+
+##WIN32##EXERES=$(KINIT:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKINIT_APP -fo $@ -r $**
+
+all-unix: kinit
+##WIN32##all-windows: $(KINIT)
+
+kinit: kinit.o kinit_kdb.o $(KRB5_BASE_DEPLIBS) $(KADMSRV_DEPLIBS)
+ $(CC_LINK) -o $@ kinit.o kinit_kdb.o $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
+
+##WIN32##$(KINIT): $(OUTPRE)kinit.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) -out:$@ $** advapi32.lib
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) kinit.o kinit_kdb.o kinit
+
+install-unix:
+ for f in kinit; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/kinit/deps b/src/clients/kinit/deps
new file mode 100644
index 000000000000..56e9d6691984
--- /dev/null
+++ b/src/clients/kinit/deps
@@ -0,0 +1,32 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kinit.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h extern.h kinit.c
+$(OUTPRE)kinit_kdb.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ extern.h kinit_kdb.c
diff --git a/src/clients/kinit/extern.h b/src/clients/kinit/extern.h
new file mode 100644
index 000000000000..2c28623180fd
--- /dev/null
+++ b/src/clients/kinit/extern.h
@@ -0,0 +1,33 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/kinit/extern.h - Global declarations for kinit */
+/*
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifndef KINIT_EXTERN_H
+#define KINIT_EXTERN_H
+
+krb5_error_code kinit_kdb_init(krb5_context *pcontext, char *realm);
+void kinit_kdb_fini(void);
+
+#endif /* KINIT_EXTERN_H */
diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c
new file mode 100644
index 000000000000..f1cd1b73db60
--- /dev/null
+++ b/src/clients/kinit/kinit.c
@@ -0,0 +1,958 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/kinit/kinit.c - Initialize a credential cache */
+/*
+ * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "autoconf.h"
+#include <k5-int.h>
+#include "k5-platform.h" /* for asprintf and getopt */
+#include <krb5.h>
+#include "extern.h"
+#include <locale.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <com_err.h>
+
+#ifndef _WIN32
+#define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
+#else
+#define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+static
+char * get_name_from_os()
+{
+ struct passwd *pw;
+ if ((pw = getpwuid((int) getuid())))
+ return pw->pw_name;
+ return 0;
+}
+#else /* HAVE_PWD_H */
+#ifdef _WIN32
+static
+char * get_name_from_os()
+{
+ static char name[1024];
+ DWORD name_size = sizeof(name);
+ if (GetUserName(name, &name_size)) {
+ name[sizeof(name)-1] = 0; /* Just to be extra safe */
+ return name;
+ } else {
+ return 0;
+ }
+}
+#else /* _WIN32 */
+static
+char * get_name_from_os()
+{
+ return 0;
+}
+#endif /* _WIN32 */
+#endif /* HAVE_PWD_H */
+
+static char *progname;
+
+typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
+
+struct k_opts
+{
+ /* in seconds */
+ krb5_deltat starttime;
+ krb5_deltat lifetime;
+ krb5_deltat rlife;
+
+ int forwardable;
+ int proxiable;
+ int request_pac;
+ int anonymous;
+ int addresses;
+
+ int not_forwardable;
+ int not_proxiable;
+ int not_request_pac;
+ int no_addresses;
+
+ int verbose;
+
+ char* principal_name;
+ char* service_name;
+ char* keytab_name;
+ char* k5_in_cache_name;
+ char* k5_out_cache_name;
+ char *armor_ccache;
+
+ action_type action;
+ int use_client_keytab;
+
+ int num_pa_opts;
+ krb5_gic_opt_pa_data *pa_opts;
+
+ int canonicalize;
+ int enterprise;
+};
+
+struct k5_data
+{
+ krb5_context ctx;
+ krb5_ccache in_cc, out_cc;
+ krb5_principal me;
+ char* name;
+ krb5_boolean switch_to_cache;
+};
+
+/* if struct[2] == NULL, then long_getopt acts as if the short flag
+ struct[3] was specified. If struct[2] != NULL, then struct[3] is
+ stored in *(struct[2]), the array index which was specified is
+ stored in *index, and long_getopt() returns 0. */
+
+const char *shopts = "r:fpFPn54aAVl:s:c:kit:T:RS:vX:CEI:";
+
+static void
+usage()
+{
+#define USAGE_BREAK "\n\t"
+
+#define USAGE_LONG_FORWARDABLE " | --forwardable | --noforwardable"
+#define USAGE_LONG_PROXIABLE " | --proxiable | --noproxiable"
+#define USAGE_LONG_ADDRESSES " | --addresses | --noaddresses"
+#define USAGE_LONG_CANONICALIZE " | --canonicalize"
+#define USAGE_LONG_ENTERPRISE " | --enterprise"
+#define USAGE_LONG_REQUESTPAC "--request-pac | --no-request-pac"
+#define USAGE_BREAK_LONG USAGE_BREAK
+
+ fprintf(stderr, "Usage: %s [-V] "
+ "[-l lifetime] [-s start_time] "
+ USAGE_BREAK
+ "[-r renewable_life] "
+ "[-f | -F" USAGE_LONG_FORWARDABLE "] "
+ USAGE_BREAK_LONG
+ "[-p | -P" USAGE_LONG_PROXIABLE "] "
+ USAGE_BREAK_LONG
+ "-n "
+ "[-a | -A" USAGE_LONG_ADDRESSES "] "
+ USAGE_BREAK_LONG
+ "[" USAGE_LONG_REQUESTPAC "] "
+ USAGE_BREAK_LONG
+ "[-C" USAGE_LONG_CANONICALIZE "] "
+ USAGE_BREAK
+ "[-E" USAGE_LONG_ENTERPRISE "] "
+ USAGE_BREAK
+ "[-v] [-R] "
+ "[-k [-i|-t keytab_file]] "
+ "[-c cachename] "
+ USAGE_BREAK
+ "[-S service_name] [-T ticket_armor_cache]"
+ USAGE_BREAK
+ "[-X <attribute>[=<value>]] [principal]"
+ "\n\n",
+ progname);
+
+ fprintf(stderr, " options:\n");
+ fprintf(stderr, _("\t-V verbose\n"));
+ fprintf(stderr, _("\t-l lifetime\n"));
+ fprintf(stderr, _("\t-s start time\n"));
+ fprintf(stderr, _("\t-r renewable lifetime\n"));
+ fprintf(stderr, _("\t-f forwardable\n"));
+ fprintf(stderr, _("\t-F not forwardable\n"));
+ fprintf(stderr, _("\t-p proxiable\n"));
+ fprintf(stderr, _("\t-P not proxiable\n"));
+ fprintf(stderr, _("\t-n anonymous\n"));
+ fprintf(stderr, _("\t-a include addresses\n"));
+ fprintf(stderr, _("\t-A do not include addresses\n"));
+ fprintf(stderr, _("\t-v validate\n"));
+ fprintf(stderr, _("\t-R renew\n"));
+ fprintf(stderr, _("\t-C canonicalize\n"));
+ fprintf(stderr, _("\t-E client is enterprise principal name\n"));
+ fprintf(stderr, _("\t-k use keytab\n"));
+ fprintf(stderr, _("\t-i use default client keytab (with -k)\n"));
+ fprintf(stderr, _("\t-t filename of keytab to use\n"));
+ fprintf(stderr, _("\t-c Kerberos 5 cache name\n"));
+ fprintf(stderr, _("\t-S service\n"));
+ fprintf(stderr, _("\t-T armor credential cache\n"));
+ fprintf(stderr, _("\t-X <attribute>[=<value>]\n"));
+ exit(2);
+}
+
+static krb5_context errctx;
+static void extended_com_err_fn (const char *myprog, errcode_t code,
+ const char *fmt, va_list args)
+{
+ const char *emsg;
+ emsg = krb5_get_error_message (errctx, code);
+ fprintf (stderr, "%s: %s ", myprog, emsg);
+ krb5_free_error_message (errctx, emsg);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, "\n");
+}
+
+static int
+add_preauth_opt(struct k_opts *opts, char *av)
+{
+ char *sep, *v;
+ krb5_gic_opt_pa_data *p, *x;
+
+ if (opts->num_pa_opts == 0) {
+ opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data));
+ if (opts->pa_opts == NULL)
+ return ENOMEM;
+ } else {
+ size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data);
+ x = realloc(opts->pa_opts, newsize);
+ if (x == NULL)
+ return ENOMEM;
+ opts->pa_opts = x;
+ }
+ p = &opts->pa_opts[opts->num_pa_opts];
+ sep = strchr(av, '=');
+ if (sep) {
+ *sep = '\0';
+ v = ++sep;
+ p->value = v;
+ } else {
+ p->value = "yes";
+ }
+ p->attr = av;
+ opts->num_pa_opts++;
+ return 0;
+}
+
+static char *
+parse_options(argc, argv, opts)
+ int argc;
+ char **argv;
+ struct k_opts* opts;
+{
+ struct option long_options[] = {
+ { "noforwardable", 0, NULL, 'F' },
+ { "noproxiable", 0, NULL, 'P' },
+ { "addresses", 0, NULL, 'a'},
+ { "forwardable", 0, NULL, 'f' },
+ { "proxiable", 0, NULL, 'p' },
+ { "noaddresses", 0, NULL, 'A' },
+ { "canonicalize", 0, NULL, 'C' },
+ { "enterprise", 0, NULL, 'E' },
+ { "request-pac", 0, &opts->request_pac, 1 },
+ { "no-request-pac", 0, &opts->not_request_pac, 1 },
+ { NULL, 0, NULL, 0 }
+ };
+ krb5_error_code code;
+ int errflg = 0;
+ int i;
+
+ while ((i = getopt_long(argc, argv, shopts, long_options, 0)) != -1) {
+ switch (i) {
+ case 'V':
+ opts->verbose = 1;
+ break;
+ case 'l':
+ /* Lifetime */
+ code = krb5_string_to_deltat(optarg, &opts->lifetime);
+ if (code != 0 || opts->lifetime == 0) {
+ fprintf(stderr, _("Bad lifetime value %s\n"), optarg);
+ errflg++;
+ }
+ break;
+ case 'r':
+ /* Renewable Time */
+ code = krb5_string_to_deltat(optarg, &opts->rlife);
+ if (code != 0 || opts->rlife == 0) {
+ fprintf(stderr, _("Bad lifetime value %s\n"), optarg);
+ errflg++;
+ }
+ break;
+ case 'f':
+ opts->forwardable = 1;
+ break;
+ case 'F':
+ opts->not_forwardable = 1;
+ break;
+ case 'p':
+ opts->proxiable = 1;
+ break;
+ case 'P':
+ opts->not_proxiable = 1;
+ break;
+ case 'n':
+ opts->anonymous = 1;
+ break;
+ case 'a':
+ opts->addresses = 1;
+ break;
+ case 'A':
+ opts->no_addresses = 1;
+ break;
+ case 's':
+ code = krb5_string_to_deltat(optarg, &opts->starttime);
+ if (code != 0 || opts->starttime == 0) {
+ /* Parse as an absolute time; intentionally undocumented
+ * but left for backwards compatibility. */
+ krb5_timestamp abs_starttime;
+
+ code = krb5_string_to_timestamp(optarg, &abs_starttime);
+ if (code != 0 || abs_starttime == 0) {
+ fprintf(stderr, _("Bad start time value %s\n"), optarg);
+ errflg++;
+ } else {
+ opts->starttime = abs_starttime - time(0);
+ }
+ }
+ break;
+ case 'S':
+ opts->service_name = optarg;
+ break;
+ case 'k':
+ opts->action = INIT_KT;
+ break;
+ case 'i':
+ opts->use_client_keytab = 1;
+ break;
+ case 't':
+ if (opts->keytab_name)
+ {
+ fprintf(stderr, _("Only one -t option allowed.\n"));
+ errflg++;
+ } else {
+ opts->keytab_name = optarg;
+ }
+ break;
+ case 'T':
+ if (opts->armor_ccache) {
+ fprintf(stderr, _("Only one armor_ccache\n"));
+ errflg++;
+ } else opts->armor_ccache = optarg;
+ break;
+ case 'R':
+ opts->action = RENEW;
+ break;
+ case 'v':
+ opts->action = VALIDATE;
+ break;
+ case 'c':
+ if (opts->k5_out_cache_name)
+ {
+ fprintf(stderr, _("Only one -c option allowed\n"));
+ errflg++;
+ } else {
+ opts->k5_out_cache_name = optarg;
+ }
+ break;
+ case 'I':
+ if (opts->k5_in_cache_name) {
+ fprintf(stderr, _("Only one -I option allowed\n"));
+ errflg++;
+ } else {
+ opts->k5_in_cache_name = optarg;
+ }
+ break;
+ case 'X':
+ code = add_preauth_opt(opts, optarg);
+ if (code)
+ {
+ com_err(progname, code, _("while adding preauth option"));
+ errflg++;
+ }
+ break;
+ case 'C':
+ opts->canonicalize = 1;
+ break;
+ case 'E':
+ opts->enterprise = 1;
+ break;
+ case '4':
+ fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
+ exit(3);
+ break;
+ case '5':
+ break;
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if (opts->forwardable && opts->not_forwardable)
+ {
+ fprintf(stderr, _("Only one of -f and -F allowed\n"));
+ errflg++;
+ }
+ if (opts->proxiable && opts->not_proxiable)
+ {
+ fprintf(stderr, _("Only one of -p and -P allowed\n"));
+ errflg++;
+ }
+ if (opts->request_pac && opts->not_request_pac)
+ {
+ fprintf(stderr, _("Only one of --request-pac and --no-request-pac "
+ "allowed\n"));
+ errflg++;
+ }
+ if (opts->addresses && opts->no_addresses)
+ {
+ fprintf(stderr, _("Only one of -a and -A allowed\n"));
+ errflg++;
+ }
+ if (opts->keytab_name != NULL && opts->use_client_keytab == 1)
+ {
+ fprintf(stderr, _("Only one of -t and -i allowed\n"));
+ errflg++;
+ }
+ if ((opts->keytab_name != NULL || opts->use_client_keytab == 1) &&
+ opts->action != INIT_KT)
+ {
+ opts->action = INIT_KT;
+ fprintf(stderr, _("keytab specified, forcing -k\n"));
+ }
+
+ if (argc - optind > 1) {
+ fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"),
+ argv[optind+1]);
+ errflg++;
+ }
+
+ if (errflg) {
+ usage();
+ }
+
+ opts->principal_name = (optind == argc-1) ? argv[optind] : 0;
+ return opts->principal_name;
+}
+
+static int
+k5_begin(opts, k5)
+ struct k_opts* opts;
+ struct k5_data* k5;
+{
+ krb5_error_code code = 0;
+ int success = 0;
+ int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
+ krb5_ccache defcache = NULL;
+ krb5_principal defcache_princ = NULL, princ;
+ krb5_keytab keytab;
+ const char *deftype = NULL;
+ char *defrealm, *name;
+
+ code = krb5_init_context(&k5->ctx);
+ if (code) {
+ com_err(progname, code, _("while initializing Kerberos 5 library"));
+ return 0;
+ }
+ errctx = k5->ctx;
+
+ if (opts->k5_out_cache_name) {
+ code = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc);
+ if (code != 0) {
+ com_err(progname, code, _("resolving ccache %s"),
+ opts->k5_out_cache_name);
+ goto cleanup;
+ }
+ if (opts->verbose) {
+ fprintf(stderr, _("Using specified cache: %s\n"),
+ opts->k5_out_cache_name);
+ }
+ } else {
+ /* Resolve the default ccache and get its type and default principal
+ * (if it is initialized). */
+ code = krb5_cc_default(k5->ctx, &defcache);
+ if (code) {
+ com_err(progname, code, _("while getting default ccache"));
+ goto cleanup;
+ }
+ deftype = krb5_cc_get_type(k5->ctx, defcache);
+ if (krb5_cc_get_principal(k5->ctx, defcache, &defcache_princ) != 0)
+ defcache_princ = NULL;
+ }
+
+ /* Choose a client principal name. */
+ if (opts->principal_name != NULL) {
+ /* Use the specified principal name. */
+ code = krb5_parse_name_flags(k5->ctx, opts->principal_name, flags,
+ &k5->me);
+ if (code) {
+ com_err(progname, code, _("when parsing name %s"),
+ opts->principal_name);
+ goto cleanup;
+ }
+ } else if (opts->anonymous) {
+ /* Use the anonymous principal for the local realm. */
+ code = krb5_get_default_realm(k5->ctx, &defrealm);
+ if (code) {
+ com_err(progname, code, _("while getting default realm"));
+ goto cleanup;
+ }
+ code = krb5_build_principal_ext(k5->ctx, &k5->me,
+ strlen(defrealm), defrealm,
+ strlen(KRB5_WELLKNOWN_NAMESTR),
+ KRB5_WELLKNOWN_NAMESTR,
+ strlen(KRB5_ANONYMOUS_PRINCSTR),
+ KRB5_ANONYMOUS_PRINCSTR,
+ 0);
+ krb5_free_default_realm(k5->ctx, defrealm);
+ if (code) {
+ com_err(progname, code, _("while building principal"));
+ goto cleanup;
+ }
+ } else if (opts->action == INIT_KT && opts->use_client_keytab) {
+ /* Use the first entry from the client keytab. */
+ code = krb5_kt_client_default(k5->ctx, &keytab);
+ if (code) {
+ com_err(progname, code,
+ _("When resolving the default client keytab"));
+ goto cleanup;
+ }
+ code = k5_kt_get_principal(k5->ctx, keytab, &k5->me);
+ krb5_kt_close(k5->ctx, keytab);
+ if (code) {
+ com_err(progname, code,
+ _("When determining client principal name from keytab"));
+ goto cleanup;
+ }
+ } else if (opts->action == INIT_KT) {
+ /* Use the default host/service name. */
+ code = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST,
+ &k5->me);
+ if (code) {
+ com_err(progname, code,
+ _("when creating default server principal name"));
+ goto cleanup;
+ }
+ if (k5->me->realm.data[0] == 0) {
+ code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+ if (code == 0) {
+ com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
+ _("(principal %s)"), k5->name);
+ } else {
+ com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
+ _("for local services"));
+ }
+ goto cleanup;
+ }
+ } else if (k5->out_cc != NULL) {
+ /* If the output ccache is initialized, use its principal. */
+ if (krb5_cc_get_principal(k5->ctx, k5->out_cc, &princ) == 0)
+ k5->me = princ;
+ } else if (defcache_princ != NULL) {
+ /* Use the default cache's principal, and use the default cache as the
+ * output cache. */
+ k5->out_cc = defcache;
+ defcache = NULL;
+ k5->me = defcache_princ;
+ defcache_princ = NULL;
+ }
+
+ /* If we still haven't chosen, use the local username. */
+ if (k5->me == NULL) {
+ name = get_name_from_os();
+ if (name == NULL) {
+ fprintf(stderr, _("Unable to identify user\n"));
+ goto cleanup;
+ }
+ code = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me);
+ if (code) {
+ com_err(progname, code, _("when parsing name %s"),
+ name);
+ goto cleanup;
+ }
+ }
+
+ if (k5->out_cc == NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
+ /* Use an existing cache for the client principal if we can. */
+ code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
+ if (code != 0 && code != KRB5_CC_NOTFOUND) {
+ com_err(progname, code, _("while searching for ccache for %s"),
+ opts->principal_name);
+ goto cleanup;
+ }
+ if (code == 0) {
+ if (opts->verbose) {
+ fprintf(stderr, _("Using existing cache: %s\n"),
+ krb5_cc_get_name(k5->ctx, k5->out_cc));
+ }
+ k5->switch_to_cache = 1;
+ } else if (defcache_princ != NULL) {
+ /* Create a new cache to avoid overwriting the initialized default
+ * cache. */
+ code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
+ if (code) {
+ com_err(progname, code, _("while generating new ccache"));
+ goto cleanup;
+ }
+ if (opts->verbose) {
+ fprintf(stderr, _("Using new cache: %s\n"),
+ krb5_cc_get_name(k5->ctx, k5->out_cc));
+ }
+ k5->switch_to_cache = 1;
+ }
+ }
+
+ /* Use the default cache if we haven't picked one yet. */
+ if (k5->out_cc == NULL) {
+ k5->out_cc = defcache;
+ defcache = NULL;
+ if (opts->verbose) {
+ fprintf(stderr, _("Using default cache: %s\n"),
+ krb5_cc_get_name(k5->ctx, k5->out_cc));
+ }
+ }
+
+ if (opts->k5_in_cache_name) {
+ code = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc);
+ if (code != 0) {
+ com_err(progname, code, _("resolving ccache %s"),
+ opts->k5_in_cache_name);
+ goto cleanup;
+ }
+ if (opts->verbose) {
+ fprintf(stderr, _("Using specified input cache: %s\n"),
+ opts->k5_in_cache_name);
+ }
+ }
+
+
+ code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+ if (code) {
+ com_err(progname, code, _("when unparsing name"));
+ goto cleanup;
+ }
+ if (opts->verbose)
+ fprintf(stderr, _("Using principal: %s\n"), k5->name);
+
+ opts->principal_name = k5->name;
+
+ success = 1;
+
+cleanup:
+ if (defcache != NULL)
+ krb5_cc_close(k5->ctx, defcache);
+ krb5_free_principal(k5->ctx, defcache_princ);
+ return success;
+}
+
+static void
+k5_end(k5)
+ struct k5_data* k5;
+{
+ if (k5->name)
+ krb5_free_unparsed_name(k5->ctx, k5->name);
+ if (k5->me)
+ krb5_free_principal(k5->ctx, k5->me);
+ if (k5->in_cc)
+ krb5_cc_close(k5->ctx, k5->in_cc);
+ if (k5->out_cc)
+ krb5_cc_close(k5->ctx, k5->out_cc);
+ if (k5->ctx)
+ krb5_free_context(k5->ctx);
+ errctx = NULL;
+ memset(k5, 0, sizeof(*k5));
+}
+
+static krb5_error_code
+KRB5_CALLCONV
+kinit_prompter(
+ krb5_context ctx,
+ void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[]
+)
+{
+ krb5_boolean *pwprompt = data;
+ krb5_prompt_type *ptypes;
+ int i;
+
+ /* Make a note if we receive a password prompt. */
+ ptypes = krb5_get_prompt_types(ctx);
+ for (i = 0; i < num_prompts; i++) {
+ if (ptypes != NULL && ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD)
+ *pwprompt = TRUE;
+ }
+
+ return krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts);
+}
+
+static int
+k5_kinit(opts, k5)
+ struct k_opts* opts;
+ struct k5_data* k5;
+{
+ int notix = 1;
+ krb5_keytab keytab = 0;
+ krb5_creds my_creds;
+ krb5_error_code code = 0;
+ krb5_get_init_creds_opt *options = NULL;
+ krb5_boolean pwprompt = FALSE;
+ int i;
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ code = krb5_get_init_creds_opt_alloc(k5->ctx, &options);
+ if (code)
+ goto cleanup;
+
+ /*
+ From this point on, we can goto cleanup because my_creds is
+ initialized.
+ */
+
+ if (opts->lifetime)
+ krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime);
+ if (opts->rlife)
+ krb5_get_init_creds_opt_set_renew_life(options, opts->rlife);
+ if (opts->forwardable)
+ krb5_get_init_creds_opt_set_forwardable(options, 1);
+ if (opts->not_forwardable)
+ krb5_get_init_creds_opt_set_forwardable(options, 0);
+ if (opts->proxiable)
+ krb5_get_init_creds_opt_set_proxiable(options, 1);
+ if (opts->not_proxiable)
+ krb5_get_init_creds_opt_set_proxiable(options, 0);
+ if (opts->canonicalize)
+ krb5_get_init_creds_opt_set_canonicalize(options, 1);
+ if (opts->anonymous)
+ krb5_get_init_creds_opt_set_anonymous(options, 1);
+ if (opts->addresses)
+ {
+ krb5_address **addresses = NULL;
+ code = krb5_os_localaddr(k5->ctx, &addresses);
+ if (code != 0) {
+ com_err(progname, code, _("getting local addresses"));
+ goto cleanup;
+ }
+ krb5_get_init_creds_opt_set_address_list(options, addresses);
+ }
+ if (opts->no_addresses)
+ krb5_get_init_creds_opt_set_address_list(options, NULL);
+ if (opts->armor_ccache)
+ krb5_get_init_creds_opt_set_fast_ccache_name(k5->ctx, options, opts->armor_ccache);
+ if (opts->request_pac)
+ krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, TRUE);
+ if (opts->not_request_pac)
+ krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, FALSE);
+
+
+ if ((opts->action == INIT_KT) && opts->keytab_name)
+ {
+#ifndef _WIN32
+ if (strncmp(opts->keytab_name, "KDB:", 4) == 0) {
+ code = kinit_kdb_init(&k5->ctx,
+ krb5_princ_realm(k5->ctx, k5->me)->data);
+ if (code != 0) {
+ com_err(progname, code,
+ _("while setting up KDB keytab for realm %s"),
+ krb5_princ_realm(k5->ctx, k5->me)->data);
+ goto cleanup;
+ }
+ }
+#endif
+
+ code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
+ if (code != 0) {
+ com_err(progname, code, _("resolving keytab %s"),
+ opts->keytab_name);
+ goto cleanup;
+ }
+ if (opts->verbose)
+ fprintf(stderr, _("Using keytab: %s\n"), opts->keytab_name);
+ } else if (opts->action == INIT_KT && opts->use_client_keytab) {
+ code = krb5_kt_client_default(k5->ctx, &keytab);
+ if (code != 0) {
+ com_err(progname, code, _("resolving default client keytab"));
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; i < opts->num_pa_opts; i++) {
+ code = krb5_get_init_creds_opt_set_pa(k5->ctx, options,
+ opts->pa_opts[i].attr,
+ opts->pa_opts[i].value);
+ if (code != 0) {
+ com_err(progname, code, _("while setting '%s'='%s'"),
+ opts->pa_opts[i].attr, opts->pa_opts[i].value);
+ goto cleanup;
+ }
+ if (opts->verbose) {
+ fprintf(stderr, _("PA Option %s = %s\n"), opts->pa_opts[i].attr,
+ opts->pa_opts[i].value);
+ }
+ }
+ if (k5->in_cc) {
+ code = krb5_get_init_creds_opt_set_in_ccache(k5->ctx, options,
+ k5->in_cc);
+ if (code)
+ goto cleanup;
+ }
+ code = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options,
+ k5->out_cc);
+ if (code)
+ goto cleanup;
+
+ switch (opts->action) {
+ case INIT_PW:
+ code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
+ 0, kinit_prompter, &pwprompt,
+ opts->starttime,
+ opts->service_name,
+ options);
+ break;
+ case INIT_KT:
+ code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
+ keytab,
+ opts->starttime,
+ opts->service_name,
+ options);
+ break;
+ case VALIDATE:
+ code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
+ opts->service_name);
+ break;
+ case RENEW:
+ code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
+ opts->service_name);
+ break;
+ }
+
+ if (code) {
+ char *doing = 0;
+ switch (opts->action) {
+ case INIT_PW:
+ case INIT_KT:
+ doing = _("getting initial credentials");
+ break;
+ case VALIDATE:
+ doing = _("validating credentials");
+ break;
+ case RENEW:
+ doing = _("renewing credentials");
+ break;
+ }
+
+ /* If reply decryption failed, or if pre-authentication failed and we
+ * were prompted for a password, assume the password was wrong. */
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
+ (pwprompt && code == KRB5KDC_ERR_PREAUTH_FAILED)) {
+ fprintf(stderr, _("%s: Password incorrect while %s\n"), progname,
+ doing);
+ } else {
+ com_err(progname, code, _("while %s"), doing);
+ }
+ goto cleanup;
+ }
+
+ if ((opts->action != INIT_PW) && (opts->action != INIT_KT)) {
+ code = krb5_cc_initialize(k5->ctx, k5->out_cc, opts->canonicalize ?
+ my_creds.client : k5->me);
+ if (code) {
+ com_err(progname, code, _("when initializing cache %s"),
+ opts->k5_out_cache_name?opts->k5_out_cache_name:"");
+ goto cleanup;
+ }
+ if (opts->verbose)
+ fprintf(stderr, _("Initialized cache\n"));
+
+ code = krb5_cc_store_cred(k5->ctx, k5->out_cc, &my_creds);
+ if (code) {
+ com_err(progname, code, _("while storing credentials"));
+ goto cleanup;
+ }
+ if (opts->verbose)
+ fprintf(stderr, _("Stored credentials\n"));
+ }
+ notix = 0;
+
+ if (k5->switch_to_cache) {
+ code = krb5_cc_switch(k5->ctx, k5->out_cc);
+ if (code) {
+ com_err(progname, code, _("while switching to new ccache"));
+ goto cleanup;
+ }
+ }
+
+cleanup:
+#ifndef _WIN32
+ kinit_kdb_fini();
+#endif
+ if (options)
+ krb5_get_init_creds_opt_free(k5->ctx, options);
+ if (my_creds.client == k5->me) {
+ my_creds.client = 0;
+ }
+ if (opts->pa_opts) {
+ free(opts->pa_opts);
+ opts->pa_opts = NULL;
+ opts->num_pa_opts = 0;
+ }
+ krb5_free_cred_contents(k5->ctx, &my_creds);
+ if (keytab)
+ krb5_kt_close(k5->ctx, keytab);
+ return notix?0:1;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct k_opts opts;
+ struct k5_data k5;
+ int authed_k5 = 0;
+
+ setlocale(LC_ALL, "");
+ progname = GET_PROGNAME(argv[0]);
+
+ /* Ensure we can be driven from a pipe */
+ if(!isatty(fileno(stdin)))
+ setvbuf(stdin, 0, _IONBF, 0);
+ if(!isatty(fileno(stdout)))
+ setvbuf(stdout, 0, _IONBF, 0);
+ if(!isatty(fileno(stderr)))
+ setvbuf(stderr, 0, _IONBF, 0);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.action = INIT_PW;
+
+ memset(&k5, 0, sizeof(k5));
+
+ set_com_err_hook (extended_com_err_fn);
+
+ parse_options(argc, argv, &opts);
+
+ if (k5_begin(&opts, &k5))
+ authed_k5 = k5_kinit(&opts, &k5);
+
+ if (authed_k5 && opts.verbose)
+ fprintf(stderr, _("Authenticated to Kerberos v5\n"));
+
+ k5_end(&k5);
+
+ if (!authed_k5)
+ exit(1);
+ return 0;
+}
diff --git a/src/clients/kinit/kinit_kdb.c b/src/clients/kinit/kinit_kdb.c
new file mode 100644
index 000000000000..47baf9010a7b
--- /dev/null
+++ b/src/clients/kinit/kinit_kdb.c
@@ -0,0 +1,77 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/kinit/kinit_kdb.c */
+/*
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/**
+ * @file kinit_kdb.c
+ * Operations to open the KDB and make the KDB key table available
+ * for kinit.
+ */
+
+
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <kdb.h>
+#include "extern.h"
+
+/** Server handle */
+static void *server_handle;
+
+/**
+ * @internal Initialize KDB for given realm
+ * @param context pointer to context that will be re-initialized
+ * @@param realm name of realm to initialize
+ */
+krb5_error_code
+kinit_kdb_init(krb5_context *pcontext, char *realm)
+{
+ kadm5_config_params config;
+ krb5_error_code retval = 0;
+
+ if (*pcontext) {
+ krb5_free_context(*pcontext);
+ *pcontext = NULL;
+ }
+ memset(&config, 0, sizeof config);
+ retval = kadm5_init_krb5_context(pcontext);
+ if (retval)
+ return retval;
+ config.mask = KADM5_CONFIG_REALM;
+ config.realm = realm;
+ retval = kadm5_init(*pcontext, "kinit", NULL /*pass*/,
+ "kinit", &config,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
+ &server_handle);
+ if (retval)
+ return retval;
+ retval = krb5_db_register_keytab(*pcontext);
+ return retval;
+}
+
+void
+kinit_kdb_fini()
+{
+ kadm5_destroy(server_handle);
+}
diff --git a/src/clients/klist/Makefile.in b/src/clients/klist/Makefile.in
new file mode 100644
index 000000000000..b93d5671d6f0
--- /dev/null
+++ b/src/clients/klist/Makefile.in
@@ -0,0 +1,35 @@
+mydir=clients$(S)klist
+BUILDTOP=$(REL)..$(S)..
+
+##WIN32##LOCALINCLUDES=-I$(BUILDTOP)\util\windows\
+
+SRCS = klist.c
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KLIST=$(OUTPRE)klist.exe
+
+##WIN32##EXERES=$(KLIST:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKLIST_APP -fo $@ -r $**
+
+all-unix: klist
+##WIN32##all-windows: $(KLIST)
+
+klist: klist.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ klist.o $(KRB5_BASE_LIBS)
+
+##WIN32##$(KLIST): $(OUTPRE)klist.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) -out:$@ $** ws2_32.lib $(SCLIB)
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) klist.o klist
+
+install-unix:
+ for f in klist; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/klist/deps b/src/clients/klist/deps
new file mode 100644
index 000000000000..ef0edbc3ef45
--- /dev/null
+++ b/src/clients/klist/deps
@@ -0,0 +1,14 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)klist.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/fake-addrinfo.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ klist.c
diff --git a/src/clients/klist/klist.c b/src/clients/klist/klist.c
new file mode 100644
index 000000000000..ba19788a25c4
--- /dev/null
+++ b/src/clients/klist/klist.c
@@ -0,0 +1,874 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/klist/klist.c - List contents of credential cache or keytab */
+/*
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include <krb5.h>
+#include <com_err.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+/* Need definition of INET6 before network headers, for IRIX. */
+#if defined(HAVE_ARPA_INET_H)
+#include <arpa/inet.h>
+#endif
+
+#ifndef _WIN32
+#define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
+#else
+#define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
+#endif
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+extern int optind;
+
+int show_flags = 0, show_time = 0, status_only = 0, show_keys = 0;
+int show_etype = 0, show_addresses = 0, no_resolve = 0, print_version = 0;
+int show_adtype = 0, show_all = 0, list_all = 0, use_client_keytab = 0;
+int show_config = 0;
+char *defname;
+char *progname;
+krb5_int32 now;
+unsigned int timestamp_width;
+
+krb5_context kcontext;
+
+krb5_boolean is_local_tgt (krb5_principal princ, krb5_data *realm);
+char * etype_string (krb5_enctype );
+void show_credential (krb5_creds *);
+
+void list_all_ccaches (void);
+int list_ccache (krb5_ccache);
+void show_all_ccaches (void);
+void do_ccache_name (char *);
+int show_ccache (krb5_ccache);
+int check_ccache (krb5_ccache);
+void do_keytab (char *);
+void printtime (time_t);
+void one_addr (krb5_address *);
+void fillit (FILE *, unsigned int, int);
+
+#define DEFAULT 0
+#define CCACHE 1
+#define KEYTAB 2
+
+static void usage()
+{
+#define KRB_AVAIL_STRING(x) ((x)?"available":"not available")
+
+ fprintf(stderr, _("Usage: %s [-e] [-V] [[-c] [-l] [-A] [-d] [-f] [-s] "
+ "[-a [-n]]] [-k [-t] [-K]] [name]\n"), progname);
+ fprintf(stderr, _("\t-c specifies credentials cache\n"));
+ fprintf(stderr, _("\t-k specifies keytab\n"));
+ fprintf(stderr, _("\t (Default is credentials cache)\n"));
+ fprintf(stderr, _("\t-i uses default client keytab if no name given\n"));
+ fprintf(stderr, _("\t-l lists credential caches in collection\n"));
+ fprintf(stderr, _("\t-A shows content of all credential caches\n"));
+ fprintf(stderr, _("\t-e shows the encryption type\n"));
+ fprintf(stderr, _("\t-V shows the Kerberos version and exits\n"));
+ fprintf(stderr, _("\toptions for credential caches:\n"));
+ fprintf(stderr, _("\t\t-d shows the submitted authorization data "
+ "types\n"));
+ fprintf(stderr, _("\t\t-f shows credentials flags\n"));
+ fprintf(stderr, _("\t\t-s sets exit status based on valid tgt "
+ "existence\n"));
+ fprintf(stderr, _("\t\t-a displays the address list\n"));
+ fprintf(stderr, _("\t\t\t-n do not reverse-resolve\n"));
+ fprintf(stderr, _("\toptions for keytabs:\n"));
+ fprintf(stderr, _("\t\t-t shows keytab entry timestamps\n"));
+ fprintf(stderr, _("\t\t-K shows keytab entry keys\n"));
+ exit(1);
+}
+
+static void
+extended_com_err_fn(const char *prog, errcode_t code, const char *fmt,
+ va_list args)
+{
+ const char *msg;
+
+ msg = krb5_get_error_message(kcontext, code);
+ fprintf(stderr, "%s: %s%s", prog, msg, (*fmt == '\0') ? "" : " ");
+ krb5_free_error_message(kcontext, msg);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ char *name;
+ int mode;
+
+ setlocale(LC_ALL, "");
+ progname = GET_PROGNAME(argv[0]);
+ set_com_err_hook(extended_com_err_fn);
+
+ name = NULL;
+ mode = DEFAULT;
+ /* V=version so v can be used for verbose later if desired. */
+ while ((c = getopt(argc, argv, "dfetKsnacki45lAVC")) != -1) {
+ switch (c) {
+ case 'd':
+ show_adtype = 1;
+ break;
+ case 'f':
+ show_flags = 1;
+ break;
+ case 'e':
+ show_etype = 1;
+ break;
+ case 't':
+ show_time = 1;
+ break;
+ case 'K':
+ show_keys = 1;
+ break;
+ case 's':
+ status_only = 1;
+ break;
+ case 'n':
+ no_resolve = 1;
+ break;
+ case 'a':
+ show_addresses = 1;
+ break;
+ case 'c':
+ if (mode != DEFAULT) usage();
+ mode = CCACHE;
+ break;
+ case 'k':
+ if (mode != DEFAULT) usage();
+ mode = KEYTAB;
+ break;
+ case 'i':
+ use_client_keytab = 1;
+ break;
+ case '4':
+ fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
+ exit(3);
+ break;
+ case '5':
+ break;
+ case 'l':
+ list_all = 1;
+ break;
+ case 'A':
+ show_all = 1;
+ break;
+ case 'C':
+ show_config = 1;
+ break;
+ case 'V':
+ print_version = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (no_resolve && !show_addresses) {
+ usage();
+ }
+
+ if (mode == DEFAULT || mode == CCACHE) {
+ if (show_time || show_keys)
+ usage();
+ if ((show_all && list_all) || (status_only && list_all))
+ usage();
+ } else {
+ if (show_flags || status_only || show_addresses ||
+ show_all || list_all)
+ usage();
+ }
+
+ if (argc - optind > 1) {
+ fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"),
+ argv[optind+1]);
+ usage();
+ }
+
+ if (print_version) {
+#ifdef _WIN32 /* No access to autoconf vars; fix somehow. */
+ printf("Kerberos for Windows\n");
+#else
+ printf(_("%s version %s\n"), PACKAGE_NAME, PACKAGE_VERSION);
+#endif
+ exit(0);
+ }
+
+ name = (optind == argc-1) ? argv[optind] : 0;
+
+ now = time(0);
+ {
+ char tmp[BUFSIZ];
+
+ if (!krb5_timestamp_to_sfstring(now, tmp, 20, (char *) NULL) ||
+ !krb5_timestamp_to_sfstring(now, tmp, sizeof(tmp),
+ (char *) NULL))
+ timestamp_width = (int) strlen(tmp);
+ else
+ timestamp_width = 15;
+ }
+
+ {
+ krb5_error_code retval;
+ retval = krb5_init_context(&kcontext);
+ if (retval) {
+ com_err(progname, retval, _("while initializing krb5"));
+ exit(1);
+ }
+
+ if (list_all)
+ list_all_ccaches();
+ else if (show_all)
+ show_all_ccaches();
+ else if (mode == DEFAULT || mode == CCACHE)
+ do_ccache_name(name);
+ else
+ do_keytab(name);
+ }
+
+ return 0;
+}
+
+void do_keytab(name)
+ char *name;
+{
+ krb5_keytab kt;
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ char buf[BUFSIZ]; /* hopefully large enough for any type */
+ char *pname;
+ int code;
+
+ if (name == NULL && use_client_keytab) {
+ if ((code = krb5_kt_client_default(kcontext, &kt))) {
+ com_err(progname, code, _("while getting default client keytab"));
+ exit(1);
+ }
+ } else if (name == NULL) {
+ if ((code = krb5_kt_default(kcontext, &kt))) {
+ com_err(progname, code, _("while getting default keytab"));
+ exit(1);
+ }
+ } else {
+ if ((code = krb5_kt_resolve(kcontext, name, &kt))) {
+ com_err(progname, code, _("while resolving keytab %s"), name);
+ exit(1);
+ }
+ }
+
+ if ((code = krb5_kt_get_name(kcontext, kt, buf, BUFSIZ))) {
+ com_err(progname, code, _("while getting keytab name"));
+ exit(1);
+ }
+
+ printf("Keytab name: %s\n", buf);
+
+ if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor))) {
+ com_err(progname, code, _("while starting keytab scan"));
+ exit(1);
+ }
+
+ /* XXX Translating would disturb table alignment; skip for now. */
+ if (show_time) {
+ printf("KVNO Timestamp");
+ fillit(stdout, timestamp_width - sizeof("Timestamp") + 2, (int) ' ');
+ printf("Principal\n");
+ printf("---- ");
+ fillit(stdout, timestamp_width, (int) '-');
+ printf(" ");
+ fillit(stdout, 78 - timestamp_width - sizeof("KVNO"), (int) '-');
+ printf("\n");
+ } else {
+ printf("KVNO Principal\n");
+ printf("---- --------------------------------------------------------------------------\n");
+ }
+
+ while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) {
+ if ((code = krb5_unparse_name(kcontext, entry.principal, &pname))) {
+ com_err(progname, code, _("while unparsing principal name"));
+ exit(1);
+ }
+ printf("%4d ", entry.vno);
+ if (show_time) {
+ printtime(entry.timestamp);
+ printf(" ");
+ }
+ printf("%s", pname);
+ if (show_etype)
+ printf(" (%s) " , etype_string(entry.key.enctype));
+ if (show_keys) {
+ printf(" (0x");
+ {
+ unsigned int i;
+ for (i = 0; i < entry.key.length; i++)
+ printf("%02x", entry.key.contents[i]);
+ }
+ printf(")");
+ }
+ printf("\n");
+ krb5_free_unparsed_name(kcontext, pname);
+ krb5_free_keytab_entry_contents(kcontext, &entry);
+ }
+ if (code && code != KRB5_KT_END) {
+ com_err(progname, code, _("while scanning keytab"));
+ exit(1);
+ }
+ if ((code = krb5_kt_end_seq_get(kcontext, kt, &cursor))) {
+ com_err(progname, code, _("while ending keytab scan"));
+ exit(1);
+ }
+ exit(0);
+}
+
+void
+list_all_ccaches(void)
+{
+ krb5_error_code code;
+ krb5_ccache cache;
+ krb5_cccol_cursor cursor;
+ int exit_status;
+
+ code = krb5_cccol_cursor_new(kcontext, &cursor);
+ if (code) {
+ if (!status_only)
+ com_err(progname, code, _("while listing ccache collection"));
+ exit(1);
+ }
+
+ /* XXX Translating would disturb table alignment; skip for now. */
+ printf("%-30s %s\n", "Principal name", "Cache name");
+ printf("%-30s %s\n", "--------------", "----------");
+ exit_status = 1;
+ while (!(code = krb5_cccol_cursor_next(kcontext, cursor, &cache)) &&
+ cache != NULL) {
+ exit_status = list_ccache(cache) && exit_status;
+ krb5_cc_close(kcontext, cache);
+ }
+ krb5_cccol_cursor_free(kcontext, &cursor);
+ exit(exit_status);
+}
+
+int
+list_ccache(krb5_ccache cache)
+{
+ krb5_error_code code;
+ krb5_principal princ = NULL;
+ char *princname = NULL, *ccname = NULL;
+ int expired, status = 1;
+
+ code = krb5_cc_get_principal(kcontext, cache, &princ);
+ if (code) /* Uninitialized cache file, probably. */
+ goto cleanup;
+ code = krb5_unparse_name(kcontext, princ, &princname);
+ if (code)
+ goto cleanup;
+ code = krb5_cc_get_full_name(kcontext, cache, &ccname);
+ if (code)
+ goto cleanup;
+
+ expired = check_ccache(cache);
+
+ printf("%-30.30s %s", princname, ccname);
+ if (expired)
+ printf(" %s", _("(Expired)"));
+ printf("\n");
+
+ status = 0;
+cleanup:
+ krb5_free_principal(kcontext, princ);
+ krb5_free_unparsed_name(kcontext, princname);
+ krb5_free_string(kcontext, ccname);
+ return status;
+}
+
+void
+show_all_ccaches(void)
+{
+ krb5_error_code code;
+ krb5_ccache cache;
+ krb5_cccol_cursor cursor;
+ krb5_boolean first;
+ int exit_status, st;
+
+ code = krb5_cccol_cursor_new(kcontext, &cursor);
+ if (code) {
+ if (!status_only)
+ com_err(progname, code, _("while listing ccache collection"));
+ exit(1);
+ }
+ exit_status = 1;
+ first = TRUE;
+ while (!(code = krb5_cccol_cursor_next(kcontext, cursor, &cache)) &&
+ cache != NULL) {
+ if (!status_only && !first)
+ printf("\n");
+ first = FALSE;
+ st = status_only ? check_ccache(cache) : show_ccache(cache);
+ exit_status = st && exit_status;
+ krb5_cc_close(kcontext, cache);
+ }
+ krb5_cccol_cursor_free(kcontext, &cursor);
+ exit(exit_status);
+}
+
+void
+do_ccache_name(char *name)
+{
+ krb5_error_code code;
+ krb5_ccache cache;
+
+ if (name == NULL) {
+ if ((code = krb5_cc_default(kcontext, &cache))) {
+ if (!status_only)
+ com_err(progname, code, _("while getting default ccache"));
+ exit(1);
+ }
+ } else {
+ if ((code = krb5_cc_resolve(kcontext, name, &cache))) {
+ if (!status_only)
+ com_err(progname, code, _("while resolving ccache %s"),
+ name);
+ exit(1);
+ }
+ }
+ exit(status_only ? check_ccache(cache) : show_ccache(cache));
+}
+
+/* Display the contents of cache. */
+int
+show_ccache(krb5_ccache cache)
+{
+ krb5_cc_cursor cur;
+ krb5_creds creds;
+ krb5_principal princ;
+ krb5_error_code code;
+
+ if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) {
+ com_err(progname, code, "");
+ return 1;
+ }
+ if ((code = krb5_unparse_name(kcontext, princ, &defname))) {
+ com_err(progname, code, _("while unparsing principal name"));
+ return 1;
+ }
+
+ printf(_("Ticket cache: %s:%s\nDefault principal: %s\n\n"),
+ krb5_cc_get_type(kcontext, cache),
+ krb5_cc_get_name(kcontext, cache), defname);
+ /* XXX Translating would disturb table alignment; skip for now. */
+ fputs("Valid starting", stdout);
+ fillit(stdout, timestamp_width - sizeof("Valid starting") + 3, (int) ' ');
+ fputs("Expires", stdout);
+ fillit(stdout, timestamp_width - sizeof("Expires") + 3, (int) ' ');
+ fputs("Service principal\n", stdout);
+
+ if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) {
+ com_err(progname, code, _("while starting to retrieve tickets"));
+ return 1;
+ }
+ while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
+ if (show_config || !krb5_is_config_principal(kcontext, creds.server))
+ show_credential(&creds);
+ krb5_free_cred_contents(kcontext, &creds);
+ }
+ krb5_free_principal(kcontext, princ);
+ krb5_free_unparsed_name(kcontext, defname);
+ defname = NULL;
+ if (code == KRB5_CC_END) {
+ if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) {
+ com_err(progname, code, _("while finishing ticket retrieval"));
+ return 1;
+ }
+ return 0;
+ } else {
+ com_err(progname, code, _("while retrieving a ticket"));
+ return 1;
+ }
+}
+
+/* Return 0 if cache is accessible, present, and unexpired; return 1 if not. */
+int
+check_ccache(krb5_ccache cache)
+{
+ krb5_error_code ret;
+ krb5_cc_cursor cur;
+ krb5_creds creds;
+ krb5_principal princ;
+ krb5_boolean found_tgt, found_current_tgt, found_current_cred;
+
+ if (krb5_cc_get_principal(kcontext, cache, &princ) != 0)
+ return 1;
+ if (krb5_cc_start_seq_get(kcontext, cache, &cur) != 0)
+ return 1;
+ found_tgt = found_current_tgt = found_current_cred = FALSE;
+ while (!(ret = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
+ if (is_local_tgt(creds.server, &princ->realm)) {
+ found_tgt = TRUE;
+ if (creds.times.endtime > now)
+ found_current_tgt = TRUE;
+ } else if (!krb5_is_config_principal(kcontext, creds.server) &&
+ creds.times.endtime > now) {
+ found_current_cred = TRUE;
+ }
+ krb5_free_cred_contents(kcontext, &creds);
+ }
+ krb5_free_principal(kcontext, princ);
+ if (ret != KRB5_CC_END)
+ return 1;
+ if (krb5_cc_end_seq_get(kcontext, cache, &cur) != 0)
+ return 1;
+
+ /* If the cache contains at least one local TGT, require that it be
+ * current. Otherwise accept any current cred. */
+ if (found_tgt)
+ return found_current_tgt ? 0 : 1;
+ return found_current_cred ? 0 : 1;
+}
+
+/* Return true if princ is the local krbtgt principal for local_realm. */
+krb5_boolean
+is_local_tgt(krb5_principal princ, krb5_data *realm)
+{
+ return princ->length == 2 && data_eq(princ->realm, *realm) &&
+ data_eq_string(princ->data[0], KRB5_TGS_NAME) &&
+ data_eq(princ->data[1], *realm);
+}
+
+char *
+etype_string(enctype)
+ krb5_enctype enctype;
+{
+ static char buf[100];
+ krb5_error_code retval;
+
+ if ((retval = krb5_enctype_to_name(enctype, FALSE, buf, sizeof(buf)))) {
+ /* XXX if there's an error != EINVAL, I should probably report it */
+ snprintf(buf, sizeof(buf), "etype %d", enctype);
+ }
+
+ return buf;
+}
+
+static char *
+flags_string(cred)
+ register krb5_creds *cred;
+{
+ static char buf[32];
+ int i = 0;
+
+ if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
+ buf[i++] = 'F';
+ if (cred->ticket_flags & TKT_FLG_FORWARDED)
+ buf[i++] = 'f';
+ if (cred->ticket_flags & TKT_FLG_PROXIABLE)
+ buf[i++] = 'P';
+ if (cred->ticket_flags & TKT_FLG_PROXY)
+ buf[i++] = 'p';
+ if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
+ buf[i++] = 'D';
+ if (cred->ticket_flags & TKT_FLG_POSTDATED)
+ buf[i++] = 'd';
+ if (cred->ticket_flags & TKT_FLG_INVALID)
+ buf[i++] = 'i';
+ if (cred->ticket_flags & TKT_FLG_RENEWABLE)
+ buf[i++] = 'R';
+ if (cred->ticket_flags & TKT_FLG_INITIAL)
+ buf[i++] = 'I';
+ if (cred->ticket_flags & TKT_FLG_HW_AUTH)
+ buf[i++] = 'H';
+ if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
+ buf[i++] = 'A';
+ if (cred->ticket_flags & TKT_FLG_TRANSIT_POLICY_CHECKED)
+ buf[i++] = 'T';
+ if (cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE)
+ buf[i++] = 'O'; /* D/d are taken. Use short strings? */
+ if (cred->ticket_flags & TKT_FLG_ANONYMOUS)
+ buf[i++] = 'a';
+ buf[i] = '\0';
+ return(buf);
+}
+
+void
+printtime(tv)
+ time_t tv;
+{
+ char timestring[BUFSIZ];
+ char fill;
+
+ fill = ' ';
+ if (!krb5_timestamp_to_sfstring((krb5_timestamp) tv,
+ timestring,
+ timestamp_width+1,
+ &fill)) {
+ printf("%s", timestring);
+ }
+}
+
+static void
+print_config_data(int col, krb5_data *data)
+{
+ unsigned int i;
+
+ for (i = 0; i < data->length; i++) {
+ while (col < 8) {
+ putchar(' ');
+ col++;
+ }
+ if (data->data[i] > 0x20 && data->data[i] < 0x7f) {
+ putchar(data->data[i]);
+ col++;
+ } else {
+ col += printf("\\%03o", (unsigned char)data->data[i]);
+ }
+ if (col > 72) {
+ putchar('\n');
+ col = 0;
+ }
+ }
+ if (col > 0)
+ putchar('\n');
+}
+
+void
+show_credential(cred)
+ register krb5_creds * cred;
+{
+ krb5_error_code retval;
+ krb5_ticket *tkt;
+ char *name, *sname, *flags;
+ int extra_field = 0, ccol = 0, i;
+
+ retval = krb5_unparse_name(kcontext, cred->client, &name);
+ if (retval) {
+ com_err(progname, retval, _("while unparsing client name"));
+ return;
+ }
+ retval = krb5_unparse_name(kcontext, cred->server, &sname);
+ if (retval) {
+ com_err(progname, retval, _("while unparsing server name"));
+ krb5_free_unparsed_name(kcontext, name);
+ return;
+ }
+ if (!cred->times.starttime)
+ cred->times.starttime = cred->times.authtime;
+
+ if (!krb5_is_config_principal(kcontext, cred->server)) {
+ printtime(cred->times.starttime);
+ putchar(' '); putchar(' ');
+ printtime(cred->times.endtime);
+ putchar(' '); putchar(' ');
+
+ printf("%s\n", sname);
+ } else {
+ fputs("config: ", stdout);
+ ccol = 8;
+ for (i = 1; i < cred->server->length; i++) {
+ ccol += printf("%s%.*s%s",
+ i > 1 ? "(" : "",
+ (int)cred->server->data[i].length,
+ cred->server->data[i].data,
+ i > 1 ? ")" : "");
+ }
+ fputs(" = ", stdout);
+ ccol += 3;
+ }
+
+ if (strcmp(name, defname)) {
+ printf(_("\tfor client %s"), name);
+ extra_field++;
+ }
+
+ if (krb5_is_config_principal(kcontext, cred->server))
+ print_config_data(ccol, &cred->ticket);
+
+ if (cred->times.renew_till) {
+ if (!extra_field)
+ fputs("\t",stdout);
+ else
+ fputs(", ",stdout);
+ fputs(_("renew until "), stdout);
+ printtime(cred->times.renew_till);
+ extra_field += 2;
+ }
+
+ if (extra_field > 3) {
+ fputs("\n", stdout);
+ extra_field = 0;
+ }
+
+ if (show_flags) {
+ flags = flags_string(cred);
+ if (flags && *flags) {
+ if (!extra_field)
+ fputs("\t",stdout);
+ else
+ fputs(", ",stdout);
+ printf(_("Flags: %s"), flags);
+ extra_field++;
+ }
+ }
+
+ if (extra_field > 2) {
+ fputs("\n", stdout);
+ extra_field = 0;
+ }
+
+ if (show_etype) {
+ retval = krb5_decode_ticket(&cred->ticket, &tkt);
+ if (retval)
+ goto err_tkt;
+
+ if (!extra_field)
+ fputs("\t",stdout);
+ else
+ fputs(", ",stdout);
+ printf(_("Etype (skey, tkt): %s, "),
+ etype_string(cred->keyblock.enctype));
+ printf("%s ",
+ etype_string(tkt->enc_part.enctype));
+ extra_field++;
+
+ err_tkt:
+ if (tkt != NULL)
+ krb5_free_ticket(kcontext, tkt);
+ }
+
+ if (show_adtype) {
+ if (cred->authdata != NULL) {
+ if (!extra_field)
+ fputs("\t",stdout);
+ else
+ fputs(", ",stdout);
+ printf(_("AD types: "));
+ for (i = 0; cred->authdata[i] != NULL; i++) {
+ if (i)
+ printf(", ");
+ printf("%d", cred->authdata[i]->ad_type);
+ }
+ extra_field++;
+ }
+ }
+
+ /* if any additional info was printed, extra_field is non-zero */
+ if (extra_field)
+ putchar('\n');
+
+
+ if (show_addresses) {
+ if (!cred->addresses || !cred->addresses[0]) {
+ printf(_("\tAddresses: (none)\n"));
+ } else {
+ printf(_("\tAddresses: "));
+ one_addr(cred->addresses[0]);
+
+ for (i=1; cred->addresses[i]; i++) {
+ printf(", ");
+ one_addr(cred->addresses[i]);
+ }
+
+ printf("\n");
+ }
+ }
+
+ krb5_free_unparsed_name(kcontext, name);
+ krb5_free_unparsed_name(kcontext, sname);
+}
+
+#include "port-sockets.h"
+#include "socket-utils.h" /* for ss2sin etc */
+#include "fake-addrinfo.h"
+
+void one_addr(a)
+ krb5_address *a;
+{
+ struct sockaddr_storage ss;
+ int err;
+ char namebuf[NI_MAXHOST];
+
+ memset (&ss, 0, sizeof (ss));
+
+ switch (a->addrtype) {
+ case ADDRTYPE_INET:
+ if (a->length != 4) {
+ broken:
+ printf(_("broken address (type %d length %d)"),
+ a->addrtype, a->length);
+ return;
+ }
+ {
+ struct sockaddr_in *sinp = ss2sin (&ss);
+ sinp->sin_family = AF_INET;
+ memcpy (&sinp->sin_addr, a->contents, 4);
+ }
+ break;
+ case ADDRTYPE_INET6:
+ if (a->length != 16)
+ goto broken;
+ {
+ struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
+ sin6p->sin6_family = AF_INET6;
+ memcpy (&sin6p->sin6_addr, a->contents, 16);
+ }
+ break;
+ default:
+ printf(_("unknown addrtype %d"), a->addrtype);
+ return;
+ }
+
+ namebuf[0] = 0;
+ err = getnameinfo (ss2sa (&ss), sa_socklen (ss2sa (&ss)),
+ namebuf, sizeof (namebuf), 0, 0,
+ no_resolve ? NI_NUMERICHOST : 0U);
+ if (err) {
+ printf(_("unprintable address (type %d, error %d %s)"), a->addrtype,
+ err, gai_strerror (err));
+ return;
+ }
+ printf ("%s", namebuf);
+}
+
+void
+fillit(f, num, c)
+ FILE *f;
+ unsigned int num;
+ int c;
+{
+ unsigned int i;
+
+ for (i=0; i<num; i++)
+ fputc(c, f);
+}
diff --git a/src/clients/kpasswd/Makefile.in b/src/clients/kpasswd/Makefile.in
new file mode 100644
index 000000000000..bd4a08cc1daf
--- /dev/null
+++ b/src/clients/kpasswd/Makefile.in
@@ -0,0 +1,37 @@
+mydir=clients$(S)kpasswd
+BUILDTOP=$(REL)..$(S)..
+
+SRCS=kpasswd.c ksetpwd.c
+
+kpasswd: kpasswd.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o kpasswd kpasswd.o $(KRB5_BASE_LIBS)
+
+ksetpwd: ksetpwd.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o ksetpwd ksetpwd.o $(KRB5_BASE_LIBS)
+
+kpasswd.o: $(srcdir)/kpasswd.c
+ksetpwd.o: $(srcdir)/ksetpwd.c
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KPWD=$(OUTPRE)kpasswd.exe
+
+##WIN32##EXERES=$(KPWD:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKPASSWD_APP -fo $@ -r $**
+
+all-unix: kpasswd ksetpwd
+
+clean-unix::
+ $(RM) kpasswd.o kpasswd ksetpwd.o ksetpwd
+
+install-all install-kdc install-server install-client install-unix:
+ $(INSTALL_PROGRAM) kpasswd $(DESTDIR)$(CLIENT_BINDIR)/`echo kpasswd|sed '$(transform)'`
+
+##WIN32##all-windows: $(KPWD)
+
+##WIN32##$(KPWD): $(OUTPRE)kpasswd.obj $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) -out:$@ $** $(SCLIB)
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
diff --git a/src/clients/kpasswd/deps b/src/clients/kpasswd/deps
new file mode 100644
index 000000000000..0c01c30cc12e
--- /dev/null
+++ b/src/clients/kpasswd/deps
@@ -0,0 +1,11 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kpasswd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/krb5.h \
+ kpasswd.c
+$(OUTPRE)ksetpwd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/krb5.h \
+ ksetpwd.c
diff --git a/src/clients/kpasswd/kpasswd.c b/src/clients/kpasswd/kpasswd.c
new file mode 100644
index 000000000000..efc596edf7ce
--- /dev/null
+++ b/src/clients/kpasswd/kpasswd.c
@@ -0,0 +1,176 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include "k5-platform.h"
+#include <locale.h>
+#include <sys/types.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <krb5.h>
+
+#define P1 _("Enter new password")
+#define P2 _("Enter it again")
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+
+static
+void get_name_from_passwd_file(program_name, kcontext, me)
+ char * program_name;
+ krb5_context kcontext;
+ krb5_principal * me;
+{
+ struct passwd *pw;
+ krb5_error_code code;
+ if ((pw = getpwuid(getuid()))) {
+ if ((code = krb5_parse_name(kcontext, pw->pw_name, me))) {
+ com_err(program_name, code, _("when parsing name %s"),
+ pw->pw_name);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, _("Unable to identify user from password file\n"));
+ exit(1);
+ }
+}
+#else /* HAVE_PWD_H */
+void get_name_from_passwd_file(kcontext, me)
+ krb5_context kcontext;
+ krb5_principal * me;
+{
+ fprintf(stderr, _("Unable to identify user\n"));
+ exit(1);
+}
+#endif /* HAVE_PWD_H */
+
+int main(int argc, char *argv[])
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal princ = NULL;
+ char *pname;
+ krb5_ccache ccache;
+ krb5_get_init_creds_opt *opts = NULL;
+ krb5_creds creds;
+ char *message;
+
+ char pw[1024];
+ unsigned int pwlen;
+ int result_code;
+ krb5_data result_code_string, result_string;
+
+ setlocale(LC_ALL, "");
+ if (argc > 2) {
+ fprintf(stderr, _("usage: %s [principal]\n"), argv[0]);
+ exit(1);
+ }
+
+ pname = argv[1];
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ com_err(argv[0], ret, _("initializing kerberos library"));
+ exit(1);
+ }
+ if ((ret = krb5_get_init_creds_opt_alloc(context, &opts))) {
+ com_err(argv[0], ret, _("allocating krb5_get_init_creds_opt"));
+ exit(1);
+ }
+
+ /* in order, use the first of:
+ - a name specified on the command line
+ - the principal name from an existing ccache
+ - the name corresponding to the ruid of the process
+
+ otherwise, it's an error.
+ We always attempt to open the default ccache in order to use FAST if
+ possible.
+ */
+ ret = krb5_cc_default(context, &ccache);
+ if (ret != 0) {
+ com_err(argv[0], ret, _("opening default ccache"));
+ exit(1);
+ }
+ ret = krb5_cc_get_principal(context, ccache, &princ);
+ if (ret != 0 && ret != KRB5_CC_NOTFOUND && ret != KRB5_FCC_NOFILE) {
+ com_err(argv[0], ret, _("getting principal from ccache"));
+ exit(1);
+ } else {
+ if (princ != NULL) {
+ ret = krb5_get_init_creds_opt_set_fast_ccache(context, opts,
+ ccache);
+ if (ret) {
+ com_err(argv[0], ret, _("while setting FAST ccache"));
+ exit(1);
+ }
+ }
+ }
+ ret = krb5_cc_close(context, ccache);
+ if (ret != 0) {
+ com_err(argv[0], ret, _("closing ccache"));
+ exit(1);
+ }
+ if (pname) {
+ krb5_free_principal(context, princ);
+ princ = NULL;
+ if ((ret = krb5_parse_name(context, pname, &princ))) {
+ com_err(argv[0], ret, _("parsing client name"));
+ exit(1);
+ }
+ }
+ if (princ == NULL)
+ get_name_from_passwd_file(argv[0], context, &princ);
+
+ krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
+ krb5_get_init_creds_opt_set_renew_life(opts, 0);
+ krb5_get_init_creds_opt_set_forwardable(opts, 0);
+ krb5_get_init_creds_opt_set_proxiable(opts, 0);
+
+ if ((ret = krb5_get_init_creds_password(context, &creds, princ, NULL,
+ krb5_prompter_posix, NULL,
+ 0, "kadmin/changepw", opts))) {
+ if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ com_err(argv[0], 0,
+ _("Password incorrect while getting initial ticket"));
+ else
+ com_err(argv[0], ret, _("getting initial ticket"));
+ krb5_get_init_creds_opt_free(context, opts);
+ exit(1);
+ }
+
+ pwlen = sizeof(pw);
+ if ((ret = krb5_read_password(context, P1, P2, pw, &pwlen))) {
+ com_err(argv[0], ret, _("while reading password"));
+ krb5_get_init_creds_opt_free(context, opts);
+ exit(1);
+ }
+
+ if ((ret = krb5_change_password(context, &creds, pw,
+ &result_code, &result_code_string,
+ &result_string))) {
+ com_err(argv[0], ret, _("changing password"));
+ krb5_get_init_creds_opt_free(context, opts);
+ exit(1);
+ }
+
+ if (result_code) {
+ if (krb5_chpw_message(context, &result_string, &message) != 0)
+ message = NULL;
+ printf("%.*s%s%s\n",
+ (int) result_code_string.length, result_code_string.data,
+ message ? ": " : "", message ? message : NULL);
+ krb5_free_string(context, message);
+ krb5_get_init_creds_opt_free(context, opts);
+ exit(2);
+ }
+
+ if (result_string.data != NULL)
+ free(result_string.data);
+ if (result_code_string.data != NULL)
+ free(result_code_string.data);
+ krb5_get_init_creds_opt_free(context, opts);
+
+ printf(_("Password changed.\n"));
+ exit(0);
+}
diff --git a/src/clients/kpasswd/ksetpwd.c b/src/clients/kpasswd/ksetpwd.c
new file mode 100644
index 000000000000..2aafb6cedeb0
--- /dev/null
+++ b/src/clients/kpasswd/ksetpwd.c
@@ -0,0 +1,309 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include <k5-platform.h>
+#include <krb5.h>
+#include <unistd.h>
+#include <time.h>
+
+#define TKTTIMELEFT 60*10 /* ten minutes */
+
+static int verify_creds()
+{
+ krb5_context kcontext;
+ krb5_ccache ccache;
+ krb5_error_code kres;
+
+ kres = krb5_init_context(&kcontext);
+ if( kres == 0 )
+ {
+ kres = krb5_cc_default( kcontext, &ccache );
+ if( kres == 0 )
+ {
+ krb5_principal user_princ;
+
+ kres = krb5_cc_get_principal( kcontext, ccache, &user_princ );
+ if( kres == 0 )
+ krb5_free_principal( kcontext, user_princ );
+ krb5_cc_close( kcontext, ccache );
+ }
+ krb5_free_context(kcontext);
+ }
+ return kres;
+}
+
+static void get_init_creds_opt_init( krb5_get_init_creds_opt *outOptions )
+{
+ krb5_preauthtype preauth[] = { KRB5_PADATA_ENC_TIMESTAMP };
+ krb5_enctype etypes[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_CRC};
+ krb5_get_init_creds_opt_set_address_list(outOptions, NULL);
+ krb5_get_init_creds_opt_set_etype_list( outOptions, etypes, sizeof(etypes)/sizeof(krb5_enctype) );
+ krb5_get_init_creds_opt_set_preauth_list(outOptions, preauth, sizeof(preauth)/sizeof(krb5_preauthtype) );
+}
+
+typedef void * kbrccache_t;
+#define CCACHE_PREFIX_DEFAULT "MEMORY:C_"
+
+static kbrccache_t userinitcontext(
+ const char * user, const char * domain, const char * passwd, const char * cachename, int initialize,
+ int * outError )
+{
+ krb5_context kcontext = 0;
+ krb5_ccache kcache = 0;
+ krb5_creds kcreds;
+ krb5_principal kme = 0;
+ krb5_error_code kres;
+ char * pPass = strdup( passwd );
+ char * pName = NULL;
+ char * pCacheName = NULL;
+ int numCreds = 0;
+
+ memset( &kcreds, 0, sizeof(kcreds) );
+ kres = krb5_init_context( &kcontext );
+ if( kres )
+ goto return_error;
+ if( domain )
+ kres = krb5_build_principal( kcontext, &kme, strlen(domain), domain, user, (char *) 0 );
+ else
+ kres = krb5_parse_name( kcontext, user, &kme );
+ if( kres )
+ goto fail;
+ krb5_unparse_name( kcontext, kme, &pName );
+ if( cachename )
+ {
+ if (asprintf(&pCacheName, "%s%s", cachename, pName) < 0)
+ {
+ kres = KRB5_CC_NOMEM;
+ goto fail;
+ }
+ kres = krb5_cc_resolve( kcontext, pCacheName, &kcache );
+ if( kres )
+ {
+ kres = krb5_cc_resolve( kcontext, CCACHE_PREFIX_DEFAULT, &kcache );
+ if( kres == 0 )
+ pCacheName = strdup(CCACHE_PREFIX_DEFAULT);
+ }
+ }
+ else
+ {
+ kres = krb5_cc_default( kcontext, &kcache );
+ pCacheName = strdup( krb5_cc_get_name( kcontext, kcache ) );
+ }
+ if( kres )
+ {
+ krb5_free_context(kcontext);
+ goto return_error;
+ }
+ if( initialize )
+ krb5_cc_initialize( kcontext, kcache, kme );
+ if( kres == 0 && user && passwd )
+ {
+ long timeneeded = time(0L) +TKTTIMELEFT;
+ int have_credentials = 0;
+ krb5_cc_cursor cc_curs = NULL;
+ numCreds = 0;
+ if( (kres=krb5_cc_start_seq_get(kcontext, kcache, &cc_curs)) >= 0 )
+ {
+ while( (kres=krb5_cc_next_cred(kcontext, kcache, &cc_curs, &kcreds))== 0)
+ {
+ numCreds++;
+ if( krb5_principal_compare( kcontext, kme, kcreds.client ) )
+ {
+ if( kcreds.ticket_flags & TKT_FLG_INITIAL && kcreds.times.endtime>timeneeded )
+ have_credentials = 1;
+ }
+ krb5_free_cred_contents( kcontext, &kcreds );
+ if( have_credentials )
+ break;
+ }
+ krb5_cc_end_seq_get( kcontext, kcache, &cc_curs );
+ }
+ else
+ {
+ const char * errmsg = error_message(kres);
+ fprintf( stderr, "%s user init(%s): %s\n", "setpass", pName, errmsg );
+ }
+ if( kres != 0 || have_credentials == 0 )
+ {
+ krb5_get_init_creds_opt *options = NULL;
+ kres = krb5_get_init_creds_opt_alloc(kcontext, &options);
+ if ( kres == 0 )
+ {
+ get_init_creds_opt_init(options);
+/*
+** no valid credentials - get new ones
+*/
+ kres = krb5_get_init_creds_password( kcontext, &kcreds, kme, pPass,
+ NULL /*prompter*/,
+ NULL /*data*/,
+ 0 /*starttime*/,
+ 0 /*in_tkt_service*/,
+ options /*options*/ );
+ }
+ if( kres == 0 )
+ {
+ if( numCreds <= 0 )
+ kres = krb5_cc_initialize( kcontext, kcache, kme );
+ if( kres == 0 )
+ kres = krb5_cc_store_cred( kcontext, kcache, &kcreds );
+ if( kres == 0 )
+ have_credentials = 1;
+ }
+ krb5_get_init_creds_opt_free(kcontext, options);
+ }
+#ifdef NOTUSED
+ if( have_credentials )
+ {
+ int mstat;
+ kres = gss_krb5_ccache_name( &mstat, pCacheName, NULL );
+ if( getenv( ENV_DEBUG_LDAPKERB ) )
+ fprintf( stderr, "gss credentials cache set to %s(%d)\n", pCacheName, kres );
+ }
+#endif
+ krb5_cc_close( kcontext, kcache );
+ }
+fail:
+ if( kres )
+ {
+ const char * errmsg = error_message(kres);
+ fprintf( stderr, "%s user init(%s): %s\n", "setpass", pName, errmsg );
+ }
+ krb5_free_principal( kcontext, kme );
+ krb5_free_cred_contents( kcontext, &kcreds );
+ if( pName )
+ free( pName );
+ free(pPass);
+ krb5_free_context(kcontext);
+
+return_error:
+ if( kres )
+ {
+ if( pCacheName )
+ {
+ free(pCacheName);
+ pCacheName = NULL;
+ }
+ }
+ if( outError )
+ *outError = kres;
+ return pCacheName;
+}
+
+static int init_creds()
+{
+ char user[512];
+ char * password = NULL;
+ int result;
+
+ user[0] = 0;
+ result = -1;
+
+ for(;;)
+ {
+ while( user[0] == 0 )
+ {
+ int userlen;
+ printf( "Username: ");
+ fflush(stdout);
+ if( fgets( user, sizeof(user), stdin ) == NULL )
+ return -1;
+ userlen = strlen( user);
+ if( userlen < 2 )
+ continue;
+ user[userlen-1] = 0; /* get rid of the newline */
+ break;
+ }
+ {
+ kbrccache_t usercontext;
+ password = getpass( "Password: ");
+ if( ! password )
+ return -1;
+ result = 0;
+ usercontext = userinitcontext( user, NULL, password, NULL, 1, &result );
+ if( usercontext )
+ break;
+ }
+ }
+ return result;
+}
+
+int main( int argc, char ** argv )
+{
+ char * new_password;
+ char * new_password2;
+ krb5_context kcontext;
+ krb5_error_code kerr;
+ krb5_principal target_principal;
+
+
+ if( argc < 2 )
+ {
+ fprintf( stderr, "Usage: setpass user@REALM\n");
+ exit(1);
+ }
+
+/*
+** verify credentials -
+*/
+ if( verify_creds() )
+ init_creds();
+ if( verify_creds() )
+ {
+ fprintf( stderr, "No user credentials available\n");
+ exit(1);
+ }
+/*
+** check the principal name -
+*/
+ krb5_init_context(&kcontext);
+ kerr = krb5_parse_name( kcontext, argv[1], &target_principal );
+
+ {
+ char * pname = NULL;
+ kerr = krb5_unparse_name( kcontext, target_principal, &pname );
+ printf( "Changing password for %s:\n", pname);
+ fflush( stdout );
+ free( pname );
+ }
+/*
+** get the new password -
+*/
+ for (;;)
+ {
+ new_password = getpass("Enter new password: ");
+ new_password2 = getpass("Verify new password: ");
+ if( strcmp( new_password, new_password2 ) == 0)
+ break;
+ printf("Passwords do not match\n");
+ free( new_password );
+ free( new_password2 );
+ }
+/*
+** change the password -
+*/
+ {
+ int pw_result;
+ krb5_ccache ccache;
+ krb5_data pw_res_string, res_string;
+
+ kerr = krb5_cc_default( kcontext, &ccache );
+ if( kerr == 0 )
+ {
+ kerr = krb5_set_password_using_ccache(kcontext, ccache, new_password, target_principal,
+ &pw_result, &pw_res_string, &res_string );
+ if( kerr )
+ fprintf( stderr, "Failed: %s\n", error_message(kerr) );
+ else
+ {
+ if( pw_result )
+ {
+ fprintf( stderr, "Failed(%d)", pw_result );
+ if( pw_res_string.length > 0 )
+ fprintf( stderr, ": %s", pw_res_string.data);
+ if( res_string.length > 0 )
+ fprintf( stderr, " %s", res_string.data);
+ fprintf( stderr, "\n");
+ }
+ }
+ }
+ }
+ return(0);
+}
diff --git a/src/clients/ksu/Makefile.in b/src/clients/ksu/Makefile.in
new file mode 100644
index 000000000000..b2fcbf240186
--- /dev/null
+++ b/src/clients/ksu/Makefile.in
@@ -0,0 +1,35 @@
+mydir=clients$(S)ksu
+BUILDTOP=$(REL)..$(S)..
+DEFINES = -DGET_TGT_VIA_PASSWD -DPRINC_LOOK_AHEAD -DCMD_PATH='"/bin /local/bin"'
+
+KSU_LIBS=@KSU_LIBS@
+
+SRCS = \
+ $(srcdir)/krb_auth_su.c \
+ $(srcdir)/ccache.c \
+ $(srcdir)/authorization.c \
+ $(srcdir)/main.c \
+ $(srcdir)/heuristic.c \
+ $(srcdir)/xmalloc.c \
+ $(srcdir)/setenv.c
+OBJS = \
+ krb_auth_su.o \
+ ccache.o \
+ authorization.o \
+ main.o \
+ heuristic.o \
+ xmalloc.o @SETENVOBJ@
+
+all: ksu
+
+ksu: $(OBJS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS)
+
+clean:
+ $(RM) ksu
+
+install:
+ -for f in ksu; do \
+ $(INSTALL_SETUID) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/ksu/authorization.c b/src/clients/ksu/authorization.c
new file mode 100644
index 000000000000..90aafbd7595b
--- /dev/null
+++ b/src/clients/ksu/authorization.c
@@ -0,0 +1,727 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1994 by the University of Southern California
+ *
+ * EXPORT OF THIS SOFTWARE from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+ * this software and its documentation in source and binary forms is
+ * hereby granted, provided that any documentation or other materials
+ * related to such distribution or use acknowledge that the software
+ * was developed by the University of Southern California.
+ *
+ * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
+ * University of Southern California MAKES NO REPRESENTATIONS OR
+ * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, the University of Southern California MAKES NO
+ * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE. The University of Southern
+ * California shall not be held liable for any liability nor for any
+ * direct, indirect, or consequential damages with respect to any
+ * claim by the user or distributor of the ksu software.
+ *
+ * KSU was writen by: Ari Medvinsky, ari@isi.edu
+ */
+
+#include "ksu.h"
+
+static void auth_cleanup (FILE *, FILE *, char *);
+
+krb5_boolean fowner(fp, uid)
+ FILE *fp;
+ uid_t uid;
+{
+ struct stat sbuf;
+
+ /*
+ * For security reasons, file must be owned either by
+ * the user himself, or by root. Otherwise, don't grant access.
+ */
+ if (fstat(fileno(fp), &sbuf)) {
+ return(FALSE);
+ }
+
+ if ((sbuf.st_uid != uid) && sbuf.st_uid) {
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+/*
+ * Given a Kerberos principal "principal", and a local username "luser",
+ * determine whether user is authorized to login according to the
+ * authorization files ~luser/.k5login" and ~luser/.k5users. Returns TRUE
+ * if authorized, FALSE if not authorized.
+ *
+ */
+
+krb5_error_code krb5_authorization(context, principal, luser,
+ cmd, ok, out_fcmd)
+/* IN */
+ krb5_context context;
+ krb5_principal principal;
+ const char *luser;
+ char *cmd;
+ /* OUT */
+ krb5_boolean *ok;
+ char **out_fcmd;
+{
+ struct passwd *pwd;
+ char *princname;
+ int k5login_flag =0;
+ int k5users_flag =0;
+ krb5_boolean retbool =FALSE;
+ FILE * login_fp = 0, * users_fp = 0;
+ krb5_error_code retval = 0;
+ struct stat st_temp;
+
+ *ok =FALSE;
+
+ /* no account => no access */
+ if ((pwd = getpwnam(luser)) == NULL)
+ return 0;
+
+ retval = krb5_unparse_name(context, principal, &princname);
+ if (retval)
+ return retval;
+
+#ifdef DEBUG
+ printf("principal to be authorized %s\n", princname);
+ printf("login file: %s\n", k5login_path);
+ printf("users file: %s\n", k5users_path);
+#endif
+
+ k5login_flag = stat(k5login_path, &st_temp);
+ k5users_flag = stat(k5users_path, &st_temp);
+
+ /* k5login and k5users must be owned by target user or root */
+ if (!k5login_flag){
+ if ((login_fp = fopen(k5login_path, "r")) == NULL)
+ return 0;
+ if ( fowner(login_fp, pwd->pw_uid) == FALSE) {
+ fclose(login_fp);
+ return 0;
+ }
+ }
+
+ if (!k5users_flag){
+ if ((users_fp = fopen(k5users_path, "r")) == NULL) {
+ return 0;
+ }
+ if ( fowner(users_fp, pwd->pw_uid) == FALSE){
+ fclose(users_fp);
+ return 0;
+ }
+ }
+
+ if (auth_debug){
+ fprintf(stderr,
+ "In krb5_authorization: if auth files exist -> can access\n");
+ }
+
+#if 0
+ if (cmd){
+ if(k5users_flag){
+ return 0; /* if kusers does not exist -> done */
+ }else{
+ if(retval = k5users_lookup(users_fp,princname,
+ cmd,&retbool,out_fcmd)){
+ auth_cleanup(users_fp, login_fp, princname);
+ return retval;
+ }else{
+ *ok =retbool;
+ return retval;
+ }
+ }
+ }
+#endif
+
+ /* if either file exists,
+ first see if the principal is in the login in file,
+ if it's not there check the k5users file */
+
+ if (!k5login_flag){
+ if (auth_debug)
+ fprintf(stderr,
+ "In krb5_authorization: principal to be authorized %s\n",
+ princname);
+
+ retval = k5login_lookup(login_fp, princname, &retbool);
+ if (retval) {
+ auth_cleanup(users_fp, login_fp, princname);
+ return retval;
+ }
+ if (retbool) {
+ if (cmd)
+ *out_fcmd = xstrdup(cmd);
+ }
+ }
+
+ if ((!k5users_flag) && (retbool == FALSE) ){
+ retval = k5users_lookup (users_fp, princname,
+ cmd, &retbool, out_fcmd);
+ if(retval) {
+ auth_cleanup(users_fp, login_fp, princname);
+ return retval;
+ }
+ }
+
+ if (k5login_flag && k5users_flag){
+
+ char * kuser = (char *) xcalloc (strlen(princname), sizeof(char));
+ if (!(krb5_aname_to_localname(context, principal,
+ strlen(princname), kuser))
+ && (strcmp(kuser, luser) == 0)) {
+ retbool = TRUE;
+ }
+
+ free(kuser);
+ }
+
+ *ok =retbool;
+ auth_cleanup(users_fp, login_fp, princname);
+ return 0;
+}
+
+/***********************************************************
+k5login_lookup looks for princname in file fp. Spaces
+before the princaname (in the file ) are not ignored
+spaces after the princname are ignored. If there are
+any tokens after the principal name FALSE is returned.
+
+***********************************************************/
+
+krb5_error_code k5login_lookup (fp, princname, found)
+ FILE *fp;
+ char *princname;
+ krb5_boolean *found;
+{
+
+ krb5_error_code retval;
+ char * line;
+ char * fprinc;
+ char * lp;
+ krb5_boolean loc_found = FALSE;
+
+ retval = get_line(fp, &line);
+ if (retval)
+ return retval;
+
+ while (line){
+ fprinc = get_first_token (line, &lp);
+
+ if (fprinc && (!strcmp(princname, fprinc))){
+ if( get_next_token (&lp) ){
+ free (line);
+ break; /* nothing should follow princname*/
+ }
+ else{
+ loc_found = TRUE;
+ free (line);
+ break;
+ }
+ }
+
+ free (line);
+
+ retval = get_line(fp, &line);
+ if (retval)
+ return retval;
+ }
+
+
+ *found = loc_found;
+ return 0;
+
+}
+
+/***********************************************************
+k5users_lookup looks for princname in file fp. Spaces
+before the princaname (in the file ) are not ignored
+spaces after the princname are ignored.
+
+authorization alg:
+
+if princname is not found return false.
+
+if princname is found{
+ if cmd == NULL then the file entry after principal
+ name must be nothing or *
+
+ if cmd !=NULL then entry must be matched (* is ok)
+}
+
+
+***********************************************************/
+krb5_error_code k5users_lookup (fp, princname, cmd, found, out_fcmd)
+ FILE *fp;
+ char *princname;
+ char *cmd;
+ krb5_boolean *found;
+ char **out_fcmd;
+{
+ krb5_error_code retval;
+ char * line;
+ char * fprinc, *fcmd;
+ char * lp;
+ char * loc_fcmd = NULL;
+ krb5_boolean loc_found = FALSE;
+
+ retval = get_line(fp, &line);
+ if (retval)
+ return retval;
+
+ while (line){
+ fprinc = get_first_token (line, &lp);
+
+ if (fprinc && (!strcmp(princname, fprinc))){
+ fcmd = get_next_token (&lp);
+
+ if ((fcmd) && (!strcmp(fcmd, PERMIT_ALL_COMMANDS))){
+ if (get_next_token(&lp) == NULL){
+ loc_fcmd =cmd ? xstrdup(cmd): NULL;
+ loc_found = TRUE;
+ }
+ free (line);
+ break;
+ }
+
+ if (cmd == NULL){
+ if (fcmd == NULL)
+ loc_found = TRUE;
+ free (line);
+ break;
+
+ }else{
+ if (fcmd != NULL) {
+ char * temp_rfcmd, *err;
+ krb5_boolean match;
+ do {
+ if(match_commands(fcmd,cmd,&match,
+ &temp_rfcmd, &err)){
+ if (auth_debug){
+ fprintf(stderr,"%s",err);
+ }
+ loc_fcmd = err;
+ break;
+ }else{
+ if (match == TRUE){
+ loc_fcmd = temp_rfcmd;
+ loc_found = TRUE;
+ break;
+ }
+ }
+
+ }while ((fcmd = get_next_token( &lp)));
+ }
+ free (line);
+ break;
+ }
+ }
+
+ free (line);
+
+ retval = get_line(fp, &line);
+ if (retval) {
+ return retval;
+ }
+ }
+
+ *out_fcmd = loc_fcmd;
+ *found = loc_found;
+ return 0;
+
+}
+
+
+/***********************************************
+fcmd_resolve -
+takes a command specified .k5users file and
+resolves it into a full path name.
+
+************************************************/
+
+krb5_boolean fcmd_resolve(fcmd, out_fcmd, out_err)
+ char *fcmd;
+ char ***out_fcmd;
+ char **out_err;
+{
+ char * err;
+ char ** tmp_fcmd;
+ char * path_ptr, *path;
+ char * lp, * tc;
+ int i=0;
+
+ tmp_fcmd = (char **) xcalloc (MAX_CMD, sizeof(char *));
+
+ if (*fcmd == '/'){ /* must be full path */
+ tmp_fcmd[0] = xstrdup(fcmd);
+ tmp_fcmd[1] = NULL;
+ *out_fcmd = tmp_fcmd;
+ return TRUE;
+ }else{
+ /* must be either full path or just the cmd name */
+ if (strchr(fcmd, '/')){
+ asprintf(&err, _("Error: bad entry - %s in %s file, must be "
+ "either full path or just the cmd name\n"),
+ fcmd, KRB5_USERS_NAME);
+ *out_err = err;
+ return FALSE;
+ }
+
+#ifndef CMD_PATH
+ asprintf(&err, _("Error: bad entry - %s in %s file, since %s is just "
+ "the cmd name, CMD_PATH must be defined \n"),
+ fcmd, KRB5_USERS_NAME, fcmd);
+ *out_err = err;
+ return FALSE;
+#else
+
+ path = xstrdup (CMD_PATH);
+ path_ptr = path;
+
+ while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++;
+
+ tc = get_first_token (path_ptr, &lp);
+
+ if (! tc){
+ asprintf(&err, _("Error: bad entry - %s in %s file, CMD_PATH "
+ "contains no paths \n"), fcmd, KRB5_USERS_NAME);
+ *out_err = err;
+ return FALSE;
+ }
+
+ i=0;
+ do{
+ if (*tc != '/'){ /* must be full path */
+ asprintf(&err, _("Error: bad path %s in CMD_PATH for %s must "
+ "start with '/' \n"), tc, KRB5_USERS_NAME );
+ *out_err = err;
+ return FALSE;
+ }
+
+ tmp_fcmd[i] = xasprintf("%s/%s", tc, fcmd);
+
+ i++;
+
+ } while((tc = get_next_token (&lp)));
+
+ tmp_fcmd[i] = NULL;
+ *out_fcmd = tmp_fcmd;
+ return TRUE;
+
+#endif /* CMD_PATH */
+ }
+}
+
+/********************************************
+cmd_single - checks if cmd consists of a path
+ or a single token
+
+********************************************/
+
+krb5_boolean cmd_single(cmd)
+ char * cmd;
+{
+
+ if ( ( strrchr( cmd, '/')) == NULL){
+ return TRUE;
+ }else{
+ return FALSE;
+ }
+}
+
+/********************************************
+cmd_arr_cmp_postfix - compares a command with the postfix
+ of fcmd
+********************************************/
+
+int cmd_arr_cmp_postfix(fcmd_arr, cmd)
+ char **fcmd_arr;
+ char *cmd;
+{
+ char * temp_fcmd;
+ char *ptr;
+ int result =1;
+ int i = 0;
+
+ while(fcmd_arr[i]){
+ if ( (ptr = strrchr( fcmd_arr[i], '/')) == NULL){
+ temp_fcmd = fcmd_arr[i];
+ }else {
+ temp_fcmd = ptr + 1;
+ }
+
+ result = strcmp (temp_fcmd, cmd);
+ if (result == 0){
+ break;
+ }
+ i++;
+ }
+
+ return result;
+
+
+}
+
+/**********************************************
+cmd_arr_cmp - checks if cmd matches any
+ of the fcmd entries.
+
+**********************************************/
+
+int cmd_arr_cmp (fcmd_arr, cmd)
+ char **fcmd_arr;
+ char *cmd;
+{
+ int result =1;
+ int i = 0;
+
+ while(fcmd_arr[i]){
+ result = strcmp (fcmd_arr[i], cmd);
+ if (result == 0){
+ break;
+ }
+ i++;
+ }
+ return result;
+}
+
+
+krb5_boolean find_first_cmd_that_exists(fcmd_arr, cmd_out, err_out)
+ char **fcmd_arr;
+ char **cmd_out;
+ char **err_out;
+{
+ struct stat st_temp;
+ int i = 0;
+ krb5_boolean retbool= FALSE;
+ int j =0;
+ struct k5buf buf;
+
+ while(fcmd_arr[i]){
+ if (!stat (fcmd_arr[i], &st_temp )){
+ *cmd_out = xstrdup(fcmd_arr[i]);
+ retbool = TRUE;
+ break;
+ }
+ i++;
+ }
+
+ if (retbool == FALSE ){
+ k5_buf_init_dynamic(&buf);
+ k5_buf_add(&buf, _("Error: not found -> "));
+ for(j= 0; j < i; j ++)
+ k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]);
+ k5_buf_add(&buf, "\n");
+ if (k5_buf_status(&buf) != 0) {
+ perror(prog_name);
+ exit(1);
+ }
+ *err_out = buf.data;
+ }
+
+
+ return retbool;
+}
+
+/***************************************************************
+returns 1 if there is an error, 0 if no error.
+
+***************************************************************/
+
+int match_commands (fcmd, cmd, match, cmd_out, err_out)
+ char *fcmd;
+ char *cmd;
+ krb5_boolean *match;
+ char **cmd_out;
+ char **err_out;
+{
+ char ** fcmd_arr;
+ char * err;
+ char * cmd_temp;
+
+ if(fcmd_resolve(fcmd, &fcmd_arr, &err )== FALSE ){
+ *err_out = err;
+ return 1;
+ }
+
+ if (cmd_single( cmd ) == TRUE){
+ if (!cmd_arr_cmp_postfix(fcmd_arr, cmd)){ /* found */
+
+ if(find_first_cmd_that_exists( fcmd_arr,&cmd_temp,&err)== TRUE){
+ *match = TRUE;
+ *cmd_out = cmd_temp;
+ return 0;
+ }else{
+ *err_out = err;
+ return 1;
+ }
+ }else{
+ *match = FALSE;
+ return 0;
+ }
+ }else{
+ if (!cmd_arr_cmp(fcmd_arr, cmd)){ /* found */
+ *match = TRUE;
+ *cmd_out = xstrdup(cmd);
+ return 0;
+ } else{
+ *match = FALSE;
+ return 0;
+ }
+ }
+
+}
+
+/*********************************************************
+ get_line - returns a line of any length. out_line
+ is set to null if eof.
+*********************************************************/
+
+krb5_error_code get_line (fp, out_line)
+/* IN */
+ FILE *fp;
+ /* OUT */
+ char **out_line;
+{
+ char * line, *r, *newline , *line_ptr;
+ int chunk_count = 1;
+
+ line = (char *) xcalloc (BUFSIZ, sizeof (char ));
+ line_ptr = line;
+ line[0] = '\0';
+
+ while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){
+ newline = strchr(line_ptr, '\n');
+ if (newline) {
+ *newline = '\0';
+ break;
+ }
+ else {
+ chunk_count ++;
+ if(!( line = (char *) realloc( line,
+ chunk_count * sizeof(char) * BUFSIZ))){
+ return ENOMEM;
+ }
+
+ line_ptr = line + (BUFSIZ -1) *( chunk_count -1) ;
+ }
+ }
+
+ if ((r == NULL) && (strlen(line) == 0)) {
+ *out_line = NULL;
+ }
+ else{
+ *out_line = line;
+ }
+
+ return 0;
+}
+
+/*******************************************************
+get_first_token -
+Expects a '\0' terminated input line .
+If there are any spaces before the first token, they
+will be returned as part of the first token.
+
+Note: this routine reuses the space pointed to by line
+******************************************************/
+
+char * get_first_token (line, lnext)
+ char *line;
+ char **lnext;
+{
+
+ char * lptr, * out_ptr;
+
+
+ out_ptr = line;
+ lptr = line;
+
+ while (( *lptr == ' ') || (*lptr == '\t')) lptr ++;
+
+ if (strlen(lptr) == 0) return NULL;
+
+ while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++;
+
+ if (*lptr == '\0'){
+ *lnext = lptr;
+ } else{
+ *lptr = '\0';
+ *lnext = lptr + 1;
+ }
+
+ return out_ptr;
+}
+/**********************************************************
+get_next_token -
+returns the next token pointed to by *lnext.
+returns NULL if there is no more tokens.
+Note: that this function modifies the stream
+ pointed to by *lnext and does not allocate
+ space for the returned tocken. It also advances
+ lnext to the next tocken.
+**********************************************************/
+
+char * get_next_token (lnext)
+ char **lnext;
+{
+ char * lptr, * out_ptr;
+
+
+ lptr = *lnext;
+
+ while (( *lptr == ' ') || (*lptr == '\t')) lptr ++;
+
+ if (strlen(lptr) == 0) return NULL;
+
+ out_ptr = lptr;
+
+ while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++;
+
+ if (*lptr == '\0'){
+ *lnext = lptr;
+ } else{
+ *lptr = '\0';
+ *lnext = lptr + 1;
+ }
+
+ return out_ptr;
+}
+
+static void auth_cleanup(users_fp, login_fp, princname)
+ FILE *users_fp;
+ FILE *login_fp;
+ char *princname;
+{
+
+ free (princname);
+ if (users_fp)
+ fclose(users_fp);
+ if (login_fp)
+ fclose(login_fp);
+}
+
+void init_auth_names(pw_dir)
+ char *pw_dir;
+{
+ const char *sep;
+ int r1, r2;
+
+ sep = ((strlen(pw_dir) == 1) && (*pw_dir == '/')) ? "" : "/";
+ r1 = snprintf(k5login_path, sizeof(k5login_path), "%s%s%s",
+ pw_dir, sep, KRB5_LOGIN_NAME);
+ r2 = snprintf(k5users_path, sizeof(k5users_path), "%s%s%s",
+ pw_dir, sep, KRB5_USERS_NAME);
+ if (SNPRINTF_OVERFLOW(r1, sizeof(k5login_path)) ||
+ SNPRINTF_OVERFLOW(r2, sizeof(k5users_path))) {
+ fprintf(stderr, _("home directory name `%s' too long, can't search "
+ "for .k5login\n"), pw_dir);
+ exit (1);
+ }
+}
diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c
new file mode 100644
index 000000000000..a0736f2daad7
--- /dev/null
+++ b/src/clients/ksu/ccache.c
@@ -0,0 +1,742 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1994 by the University of Southern California
+ *
+ * EXPORT OF THIS SOFTWARE from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+ * this software and its documentation in source and binary forms is
+ * hereby granted, provided that any documentation or other materials
+ * related to such distribution or use acknowledge that the software
+ * was developed by the University of Southern California.
+ *
+ * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
+ * University of Southern California MAKES NO REPRESENTATIONS OR
+ * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, the University of Southern California MAKES NO
+ * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE. The University of Southern
+ * California shall not be held liable for any liability nor for any
+ * direct, indirect, or consequential damages with respect to any
+ * claim by the user or distributor of the ksu software.
+ *
+ * KSU was writen by: Ari Medvinsky, ari@isi.edu
+ */
+
+#include "ksu.h"
+#include "k5-base64.h"
+#include "adm_proto.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/******************************************************************
+krb5_cache_copy
+
+gets rid of any expired tickets in the secondary cache,
+copies the default cache into the secondary cache,
+
+************************************************************************/
+
+void show_credential();
+
+/* modifies only the cc_other, the algorithm may look a bit funny,
+ but I had to do it this way, since remove function did not come
+ with k5 beta 3 release.
+*/
+
+krb5_error_code krb5_ccache_copy(context, cc_def, target_principal, cc_target,
+ restrict_creds, primary_principal, stored)
+/* IN */
+ krb5_context context;
+ krb5_ccache cc_def;
+ krb5_principal target_principal;
+ krb5_ccache cc_target;
+ krb5_boolean restrict_creds;
+ krb5_principal primary_principal;
+ /* OUT */
+ krb5_boolean *stored;
+{
+ int i=0;
+ krb5_error_code retval=0;
+ krb5_creds ** cc_def_creds_arr = NULL;
+ krb5_creds ** cc_other_creds_arr = NULL;
+
+ if (ks_ccache_is_initialized(context, cc_def)) {
+ if((retval = krb5_get_nonexp_tkts(context,cc_def,&cc_def_creds_arr))){
+ return retval;
+ }
+ }
+
+ retval = krb5_cc_initialize(context, cc_target, target_principal);
+ if (retval)
+ return retval;
+
+ if (restrict_creds) {
+ retval = krb5_store_some_creds(context, cc_target, cc_def_creds_arr,
+ cc_other_creds_arr, primary_principal,
+ stored);
+ } else {
+ *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr,
+ primary_principal);
+ retval = krb5_store_all_creds(context, cc_target, cc_def_creds_arr,
+ cc_other_creds_arr);
+ }
+
+ if (cc_def_creds_arr){
+ while (cc_def_creds_arr[i]){
+ krb5_free_creds(context, cc_def_creds_arr[i]);
+ i++;
+ }
+ }
+
+ i=0;
+
+ if(cc_other_creds_arr){
+ while (cc_other_creds_arr[i]){
+ krb5_free_creds(context, cc_other_creds_arr[i]);
+ i++;
+ }
+ }
+
+ return retval;
+}
+
+
+krb5_error_code krb5_store_all_creds(context, cc, creds_def, creds_other)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_creds **creds_def;
+ krb5_creds **creds_other;
+{
+
+ int i = 0;
+ krb5_error_code retval = 0;
+ krb5_creds ** temp_creds= NULL;
+
+
+ if ((creds_def == NULL) && (creds_other == NULL))
+ return 0;
+
+ if ((creds_def == NULL) && (creds_other != NULL))
+ temp_creds = creds_other;
+
+ if ((creds_def != NULL) && (creds_other == NULL))
+ temp_creds = creds_def;
+
+
+ if (temp_creds){
+ while(temp_creds[i]){
+ if ((retval= krb5_cc_store_cred(context, cc,
+ temp_creds[i]))){
+ return retval;
+ }
+ i++;
+ }
+ }
+ else { /* both arrays have elements in them */
+
+ return KRB5KRB_ERR_GENERIC;
+
+/************ while(creds_other[i]){
+ cmp = FALSE;
+ j = 0;
+ while(creds_def[j]){
+ cmp = compare_creds(creds_other[i],creds_def[j]);
+
+ if( cmp == TRUE) break;
+
+ j++;
+ }
+ if (cmp == FALSE){
+ if (retval= krb5_cc_store_cred(context, cc,
+ creds_other[i])){
+ return retval;
+ }
+ }
+ i ++;
+ }
+
+ i=0;
+ while(creds_def[i]){
+ if (retval= krb5_cc_store_cred(context, cc,
+ creds_def[i])){
+ return retval;
+ }
+ i++;
+ }
+
+**************/
+ }
+ return 0;
+}
+
+krb5_boolean compare_creds(context, cred1, cred2)
+ krb5_context context;
+ krb5_creds *cred1;
+ krb5_creds *cred2;
+{
+ krb5_boolean retval;
+
+ retval = krb5_principal_compare (context, cred1->client, cred2->client);
+
+ if (retval == TRUE)
+ retval = krb5_principal_compare (context, cred1->server, cred2->server);
+
+ return retval;
+}
+
+
+
+
+krb5_error_code krb5_get_nonexp_tkts(context, cc, creds_array)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_creds ***creds_array;
+{
+
+ krb5_creds creds, temp_tktq, temp_tkt;
+ krb5_creds **temp_creds;
+ krb5_error_code retval=0;
+ krb5_cc_cursor cur;
+ int count = 0;
+ int chunk_count = 1;
+
+ if ( ! ( temp_creds = (krb5_creds **) malloc( CHUNK * sizeof(krb5_creds *)))){
+ return ENOMEM;
+ }
+
+
+ memset(&temp_tktq, 0, sizeof(temp_tktq));
+ memset(&temp_tkt, 0, sizeof(temp_tkt));
+ memset(&creds, 0, sizeof(creds));
+
+ /* initialize the cursor */
+ if ((retval = krb5_cc_start_seq_get(context, cc, &cur))) {
+ return retval;
+ }
+
+ while (!(retval = krb5_cc_next_cred(context, cc, &cur, &creds))){
+
+ if (!krb5_is_config_principal(context, creds.server) &&
+ (retval = krb5_check_exp(context, creds.times))){
+ if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){
+ return retval;
+ }
+ if (auth_debug){
+ fprintf(stderr,"krb5_ccache_copy: CREDS EXPIRED:\n");
+ fputs(" Valid starting Expires Service principal\n",stdout);
+ show_credential(context, &creds, cc);
+ fprintf(stderr,"\n");
+ }
+ }
+ else { /* these credentials didn't expire */
+
+ if ((retval = krb5_copy_creds(context, &creds,
+ &temp_creds[count]))){
+ return retval;
+ }
+ count ++;
+
+ if (count == (chunk_count * CHUNK -1)){
+ chunk_count ++;
+ if (!(temp_creds = (krb5_creds **) realloc(temp_creds,
+ chunk_count * CHUNK * sizeof(krb5_creds *)))){
+ return ENOMEM;
+ }
+ }
+ }
+
+ }
+
+ temp_creds[count] = NULL;
+ *creds_array = temp_creds;
+
+ if (retval == KRB5_CC_END) {
+ retval = krb5_cc_end_seq_get(context, cc, &cur);
+ }
+
+ return retval;
+
+}
+
+
+krb5_error_code krb5_check_exp(context, tkt_time)
+ krb5_context context;
+ krb5_ticket_times tkt_time;
+{
+ krb5_error_code retval =0;
+ krb5_timestamp currenttime;
+
+ if ((retval = krb5_timeofday (context, &currenttime))){
+ return retval;
+ }
+ if (auth_debug){
+ fprintf(stderr,"krb5_check_exp: the krb5_clockskew is %d \n",
+ context->clockskew);
+
+ fprintf(stderr,"krb5_check_exp: currenttime - endtime %d \n",
+ (currenttime - tkt_time.endtime ));
+
+ }
+
+ if (currenttime - tkt_time.endtime > context->clockskew){
+ retval = KRB5KRB_AP_ERR_TKT_EXPIRED ;
+ return retval;
+ }
+
+ return 0;
+}
+
+
+char *flags_string(cred)
+ krb5_creds *cred;
+{
+ static char buf[32];
+ int i = 0;
+
+ if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
+ buf[i++] = 'F';
+ if (cred->ticket_flags & TKT_FLG_FORWARDED)
+ buf[i++] = 'f';
+ if (cred->ticket_flags & TKT_FLG_PROXIABLE)
+ buf[i++] = 'P';
+ if (cred->ticket_flags & TKT_FLG_PROXY)
+ buf[i++] = 'p';
+ if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
+ buf[i++] = 'D';
+ if (cred->ticket_flags & TKT_FLG_POSTDATED)
+ buf[i++] = 'd';
+ if (cred->ticket_flags & TKT_FLG_INVALID)
+ buf[i++] = 'i';
+ if (cred->ticket_flags & TKT_FLG_RENEWABLE)
+ buf[i++] = 'R';
+ if (cred->ticket_flags & TKT_FLG_INITIAL)
+ buf[i++] = 'I';
+ if (cred->ticket_flags & TKT_FLG_HW_AUTH)
+ buf[i++] = 'H';
+ if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
+ buf[i++] = 'A';
+ buf[i] = '\0';
+ return(buf);
+}
+
+void printtime(tv)
+ time_t tv;
+{
+ char fmtbuf[18];
+ char fill;
+ krb5_timestamp tstamp;
+
+ /* XXXX ASSUMES sizeof(krb5_timestamp) >= sizeof(time_t) */
+ (void) localtime((time_t *)&tv);
+ tstamp = tv;
+ fill = ' ';
+ if (!krb5_timestamp_to_sfstring(tstamp,
+ fmtbuf,
+ sizeof(fmtbuf),
+ &fill))
+ printf("%s", fmtbuf);
+}
+
+
+krb5_error_code
+krb5_get_login_princ(luser, princ_list)
+ const char *luser;
+ char ***princ_list;
+{
+ struct stat sbuf;
+ struct passwd *pwd;
+ char pbuf[MAXPATHLEN];
+ FILE *fp;
+ char * linebuf;
+ char *newline;
+ int gobble, result;
+ char ** buf_out;
+ struct stat st_temp;
+ int count = 0, chunk_count = 1;
+
+ /* no account => no access */
+
+ if ((pwd = getpwnam(luser)) == NULL) {
+ return 0;
+ }
+ result = snprintf(pbuf, sizeof(pbuf), "%s/.k5login", pwd->pw_dir);
+ if (SNPRINTF_OVERFLOW(result, sizeof(pbuf))) {
+ fprintf(stderr, _("home directory path for %s too long\n"), luser);
+ exit (1);
+ }
+
+ if (stat(pbuf, &st_temp)) { /* not accessible */
+ return 0;
+ }
+
+
+ /* open ~/.k5login */
+ if ((fp = fopen(pbuf, "r")) == NULL) {
+ return 0;
+ }
+ /*
+ * For security reasons, the .k5login file must be owned either by
+ * the user himself, or by root. Otherwise, don't grant access.
+ */
+ if (fstat(fileno(fp), &sbuf)) {
+ fclose(fp);
+ return 0;
+ }
+ if ((sbuf.st_uid != pwd->pw_uid) && sbuf.st_uid) {
+ fclose(fp);
+ return 0;
+ }
+
+ /* check each line */
+
+
+ if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return ENOMEM;
+
+ if (!(buf_out = (char **) malloc( CHUNK * sizeof(char *)))) return ENOMEM;
+
+ while ( fgets(linebuf, BUFSIZ, fp) != NULL) {
+ /* null-terminate the input string */
+ linebuf[BUFSIZ-1] = '\0';
+ newline = NULL;
+ /* nuke the newline if it exists */
+ if ((newline = strchr(linebuf, '\n')))
+ *newline = '\0';
+
+ buf_out[count] = linebuf;
+ count ++;
+
+ if (count == (chunk_count * CHUNK -1)){
+ chunk_count ++;
+ if (!(buf_out = (char **) realloc(buf_out,
+ chunk_count * CHUNK * sizeof(char *)))){
+ return ENOMEM;
+ }
+ }
+
+ /* clean up the rest of the line if necessary */
+ if (!newline)
+ while (((gobble = getc(fp)) != EOF) && gobble != '\n');
+
+ if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return ENOMEM;
+ }
+
+ buf_out[count] = NULL;
+ *princ_list = buf_out;
+ fclose(fp);
+ return 0;
+}
+
+
+
+void
+show_credential(context, cred, cc)
+ krb5_context context;
+ krb5_creds *cred;
+ krb5_ccache cc;
+{
+ krb5_error_code retval;
+ char *name, *sname, *flags;
+ int first = 1;
+ krb5_principal princ;
+ char * defname;
+ int show_flags =1;
+
+ retval = krb5_unparse_name(context, cred->client, &name);
+ if (retval) {
+ com_err(prog_name, retval, _("while unparsing client name"));
+ return;
+ }
+ retval = krb5_unparse_name(context, cred->server, &sname);
+ if (retval) {
+ com_err(prog_name, retval, _("while unparsing server name"));
+ free(name);
+ return;
+ }
+
+ if ((retval = krb5_cc_get_principal(context, cc, &princ))) {
+ com_err(prog_name, retval, _("while retrieving principal name"));
+ return;
+ }
+ if ((retval = krb5_unparse_name(context, princ, &defname))) {
+ com_err(prog_name, retval, _("while unparsing principal name"));
+ return;
+ }
+
+ if (!cred->times.starttime)
+ cred->times.starttime = cred->times.authtime;
+
+ printtime(cred->times.starttime);
+ putchar(' '); putchar(' ');
+ printtime(cred->times.endtime);
+ putchar(' '); putchar(' ');
+
+ printf("%s\n", sname);
+
+ if (strcmp(name, defname)) {
+ printf(_("\tfor client %s"), name);
+ first = 0;
+ }
+
+ if (cred->times.renew_till) {
+ if (first)
+ fputs("\t",stdout);
+ else
+ fputs(", ",stdout);
+ fputs(_("renew until "), stdout);
+ printtime(cred->times.renew_till);
+ }
+ if (show_flags) {
+ flags = flags_string(cred);
+ if (flags && *flags) {
+ if (first)
+ fputs("\t",stdout);
+ else
+ fputs(", ",stdout);
+ printf(_("Flags: %s"), flags);
+ first = 0;
+ }
+ }
+ putchar('\n');
+ free(name);
+ free(sname);
+}
+
+/* Create a random string suitable for a filename extension. */
+krb5_error_code
+gen_sym(krb5_context context, char **sym_out)
+{
+ krb5_error_code retval;
+ char bytes[6], *p, *sym;
+ krb5_data data = make_data(bytes, sizeof(bytes));
+
+ *sym_out = NULL;
+ retval = krb5_c_random_make_octets(context, &data);
+ if (retval)
+ return retval;
+ sym = k5_base64_encode(data.data, data.length);
+ if (sym == NULL)
+ return ENOMEM;
+ /* Tweak the output alphabet just a bit. */
+ while ((p = strchr(sym, '/')) != NULL)
+ *p = '_';
+ while ((p = strchr(sym, '+')) != NULL)
+ *p = '-';
+ *sym_out = sym;
+ return 0;
+}
+
+krb5_error_code krb5_ccache_overwrite(context, ccs, cct, primary_principal)
+ krb5_context context;
+ krb5_ccache ccs;
+ krb5_ccache cct;
+ krb5_principal primary_principal;
+{
+ krb5_error_code retval=0;
+ krb5_principal temp_principal;
+ krb5_creds ** ccs_creds_arr = NULL;
+ int i=0;
+
+ if (ks_ccache_is_initialized(context, ccs)) {
+ if ((retval = krb5_get_nonexp_tkts(context, ccs, &ccs_creds_arr))){
+ return retval;
+ }
+ }
+
+ if (ks_ccache_is_initialized(context, cct)) {
+ if ((retval = krb5_cc_get_principal(context, cct, &temp_principal))){
+ return retval;
+ }
+ }else{
+ temp_principal = primary_principal;
+ }
+
+ if ((retval = krb5_cc_initialize(context, cct, temp_principal))){
+ return retval;
+ }
+
+ retval = krb5_store_all_creds(context, cct, ccs_creds_arr, NULL);
+
+ if (ccs_creds_arr){
+ while (ccs_creds_arr[i]){
+ krb5_free_creds(context, ccs_creds_arr[i]);
+ i++;
+ }
+ }
+
+ return retval;
+}
+
+krb5_error_code krb5_store_some_creds(context, cc, creds_def, creds_other, prst,
+ stored)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_creds **creds_def;
+ krb5_creds **creds_other;
+ krb5_principal prst;
+ krb5_boolean *stored;
+{
+
+ int i = 0;
+ krb5_error_code retval = 0;
+ krb5_creds ** temp_creds= NULL;
+ krb5_boolean temp_stored = FALSE;
+
+
+ if ((creds_def == NULL) && (creds_other == NULL))
+ return 0;
+
+ if ((creds_def == NULL) && (creds_other != NULL))
+ temp_creds = creds_other;
+
+ if ((creds_def != NULL) && (creds_other == NULL))
+ temp_creds = creds_def;
+
+
+ if (temp_creds){
+ while(temp_creds[i]){
+ if (krb5_principal_compare(context,
+ temp_creds[i]->client,
+ prst)== TRUE) {
+
+ if ((retval = krb5_cc_store_cred(context,
+ cc,temp_creds[i]))){
+ return retval;
+ }
+ temp_stored = TRUE;
+ }
+
+ i++;
+ }
+ }
+ else { /* both arrays have elements in them */
+ return KRB5KRB_ERR_GENERIC;
+ }
+
+ *stored = temp_stored;
+ return 0;
+}
+
+krb5_error_code krb5_ccache_filter (context, cc, prst)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_principal prst;
+{
+
+ int i=0;
+ krb5_error_code retval=0;
+ krb5_principal temp_principal;
+ krb5_creds ** cc_creds_arr = NULL;
+ const char * cc_name;
+ krb5_boolean stored;
+
+ cc_name = krb5_cc_get_name(context, cc);
+
+ if (ks_ccache_is_initialized(context, cc)) {
+ if (auth_debug) {
+ fprintf(stderr,"putting cache %s through a filter for -z option\n", cc_name);
+ }
+
+ if ((retval = krb5_get_nonexp_tkts(context, cc, &cc_creds_arr))){
+ return retval;
+ }
+
+ if ((retval = krb5_cc_get_principal(context, cc, &temp_principal))){
+ return retval;
+ }
+
+ if ((retval = krb5_cc_initialize(context, cc, temp_principal))){
+ return retval;
+ }
+
+ if ((retval = krb5_store_some_creds(context, cc, cc_creds_arr,
+ NULL, prst, &stored))){
+ return retval;
+ }
+
+ if (cc_creds_arr){
+ while (cc_creds_arr[i]){
+ krb5_free_creds(context, cc_creds_arr[i]);
+ i++;
+ }
+ }
+ }
+ return 0;
+}
+
+krb5_boolean krb5_find_princ_in_cred_list (context, creds_list, princ)
+ krb5_context context;
+ krb5_creds **creds_list;
+ krb5_principal princ;
+{
+
+ int i = 0;
+ krb5_boolean temp_stored = FALSE;
+
+ if (creds_list){
+ while(creds_list[i]){
+ if (krb5_principal_compare(context,
+ creds_list[i]->client,
+ princ)== TRUE){
+ temp_stored = TRUE;
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ return temp_stored;
+}
+
+krb5_error_code krb5_find_princ_in_cache (context, cc, princ, found)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_principal princ;
+ krb5_boolean *found;
+{
+ krb5_error_code retval;
+ krb5_creds ** creds_list = NULL;
+
+ if (ks_ccache_is_initialized(context, cc)) {
+ if ((retval = krb5_get_nonexp_tkts(context, cc, &creds_list))){
+ return retval;
+ }
+ }
+
+ *found = krb5_find_princ_in_cred_list(context, creds_list, princ);
+ return 0;
+}
+
+krb5_boolean
+ks_ccache_name_is_initialized(krb5_context context, const char *cctag)
+{
+ krb5_boolean result;
+ krb5_ccache cc;
+
+ if (krb5_cc_resolve(context, cctag, &cc) != 0)
+ return FALSE;
+ result = ks_ccache_is_initialized(context, cc);
+ krb5_cc_close(context, cc);
+
+ return result;
+}
+
+krb5_boolean
+ks_ccache_is_initialized(krb5_context context, krb5_ccache cc)
+{
+ krb5_principal princ;
+ krb5_error_code retval;
+
+ if (cc == NULL)
+ return FALSE;
+
+ retval = krb5_cc_get_principal(context, cc, &princ);
+ if (retval == 0)
+ krb5_free_principal(context, princ);
+
+ return retval == 0;
+}
diff --git a/src/clients/ksu/deps b/src/clients/ksu/deps
new file mode 100644
index 000000000000..a3a07b941f4f
--- /dev/null
+++ b/src/clients/ksu/deps
@@ -0,0 +1,72 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)krb_auth_su.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/k5-util.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ krb_auth_su.c ksu.h
+$(OUTPRE)ccache.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h \
+ $(top_srcdir)/include/k5-base64.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/k5-util.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ ccache.c ksu.h
+$(OUTPRE)authorization.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/k5-util.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ authorization.c ksu.h
+$(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/k5-util.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h ksu.h main.c
+$(OUTPRE)heuristic.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/k5-util.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ heuristic.c ksu.h
+$(OUTPRE)xmalloc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/k5-util.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ ksu.h xmalloc.c
+$(OUTPRE)setenv.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ setenv.c
diff --git a/src/clients/ksu/heuristic.c b/src/clients/ksu/heuristic.c
new file mode 100644
index 000000000000..0d055e471c54
--- /dev/null
+++ b/src/clients/ksu/heuristic.c
@@ -0,0 +1,750 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1994 by the University of Southern California
+ *
+ * EXPORT OF THIS SOFTWARE from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+ * this software and its documentation in source and binary forms is
+ * hereby granted, provided that any documentation or other materials
+ * related to such distribution or use acknowledge that the software
+ * was developed by the University of Southern California.
+ *
+ * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
+ * University of Southern California MAKES NO REPRESENTATIONS OR
+ * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, the University of Southern California MAKES NO
+ * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE. The University of Southern
+ * California shall not be held liable for any liability nor for any
+ * direct, indirect, or consequential damages with respect to any
+ * claim by the user or distributor of the ksu software.
+ *
+ * KSU was writen by: Ari Medvinsky, ari@isi.edu
+ */
+
+#include "ksu.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+/*******************************************************************
+get_all_princ_from_file - retrieves all principal names
+ from file pointed to by fp.
+
+*******************************************************************/
+static void close_time (int, FILE *, int, FILE *);
+static krb5_boolean find_str_in_list (char **, char *);
+
+krb5_error_code get_all_princ_from_file (fp, plist)
+ FILE *fp;
+ char ***plist;
+{
+
+ krb5_error_code retval;
+ char * line, * fprinc, * lp, ** temp_list = NULL;
+ int count = 0, chunk_count = 1;
+
+ if (!(temp_list = (char **) malloc( CHUNK * sizeof(char *))))
+ return ENOMEM;
+
+ retval = get_line(fp, &line);
+ if (retval)
+ return retval;
+
+ while (line){
+ fprinc = get_first_token (line, &lp);
+
+ if (fprinc ){
+ temp_list[count] = xstrdup(fprinc);
+ count ++;
+ }
+
+ if(count == (chunk_count * CHUNK -1)){
+ chunk_count ++;
+ if (!(temp_list = (char **) realloc(temp_list,
+ chunk_count * CHUNK * sizeof(char *)))){
+ return ENOMEM;
+ }
+ }
+
+
+ free (line);
+ retval = get_line(fp, &line);
+ if (retval)
+ return retval;
+ }
+
+ temp_list[count] = NULL;
+
+ *plist = temp_list;
+ return 0;
+}
+
+/*************************************************************
+list_union - combines list1 and list2 into combined_list.
+ the space for list1 and list2 is either freed
+ or used by combined_list.
+**************************************************************/
+
+krb5_error_code list_union(list1, list2, combined_list)
+ char **list1;
+ char **list2;
+ char ***combined_list;
+{
+
+ unsigned int c1 =0, c2 = 0, i=0, j=0;
+ char ** tlist;
+
+ if (! list1){
+ *combined_list = list2;
+ return 0;
+ }
+
+ if (! list2){
+ *combined_list = list1;
+ return 0;
+ }
+
+ while (list1[c1]) c1++;
+ while (list2[c2]) c2++;
+
+ if (!(tlist = (char **) calloc( c1 + c2 + 1, sizeof ( char *))))
+ return ENOMEM;
+
+ i = 0;
+ while(list1[i]) {
+ tlist[i] = list1[i];
+ i++;
+ }
+ j = 0;
+ while(list2[j]){
+ if(find_str_in_list(list1, list2[j])==FALSE){
+ tlist[i] = list2[j];
+ i++;
+ }
+ j++;
+ }
+
+ free (list1);
+ free (list2);
+
+ tlist[i]= NULL;
+
+ *combined_list = tlist;
+ return 0;
+}
+
+krb5_error_code
+filter(fp, cmd, k5users_list, k5users_filt_list)
+ FILE *fp;
+ char *cmd;
+ char **k5users_list;
+ char ***k5users_filt_list;
+{
+
+ krb5_error_code retval =0;
+ krb5_boolean found = FALSE;
+ char * out_cmd = NULL;
+ unsigned int i=0, j=0, found_count = 0, k=0;
+ char ** temp_filt_list;
+
+ *k5users_filt_list = NULL;
+
+ if (! k5users_list){
+ return 0;
+ }
+
+ while(k5users_list[i]){
+
+ retval= k5users_lookup(fp, k5users_list[i], cmd, &found, &out_cmd);
+ if (retval)
+ return retval;
+
+ if (found == FALSE){
+ free (k5users_list[i]);
+ k5users_list[i] = NULL;
+ if (out_cmd) gb_err = out_cmd;
+ } else
+ found_count ++;
+
+ i++;
+ }
+
+ if (! (temp_filt_list = (char **) calloc(found_count +1, sizeof (char*))))
+ return ENOMEM;
+
+ for(j= 0, k=0; j < i; j++ ) {
+ if (k5users_list[j]){
+ temp_filt_list[k] = k5users_list[j];
+ k++;
+ }
+ }
+
+ temp_filt_list[k] = NULL;
+
+ free (k5users_list);
+
+ *k5users_filt_list = temp_filt_list;
+ return 0;
+}
+
+krb5_error_code
+get_authorized_princ_names(luser, cmd, princ_list)
+ const char *luser;
+ char *cmd;
+ char ***princ_list;
+{
+
+ struct passwd *pwd;
+ int k5login_flag =0;
+ int k5users_flag =0;
+ FILE * login_fp = NULL , * users_fp = NULL;
+ char ** k5login_list = NULL, ** k5users_list = NULL;
+ char ** k5users_filt_list = NULL;
+ char ** combined_list = NULL;
+ struct stat tb;
+ krb5_error_code retval;
+
+ *princ_list = NULL;
+
+ /* no account => no access */
+
+ if ((pwd = getpwnam(luser)) == NULL)
+ return 0;
+
+ k5login_flag = stat(k5login_path, &tb);
+ k5users_flag = stat(k5users_path, &tb);
+
+ if (!k5login_flag){
+ if ((login_fp = fopen(k5login_path, "r")) == NULL)
+ return 0;
+ if ( fowner(login_fp, pwd->pw_uid) == FALSE){
+ close_time(1 /*k5users_flag*/, (FILE *) 0 /*users_fp*/,
+ k5login_flag,login_fp);
+ return 0;
+ }
+ }
+ if (!k5users_flag){
+ if ((users_fp = fopen(k5users_path, "r")) == NULL)
+ return 0;
+
+ if ( fowner(users_fp, pwd->pw_uid) == FALSE){
+ close_time(k5users_flag,users_fp, k5login_flag,login_fp);
+ return 0;
+ }
+
+ retval = get_all_princ_from_file (users_fp, &k5users_list);
+ if(retval) {
+ close_time(k5users_flag,users_fp, k5login_flag,login_fp);
+ return retval;
+ }
+
+ rewind(users_fp);
+
+ retval = filter(users_fp,cmd, k5users_list, &k5users_filt_list);
+ if(retval) {
+ close_time(k5users_flag,users_fp, k5login_flag, login_fp);
+ return retval;
+ }
+ }
+
+ if (!k5login_flag){
+ retval = get_all_princ_from_file (login_fp, &k5login_list);
+ if(retval) {
+ close_time(k5users_flag,users_fp, k5login_flag,login_fp);
+ return retval;
+ }
+ }
+
+ close_time(k5users_flag,users_fp, k5login_flag, login_fp);
+
+ retval = list_union(k5login_list, k5users_filt_list, &combined_list);
+ if (retval){
+ close_time(k5users_flag,users_fp, k5login_flag,login_fp);
+ return retval;
+ }
+ *princ_list = combined_list;
+ return 0;
+}
+
+static void close_time(k5users_flag, users_fp, k5login_flag, login_fp)
+ int k5users_flag;
+ FILE *users_fp;
+ int k5login_flag;
+ FILE *login_fp;
+{
+
+ if (!k5users_flag) fclose(users_fp);
+ if (!k5login_flag) fclose(login_fp);
+
+}
+
+static krb5_boolean find_str_in_list(list , elm)
+ char **list;
+ char *elm;
+{
+
+ int i=0;
+ krb5_boolean found = FALSE;
+
+ if (!list) return found;
+
+ while (list[i] ){
+ if (!strcmp(list[i], elm)){
+ found = TRUE;
+ break;
+ }
+ i++;
+ }
+
+ return found;
+}
+
+/**********************************************************************
+returns the principal that is closes to client (can be the the client
+himself). plist contains
+a principal list obtained from .k5login and .k5users file.
+A principal is picked that has the best chance of getting in.
+
+**********************************************************************/
+
+
+krb5_error_code get_closest_principal(context, plist, client, found)
+ krb5_context context;
+ char **plist;
+ krb5_principal *client;
+ krb5_boolean *found;
+{
+ krb5_error_code retval =0;
+ krb5_principal temp_client, best_client = NULL;
+ int i = 0, j=0, cnelem, pnelem;
+ krb5_boolean got_one;
+
+ *found = FALSE;
+
+ if (! plist ) return 0;
+
+ cnelem = krb5_princ_size(context, *client);
+
+ while(plist[i]){
+
+ retval = krb5_parse_name(context, plist[i], &temp_client);
+ if (retval)
+ return retval;
+
+ pnelem = krb5_princ_size(context, temp_client);
+
+ if ( cnelem > pnelem){
+ i++;
+ continue;
+ }
+
+ if (data_eq(*krb5_princ_realm(context, *client),
+ *krb5_princ_realm(context, temp_client))) {
+
+ got_one = TRUE;
+ for(j =0; j < cnelem; j ++){
+ krb5_data *p1 =
+ krb5_princ_component(context, *client, j);
+ krb5_data *p2 =
+ krb5_princ_component(context, temp_client, j);
+
+ if (!p1 || !p2 || !data_eq(*p1, *p2)) {
+ got_one = FALSE;
+ break;
+ }
+ }
+ if (got_one == TRUE){
+ if(best_client){
+ if(krb5_princ_size(context, best_client) >
+ krb5_princ_size(context, temp_client)){
+ best_client = temp_client;
+ }
+ }else
+ best_client = temp_client;
+ }
+ }
+ i++;
+ }
+
+ if (best_client) {
+ *found = TRUE;
+ *client = best_client;
+ }
+
+ return 0;
+}
+
+/****************************************************************
+find_either_ticket checks to see whether there is a ticket for the
+ end server or tgt, if neither is there the return FALSE,
+*****************************************************************/
+
+krb5_error_code find_either_ticket (context, cc, client, end_server, found)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_principal client;
+ krb5_principal end_server;
+ krb5_boolean *found;
+{
+
+ krb5_principal kdc_server;
+ krb5_error_code retval;
+ krb5_boolean temp_found = FALSE;
+
+ if (ks_ccache_is_initialized(context, cc)) {
+
+ retval = find_ticket(context, cc, client, end_server, &temp_found);
+ if (retval)
+ return retval;
+
+ if (temp_found == FALSE){
+ retval = ksu_tgtname(context,
+ krb5_princ_realm(context, client),
+ krb5_princ_realm(context, client),
+ &kdc_server);
+ if (retval)
+ return retval;
+
+ retval = find_ticket(context, cc,client, kdc_server, &temp_found);
+ if(retval)
+ return retval;
+ }
+ else if (auth_debug)
+ printf("find_either_ticket: found end server ticket\n");
+ }
+
+ *found = temp_found;
+
+ return 0;
+}
+
+
+krb5_error_code find_ticket (context, cc, client, server, found)
+ krb5_context context;
+ krb5_ccache cc;
+ krb5_principal client;
+ krb5_principal server;
+ krb5_boolean *found;
+{
+
+ krb5_creds tgt, tgtq;
+ krb5_error_code retval;
+
+ *found = FALSE;
+
+ memset(&tgtq, 0, sizeof(tgtq));
+ memset(&tgt, 0, sizeof(tgt));
+
+ retval= krb5_copy_principal(context, client, &tgtq.client);
+ if (retval)
+ return retval;
+
+ retval= krb5_copy_principal(context, server, &tgtq.server);
+ if (retval)
+ return retval ;
+
+ retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
+ &tgtq, &tgt);
+
+ if (! retval) retval = krb5_check_exp(context, tgt.times);
+
+ if (retval){
+ if ((retval != KRB5_CC_NOTFOUND) &&
+ (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){
+ return retval ;
+ }
+ } else{
+ *found = TRUE;
+ return 0;
+ }
+
+ free(tgtq.server);
+ free(tgtq.client);
+
+ return 0;
+}
+
+
+
+krb5_error_code find_princ_in_list (context, princ, plist, found)
+ krb5_context context;
+ krb5_principal princ;
+ char **plist;
+ krb5_boolean *found;
+{
+
+ int i=0;
+ char * princname;
+ krb5_error_code retval;
+
+ *found = FALSE;
+
+ if (!plist) return 0;
+
+ retval = krb5_unparse_name(context, princ, &princname);
+ if (retval)
+ return retval;
+
+ while (plist[i] ){
+ if (!strcmp(plist[i], princname)){
+ *found = TRUE;
+ break;
+ }
+ i++;
+ }
+
+ return 0;
+
+}
+
+typedef struct princ_info {
+ krb5_principal p;
+ krb5_boolean found;
+}princ_info;
+
+/**********************************************************************
+get_best_princ_for_target -
+
+sets the client name, path_out gets set, if authorization is not possible
+path_out gets set to ...
+
+***********************************************************************/
+
+krb5_error_code get_best_princ_for_target(context, source_uid, target_uid,
+ source_user, target_user,
+ cc_source, options, cmd,
+ hostname, client, path_out)
+ krb5_context context;
+ uid_t source_uid;
+ uid_t target_uid;
+ char *source_user;
+ char *target_user;
+ krb5_ccache cc_source;
+ krb5_get_init_creds_opt *options;
+ char *cmd;
+ char *hostname;
+ krb5_principal *client;
+ int *path_out;
+{
+
+ princ_info princ_trials[10];
+ krb5_principal cc_def_princ = NULL;
+ krb5_principal temp_client;
+ krb5_principal target_client;
+ krb5_principal source_client;
+ krb5_principal end_server;
+ krb5_error_code retval;
+ char ** aplist =NULL;
+ krb5_boolean found = FALSE;
+ struct stat tb;
+ int count =0;
+ int i;
+
+ *path_out = 0;
+
+ /* -n option was specified client is set we are done */
+ if (*client != NULL)
+ return 0;
+
+ if (ks_ccache_is_initialized(context, cc_source)) {
+ retval = krb5_cc_get_principal(context, cc_source, &cc_def_princ);
+ if (retval)
+ return retval;
+ }
+
+ retval=krb5_parse_name(context, target_user, &target_client);
+ if (retval)
+ return retval;
+
+ retval=krb5_parse_name(context, source_user, &source_client);
+ if (retval)
+ return retval;
+
+ if (source_uid == 0){
+ if (target_uid != 0)
+ *client = target_client; /* this will be used to restrict
+ the cache copty */
+ else {
+ if(cc_def_princ)
+ *client = cc_def_princ;
+ else
+ *client = target_client;
+ }
+
+ if (auth_debug)
+ printf(" GET_best_princ_for_target: via source_uid == 0\n");
+
+ return 0;
+ }
+
+ /* from here on, the code is for source_uid != 0 */
+
+ if (source_uid && (source_uid == target_uid)){
+ if(cc_def_princ)
+ *client = cc_def_princ;
+ else
+ *client = target_client;
+ if (auth_debug)
+ printf("GET_best_princ_for_target: via source_uid == target_uid\n");
+ return 0;
+ }
+
+ /* Become root, then target for looking at .k5login.*/
+ if (krb5_seteuid(0) || krb5_seteuid(target_uid) ) {
+ return errno;
+ }
+
+ /* if .k5users and .k5login do not exist */
+ if (stat(k5login_path, &tb) && stat(k5users_path, &tb) ){
+ *client = target_client;
+
+ if (cmd)
+ *path_out = NOT_AUTHORIZED;
+
+ if (auth_debug)
+ printf(" GET_best_princ_for_target: via no auth files path\n");
+
+ return 0;
+ }else{
+ retval = get_authorized_princ_names(target_user, cmd, &aplist);
+ if (retval)
+ return retval;
+
+ /* .k5users or .k5login exist, but no authorization */
+ if ((!aplist) || (!aplist[0])) {
+ *path_out = NOT_AUTHORIZED;
+ if (auth_debug)
+ printf("GET_best_princ_for_target: via empty auth files path\n");
+ return 0;
+ }
+ }
+
+ retval = krb5_sname_to_principal(context, hostname, NULL,
+ KRB5_NT_SRV_HST, &end_server);
+ if (retval)
+ return retval;
+
+
+ /* first see if default principal of the source cache
+ * can get us in, then the target_user@realm, then the
+ * source_user@realm. If all of them fail, try any
+ * other ticket in the cache. */
+
+ if (cc_def_princ)
+ princ_trials[count ++].p = cc_def_princ;
+ else
+ princ_trials[count ++].p = NULL;
+
+ princ_trials[count ++].p = target_client;
+ princ_trials[count ++].p = source_client;
+
+ for (i= 0; i < count; i ++)
+ princ_trials[i].found = FALSE;
+
+ for (i= 0; i < count; i ++){
+ if(princ_trials[i].p) {
+ retval= find_princ_in_list(context, princ_trials[i].p, aplist,
+ &found);
+ if (retval)
+ return retval;
+
+ if (found == TRUE){
+ princ_trials[i].found = TRUE;
+
+ retval = find_either_ticket (context, cc_source,
+ princ_trials[i].p,
+ end_server, &found);
+ if (retval)
+ return retval;
+ if (found == TRUE){
+ *client = princ_trials[i].p;
+ if (auth_debug)
+ printf("GET_best_princ_for_target: via ticket file, choice #%d\n", i);
+ return 0;
+ }
+ }
+ }
+ }
+
+ /* out of preferred principals, see if there is any ticket that will
+ get us in */
+
+ i=0;
+ while (aplist[i]){
+ retval = krb5_parse_name(context, aplist[i], &temp_client);
+ if (retval)
+ return retval;
+
+ retval = find_either_ticket (context, cc_source, temp_client,
+ end_server, &found);
+ if (retval)
+ return retval;
+
+ if (found == TRUE){
+ if (auth_debug)
+ printf("GET_best_princ_for_target: via ticket file, choice: any ok ticket \n" );
+ *client = temp_client;
+ return 0;
+ }
+
+ krb5_free_principal(context, temp_client);
+
+ i++;
+ }
+
+ /* no tickets qualified, select a principal, that may be used
+ for password promting */
+
+
+ for (i=0; i < count; i ++){
+ if (princ_trials[i].found == TRUE){
+ *client = princ_trials[i].p;
+
+ if (auth_debug)
+ printf("GET_best_princ_for_target: via prompt passwd list choice #%d \n",i);
+ return 0;
+ }
+ }
+
+#ifdef PRINC_LOOK_AHEAD
+ for (i=0; i < count; i ++){
+ if (princ_trials[i].p){
+ retval=krb5_copy_principal(context, princ_trials[i].p,
+ &temp_client);
+ if(retval)
+ return retval;
+
+ /* get the client name that is the closest
+ to the three princ in trials */
+
+ retval=get_closest_principal(context, aplist, &temp_client,
+ &found);
+ if(retval)
+ return retval;
+
+ if (found == TRUE){
+ *client = temp_client;
+ if (auth_debug)
+ printf("GET_best_princ_for_target: via prompt passwd list choice: approximation of princ in trials # %d \n",i);
+ return 0;
+ }
+ krb5_free_principal(context, temp_client);
+ }
+ }
+
+#endif /* PRINC_LOOK_AHEAD */
+
+
+ if(auth_debug)
+ printf( "GET_best_princ_for_target: out of luck, can't get appropriate default principal\n");
+
+ *path_out = NOT_AUTHORIZED;
+ return 0;
+}
diff --git a/src/clients/ksu/krb_auth_su.c b/src/clients/ksu/krb_auth_su.c
new file mode 100644
index 000000000000..7af48195cb1f
--- /dev/null
+++ b/src/clients/ksu/krb_auth_su.c
@@ -0,0 +1,313 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1994 by the University of Southern California
+ *
+ * EXPORT OF THIS SOFTWARE from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+ * this software and its documentation in source and binary forms is
+ * hereby granted, provided that any documentation or other materials
+ * related to such distribution or use acknowledge that the software
+ * was developed by the University of Southern California.
+ *
+ * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
+ * University of Southern California MAKES NO REPRESENTATIONS OR
+ * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, the University of Southern California MAKES NO
+ * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE. The University of Southern
+ * California shall not be held liable for any liability nor for any
+ * direct, indirect, or consequential damages with respect to any
+ * claim by the user or distributor of the ksu software.
+ *
+ * KSU was writen by: Ari Medvinsky, ari@isi.edu
+ */
+
+#include "ksu.h"
+
+
+void plain_dump_principal ();
+
+krb5_boolean krb5_auth_check(context, client_pname, hostname, options,
+ target_user, cc, path_passwd, target_uid)
+ krb5_context context;
+ krb5_principal client_pname;
+ char *hostname;
+ krb5_get_init_creds_opt *options;
+ char *target_user;
+ uid_t target_uid;
+ krb5_ccache cc;
+ int *path_passwd;
+{
+ krb5_principal client;
+ krb5_verify_init_creds_opt vfy_opts;
+ krb5_creds tgt, tgtq;
+ krb5_error_code retval =0;
+ int got_it = 0;
+ krb5_boolean zero_password;
+
+ *path_passwd = 0;
+ memset(&tgtq, 0, sizeof(tgtq));
+ memset(&tgt, 0, sizeof(tgt));
+
+ if ((retval= krb5_copy_principal(context, client_pname, &client))){
+ com_err(prog_name, retval, _("while copying client principal"));
+ return (FALSE) ;
+ }
+
+ if ((retval= krb5_copy_principal(context, client, &tgtq.client))){
+ com_err(prog_name, retval, _("while copying client principal"));
+ return (FALSE) ;
+ }
+
+ if ((retval = ksu_tgtname(context, krb5_princ_realm(context, client),
+ krb5_princ_realm(context, client),
+ &tgtq.server))){
+ com_err(prog_name, retval, _("while creating tgt for local realm"));
+ krb5_free_principal(context, client);
+ return (FALSE) ;
+ }
+
+ if (auth_debug){ dump_principal(context, "local tgt principal name", tgtq.server ); }
+ retval = krb5_cc_retrieve_cred(context, cc,
+ KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
+ &tgtq, &tgt);
+
+ if (! retval) retval = krb5_check_exp(context, tgt.times);
+
+ if (retval){
+ if ((retval != KRB5_CC_NOTFOUND) &&
+ (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){
+ com_err(prog_name, retval, _("while retrieving creds from cache"));
+ return (FALSE) ;
+ }
+ } else{
+ got_it = 1;
+ }
+
+ if (! got_it){
+
+#ifdef GET_TGT_VIA_PASSWD
+ if (krb5_seteuid(0)||krb5_seteuid(target_uid)) {
+ com_err("ksu", errno, _("while switching to target uid"));
+ return FALSE;
+ }
+
+
+ fprintf(stderr, _("WARNING: Your password may be exposed if you enter "
+ "it here and are logged \n"));
+ fprintf(stderr, _(" in remotely using an unsecure "
+ "(non-encrypted) channel. \n"));
+
+ /*get the ticket granting ticket, via passwd(prompt for passwd)*/
+ if (ksu_get_tgt_via_passwd(context, client, options, &zero_password,
+ &tgt) == FALSE) {
+ krb5_seteuid(0);
+
+ return FALSE;
+ }
+ *path_passwd = 1;
+ if (krb5_seteuid(0)) {
+ com_err("ksu", errno, _("while reclaiming root uid"));
+ return FALSE;
+ }
+
+#else
+ plain_dump_principal (context, client);
+ fprintf(stderr,
+ _("does not have any appropriate tickets in the cache.\n"));
+ return FALSE;
+
+#endif /* GET_TGT_VIA_PASSWD */
+
+ }
+
+ krb5_verify_init_creds_opt_init(&vfy_opts);
+ krb5_verify_init_creds_opt_set_ap_req_nofail( &vfy_opts, 1);
+ retval = krb5_verify_init_creds(context, &tgt, NULL, NULL, NULL,
+ &vfy_opts);
+ if (retval) {
+ com_err(prog_name, retval, _("while verifying ticket for server"));
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+krb5_boolean ksu_get_tgt_via_passwd(context, client, options, zero_password,
+ creds_out)
+ krb5_context context;
+ krb5_principal client;
+ krb5_get_init_creds_opt *options;
+ krb5_boolean *zero_password;
+ krb5_creds *creds_out;
+{
+ krb5_error_code code;
+ krb5_creds creds;
+ krb5_timestamp now;
+ unsigned int pwsize;
+ char password[255], *client_name, prompt[255];
+ int result;
+
+ *zero_password = FALSE;
+ if (creds_out != NULL)
+ memset(creds_out, 0, sizeof(*creds_out));
+
+ if ((code = krb5_unparse_name(context, client, &client_name))) {
+ com_err (prog_name, code, _("when unparsing name"));
+ return (FALSE);
+ }
+
+ memset(&creds, 0, sizeof(creds));
+
+ if ((code = krb5_timeofday(context, &now))) {
+ com_err(prog_name, code, _("while getting time of day"));
+ return (FALSE);
+ }
+
+ result = snprintf(prompt, sizeof(prompt), _("Kerberos password for %s: "),
+ client_name);
+ if (SNPRINTF_OVERFLOW(result, sizeof(prompt))) {
+ fprintf(stderr,
+ _("principal name %s too long for internal buffer space\n"),
+ client_name);
+ return FALSE;
+ }
+
+ pwsize = sizeof(password);
+
+ code = krb5_read_password(context, prompt, 0, password, &pwsize);
+ if (code ) {
+ com_err(prog_name, code, _("while reading password for '%s'\n"),
+ client_name);
+ memset(password, 0, sizeof(password));
+ return (FALSE);
+ }
+
+ if ( pwsize == 0) {
+ fprintf(stderr, _("No password given\n"));
+ *zero_password = TRUE;
+ memset(password, 0, sizeof(password));
+ return (FALSE);
+ }
+
+ code = krb5_get_init_creds_password(context, &creds, client, password,
+ krb5_prompter_posix, NULL, 0, NULL,
+ options);
+ memset(password, 0, sizeof(password));
+
+
+ if (code) {
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ fprintf(stderr, _("%s: Password incorrect\n"), prog_name);
+ else
+ com_err(prog_name, code, _("while getting initial credentials"));
+ return (FALSE);
+ }
+ if (creds_out != NULL)
+ *creds_out = creds;
+ else
+ krb5_free_cred_contents(context, &creds);
+ return (TRUE);
+}
+
+
+void dump_principal (context, str, p)
+ krb5_context context;
+ char *str;
+ krb5_principal p;
+{
+ char * stname;
+ krb5_error_code retval;
+
+ if ((retval = krb5_unparse_name(context, p, &stname))) {
+ fprintf(stderr, _(" %s while unparsing name\n"),
+ error_message(retval));
+ }
+ fprintf(stderr, " %s: %s\n", str, stname);
+}
+
+void plain_dump_principal (context, p)
+ krb5_context context;
+ krb5_principal p;
+{
+ char * stname;
+ krb5_error_code retval;
+
+ if ((retval = krb5_unparse_name(context, p, &stname))) {
+ fprintf(stderr, _(" %s while unparsing name\n"),
+ error_message(retval));
+ }
+ fprintf(stderr, "%s ", stname);
+}
+
+
+/**********************************************************************
+returns the principal that is closest to client. plist contains
+a principal list obtained from .k5login and parhaps .k5users file.
+This routine gets called before getting the password for a tgt.
+A principal is picked that has the best chance of getting in.
+
+**********************************************************************/
+
+
+krb5_error_code get_best_principal(context, plist, client)
+ krb5_context context;
+ char **plist;
+ krb5_principal *client;
+{
+ krb5_error_code retval =0;
+ krb5_principal temp_client, best_client = NULL;
+
+ int i = 0, nelem;
+
+ if (! plist ) return 0;
+
+ nelem = krb5_princ_size(context, *client);
+
+ while(plist[i]){
+
+ if ((retval = krb5_parse_name(context, plist[i], &temp_client))){
+ return retval;
+ }
+
+ if (data_eq(*krb5_princ_realm(context, *client),
+ *krb5_princ_realm(context, temp_client))) {
+
+ if (nelem &&
+ krb5_princ_size(context, *client) > 0 &&
+ krb5_princ_size(context, temp_client) > 0) {
+ krb5_data *p1 =
+ krb5_princ_component(context, *client, 0);
+ krb5_data *p2 =
+ krb5_princ_component(context, temp_client, 0);
+
+ if (data_eq(*p1, *p2)) {
+
+ if (auth_debug){
+ fprintf(stderr,
+ "get_best_principal: compare with %s\n",
+ plist[i]);
+ }
+
+ if(best_client){
+ if(krb5_princ_size(context, best_client) >
+ krb5_princ_size(context, temp_client)){
+ best_client = temp_client;
+ }
+ }else{
+ best_client = temp_client;
+ }
+ }
+ }
+
+ }
+ i++;
+ }
+
+ if (best_client) *client = best_client;
+ return 0;
+}
diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h
new file mode 100644
index 000000000000..ee8e9d6a0f79
--- /dev/null
+++ b/src/clients/ksu/ksu.h
@@ -0,0 +1,247 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1994 by the University of Southern California
+ *
+ * EXPORT OF THIS SOFTWARE from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+ * this software and its documentation in source and binary forms is
+ * hereby granted, provided that any documentation or other materials
+ * related to such distribution or use acknowledge that the software
+ * was developed by the University of Southern California.
+ *
+ * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
+ * University of Southern California MAKES NO REPRESENTATIONS OR
+ * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, the University of Southern California MAKES NO
+ * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE. The University of Southern
+ * California shall not be held liable for any liability nor for any
+ * direct, indirect, or consequential damages with respect to any
+ * claim by the user or distributor of the ksu software.
+ *
+ * KSU was writen by: Ari Medvinsky, ari@isi.edu
+ */
+
+#include "k5-int.h"
+#include "k5-util.h"
+#include <stdio.h>
+#include "com_err.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+/* <stdarg.h> or <varargs.h> is already included by com_err.h. */
+
+#define NO_TARGET_FILE '.'
+#define SOURCE_USER_LOGIN "."
+
+#define KRB5_DEFAULT_OPTIONS 0
+#define KRB5_DEFAULT_TKT_LIFE 60*60*12 /* 12 hours */
+
+#define KRB5_LOGIN_NAME ".k5login"
+#define KRB5_USERS_NAME ".k5users"
+#define USE_DEFAULT_REALM_NAME "."
+#define PERMIT_ALL_COMMANDS "*"
+#define KRB5_SEC_BUFFSIZE 80
+#define NOT_AUTHORIZED 1
+
+#define CHUNK 3
+#define CACHE_MODE 0600
+#define MAX_CMD 2048 /* this is temp, should use realloc instead,
+ as done in most of the code */
+
+
+extern int optind;
+extern char * optarg;
+
+/* globals */
+extern char * prog_name;
+extern int auth_debug;
+extern int quiet;
+extern char k5login_path[MAXPATHLEN];
+extern char k5users_path[MAXPATHLEN];
+extern char * gb_err;
+/***********/
+
+/* krb_auth_su.c */
+extern krb5_boolean krb5_auth_check
+(krb5_context, krb5_principal, char *, krb5_get_init_creds_opt *,
+ char *, krb5_ccache, int *, uid_t);
+
+extern krb5_boolean krb5_fast_auth
+(krb5_context, krb5_principal, krb5_principal, char *,
+ krb5_ccache);
+
+extern krb5_boolean ksu_get_tgt_via_passwd
+(krb5_context,
+ krb5_principal, krb5_get_init_creds_opt *, krb5_boolean *, krb5_creds *);
+
+extern void dump_principal
+(krb5_context, char *, krb5_principal);
+
+extern void plain_dump_principal
+(krb5_context, krb5_principal);
+
+
+extern krb5_error_code krb5_parse_lifetime
+(char *, long *);
+
+extern krb5_error_code get_best_principal
+(krb5_context, char **, krb5_principal *);
+
+/* ccache.c */
+extern krb5_error_code krb5_ccache_copy
+(krb5_context, krb5_ccache, krb5_principal, krb5_ccache,
+ krb5_boolean, krb5_principal, krb5_boolean *);
+
+extern krb5_error_code krb5_store_all_creds
+(krb5_context, krb5_ccache, krb5_creds **, krb5_creds **);
+
+extern krb5_error_code krb5_store_all_creds
+(krb5_context, krb5_ccache, krb5_creds **, krb5_creds **);
+
+extern krb5_boolean compare_creds
+(krb5_context, krb5_creds *, krb5_creds *);
+
+extern krb5_error_code krb5_get_nonexp_tkts
+(krb5_context, krb5_ccache, krb5_creds ***);
+
+extern krb5_error_code krb5_check_exp
+(krb5_context, krb5_ticket_times);
+
+extern char *flags_string (krb5_creds *);
+
+extern krb5_error_code krb5_get_login_princ
+(const char *, char ***);
+
+extern void show_credential
+(krb5_context, krb5_creds *, krb5_ccache);
+
+krb5_error_code gen_sym(krb5_context context, char **sym);
+
+extern krb5_error_code krb5_ccache_overwrite
+(krb5_context, krb5_ccache, krb5_ccache, krb5_principal);
+
+extern krb5_error_code krb5_store_some_creds
+(krb5_context, krb5_ccache, krb5_creds **, krb5_creds **,
+ krb5_principal, krb5_boolean *);
+
+extern krb5_boolean ks_ccache_name_is_initialized
+(krb5_context, const char *);
+
+extern krb5_boolean ks_ccache_is_initialized
+(krb5_context, krb5_ccache);
+
+extern krb5_error_code krb5_ccache_refresh
+(krb5_context, krb5_ccache);
+
+extern krb5_error_code krb5_ccache_filter
+(krb5_context, krb5_ccache, krb5_principal);
+
+extern krb5_boolean krb5_find_princ_in_cred_list
+(krb5_context, krb5_creds **, krb5_principal);
+
+extern krb5_error_code krb5_find_princ_in_cache
+(krb5_context, krb5_ccache, krb5_principal, krb5_boolean *);
+
+extern void printtime (time_t);
+
+/* authorization.c */
+extern krb5_boolean fowner (FILE *, uid_t);
+
+extern krb5_error_code krb5_authorization
+(krb5_context, krb5_principal, const char *, char *,
+ krb5_boolean *, char **);
+
+extern krb5_error_code k5login_lookup (FILE *, char *,
+ krb5_boolean *);
+
+extern krb5_error_code k5users_lookup
+(FILE *, char *, char *, krb5_boolean *, char **);
+
+extern krb5_boolean fcmd_resolve
+(char *, char ***, char **);
+
+extern krb5_boolean cmd_single (char *);
+
+extern int cmd_arr_cmp_postfix (char **, char *);
+
+extern int cmd_arr_cmp (char **, char *);
+
+extern krb5_boolean find_first_cmd_that_exists
+(char **, char **, char **);
+
+extern int match_commands
+(char *, char *, krb5_boolean *, char **, char **);
+
+extern krb5_error_code get_line (FILE *, char **);
+
+extern char * get_first_token (char *, char **);
+
+extern char * get_next_token (char **);
+
+extern void init_auth_names (char *);
+
+/* main.c */
+extern void usage (void);
+
+extern int standard_shell (char *);
+
+extern krb5_error_code get_params (int *, int, char **, char ***);
+
+/* heuristic.c */
+extern krb5_error_code get_all_princ_from_file (FILE *, char ***);
+
+extern krb5_error_code list_union (char **, char **, char ***);
+
+extern krb5_error_code filter (FILE *, char *, char **, char ***);
+
+extern krb5_error_code get_authorized_princ_names
+(const char *, char *, char ***);
+
+extern krb5_error_code get_closest_principal
+(krb5_context, char **, krb5_principal *, krb5_boolean *);
+
+extern krb5_error_code find_either_ticket
+(krb5_context, krb5_ccache, krb5_principal,
+ krb5_principal, krb5_boolean *);
+
+extern krb5_error_code find_ticket
+(krb5_context, krb5_ccache, krb5_principal,
+ krb5_principal, krb5_boolean *);
+
+
+extern krb5_error_code find_princ_in_list
+(krb5_context, krb5_principal, char **, krb5_boolean *);
+
+extern krb5_error_code get_best_princ_for_target
+(krb5_context, uid_t, uid_t, char *, char *, krb5_ccache,
+ krb5_get_init_creds_opt *, char *, char *, krb5_principal *, int *);
+
+extern krb5_error_code ksu_tgtname (krb5_context, const krb5_data *,
+ const krb5_data *,
+ krb5_principal *tgtprinc);
+
+#ifndef min
+#define min(a,b) ((a) > (b) ? (b) : (a))
+#endif /* min */
+
+
+extern char *krb5_lname_file; /* Note: print this out just be sure
+ that it gets set */
+
+extern void *xmalloc (size_t),
+ *xrealloc (void *, size_t),
+ *xcalloc (size_t, size_t);
+ extern char *xstrdup (const char *);
+ extern char *xasprintf (const char *format, ...);
+
+#ifndef HAVE_UNSETENV
+ void unsetenv (char *);
+#endif
diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
new file mode 100644
index 000000000000..28342c2d7736
--- /dev/null
+++ b/src/clients/ksu/main.c
@@ -0,0 +1,1038 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1994 by the University of Southern California
+ *
+ * EXPORT OF THIS SOFTWARE from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+ * this software and its documentation in source and binary forms is
+ * hereby granted, provided that any documentation or other materials
+ * related to such distribution or use acknowledge that the software
+ * was developed by the University of Southern California.
+ *
+ * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
+ * University of Southern California MAKES NO REPRESENTATIONS OR
+ * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
+ * limitation, the University of Southern California MAKES NO
+ * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE. The University of Southern
+ * California shall not be held liable for any liability nor for any
+ * direct, indirect, or consequential damages with respect to any
+ * claim by the user or distributor of the ksu software.
+ *
+ * KSU was writen by: Ari Medvinsky, ari@isi.edu
+ */
+
+#include "ksu.h"
+#include "adm_proto.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <grp.h>
+
+/* globals */
+char * prog_name;
+int auth_debug =0;
+char k5login_path[MAXPATHLEN];
+char k5users_path[MAXPATHLEN];
+char * gb_err = NULL;
+int quiet = 0;
+/***********/
+
+#define KS_TEMPORARY_CACHE "MEMORY:_ksu"
+#define KS_TEMPORARY_PRINC "_ksu/_ksu@_ksu"
+#define _DEF_CSH "/bin/csh"
+static int set_env_var (char *, char *);
+static void sweep_up (krb5_context, krb5_ccache);
+static char * ontty (void);
+static krb5_error_code set_ccname_env(krb5_context, krb5_ccache);
+static void print_status( const char *fmt, ...)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+ __attribute__ ((__format__ (__printf__, 1, 2)))
+#endif
+ ;
+static krb5_error_code resolve_target_cache(krb5_context ksu_context,
+ krb5_principal princ,
+ krb5_ccache *ccache_out,
+ krb5_boolean *ccache_reused);
+
+/* Note -e and -a options are mutually exclusive */
+/* insure the proper specification of target user as well as catching
+ ill specified arguments to commands */
+
+void usage (){
+ fprintf(stderr,
+ _("Usage: %s [target user] [-n principal] [-c source cachename] "
+ "[-k] [-r time] [-pf] [-l lifetime] [-zZ] [-q] "
+ "[-e command [args... ] ] [-a [args... ] ]\n"), prog_name);
+}
+
+/* for Ultrix and friends ... */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/* These are file static so sweep_up can get to them*/
+static uid_t source_uid, target_uid;
+
+int
+main (argc, argv)
+ int argc;
+ char ** argv;
+{
+ int hp =0;
+ int some_rest_copy = 0;
+ int all_rest_copy = 0;
+ char *localhostname = NULL;
+ krb5_get_init_creds_opt *options = NULL;
+ int option=0;
+ int statusp=0;
+ krb5_error_code retval = 0;
+ krb5_principal client = NULL, tmp_princ = NULL;
+ krb5_ccache cc_tmp = NULL, cc_target = NULL;
+ krb5_context ksu_context;
+ char * cc_target_tag = NULL;
+ char * target_user = NULL;
+ char * source_user;
+
+ krb5_ccache cc_source = NULL;
+ const char * cc_source_tag = NULL;
+ const char * cc_source_tag_tmp = NULL;
+ char * cmd = NULL, * exec_cmd = NULL;
+ int errflg = 0;
+ krb5_boolean auth_val;
+ krb5_boolean authorization_val = FALSE;
+ int path_passwd = 0;
+ int done =0,i,j;
+ uid_t ruid = getuid ();
+ struct passwd *pwd=NULL, *target_pwd ;
+ char * shell;
+ char ** params;
+ int keep_target_cache = 0;
+ int child_pid, child_pgrp, ret_pid;
+ extern char * getpass(), *crypt();
+ int pargc;
+ char ** pargv;
+ krb5_boolean stored = FALSE, cc_reused = FALSE, given_princ = FALSE;
+ krb5_boolean zero_password;
+ krb5_boolean restrict_creds;
+ krb5_deltat lifetime, rlife;
+
+ params = (char **) xcalloc (2, sizeof (char *));
+ params[1] = NULL;
+
+ unsetenv ("KRB5_CONFIG");
+
+ retval = krb5_init_secure_context(&ksu_context);
+ if (retval) {
+ com_err(argv[0], retval, _("while initializing krb5"));
+ exit(1);
+ }
+
+ retval = krb5_get_init_creds_opt_alloc(ksu_context, &options);
+ if (retval) {
+ com_err(argv[0], retval, _("while initializing krb5"));
+ exit(1);
+ }
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+ prog_name = argv[0];
+ if (strlen (prog_name) > 50) {
+ /* this many chars *after* last / ?? */
+ com_err(prog_name, 0,
+ _("program name too long - quitting to avoid triggering "
+ "system logging bugs"));
+ exit (1);
+ }
+
+
+#ifndef LOG_NDELAY
+#define LOG_NDELAY 0
+#endif
+
+#ifndef LOG_AUTH /* 4.2 syslog */
+ openlog(prog_name, LOG_PID|LOG_NDELAY);
+#else
+ openlog(prog_name, LOG_PID | LOG_NDELAY, LOG_AUTH);
+#endif /* 4.2 syslog */
+
+
+ if (( argc == 1) || (argv[1][0] == '-')){
+ target_user = xstrdup("root");
+ pargc = argc;
+ pargv = argv;
+ } else {
+ target_user = xstrdup(argv[1]);
+ pargc = argc -1;
+
+ if ((pargv =(char **) calloc(pargc +1,sizeof(char *)))==NULL){
+ com_err(prog_name, errno, _("while allocating memory"));
+ exit(1);
+ }
+
+ pargv[pargc] = NULL;
+ pargv[0] = argv[0];
+
+ for(i =1; i< pargc; i ++){
+ pargv[i] = argv[i + 1];
+ }
+ }
+
+ if (krb5_seteuid (ruid)) {
+ com_err (prog_name, errno, _("while setting euid to source user"));
+ exit (1);
+ }
+ while(!done && ((option = getopt(pargc, pargv,"n:c:r:a:zZDfpkql:e:")) != -1)){
+ switch (option) {
+ case 'r':
+ if (strlen (optarg) >= 14)
+ optarg = "bad-time";
+ retval = krb5_string_to_deltat(optarg, &rlife);
+ if (retval != 0 || rlife == 0) {
+ fprintf(stderr, _("Bad lifetime value (%s hours?)\n"), optarg);
+ errflg++;
+ }
+ krb5_get_init_creds_opt_set_renew_life(options, rlife);
+ break;
+ case 'a':
+ /* when integrating this remember to pass in pargc, pargv and
+ take care of params argument */
+ optind --;
+ if (auth_debug){printf("Before get_params optind=%d\n", optind);}
+
+ if ((retval = get_params( & optind, pargc, pargv, &params))){
+ com_err(prog_name, retval, _("when gathering parameters"));
+ errflg++;
+ }
+ if(auth_debug){ printf("After get_params optind=%d\n", optind);}
+ done = 1;
+ break;
+ case 'p':
+ krb5_get_init_creds_opt_set_proxiable(options, 1);
+ break;
+ case 'f':
+ krb5_get_init_creds_opt_set_forwardable(options, 1);
+ break;
+ case 'k':
+ keep_target_cache =1;
+ break;
+ case 'q':
+ quiet =1;
+ break;
+ case 'l':
+ if (strlen (optarg) >= 14)
+ optarg = "bad-time";
+ retval = krb5_string_to_deltat(optarg, &lifetime);
+ if (retval != 0 || lifetime == 0) {
+ fprintf(stderr, _("Bad lifetime value (%s hours?)\n"), optarg);
+ errflg++;
+ }
+ krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
+ break;
+ case 'n':
+ if ((retval = krb5_parse_name(ksu_context, optarg, &client))){
+ com_err(prog_name, retval, _("when parsing name %s"), optarg);
+ errflg++;
+ }
+ given_princ = TRUE;
+ break;
+#ifdef DEBUG
+ case 'D':
+ auth_debug = 1;
+ break;
+#endif
+ case 'z':
+ some_rest_copy = 1;
+ if(all_rest_copy) {
+ fprintf(stderr,
+ _("-z option is mutually exclusive with -Z.\n"));
+ errflg++;
+ }
+ break;
+ case 'Z':
+ all_rest_copy = 1;
+ if(some_rest_copy) {
+ fprintf(stderr,
+ _("-Z option is mutually exclusive with -z.\n"));
+ errflg++;
+ }
+ break;
+ case 'c':
+ if (cc_source_tag == NULL) {
+ cc_source_tag = xstrdup(optarg);
+ if ( strchr(cc_source_tag, ':')){
+ cc_source_tag_tmp = strchr(cc_source_tag, ':') + 1;
+
+ if (!ks_ccache_name_is_initialized(ksu_context,
+ cc_source_tag)) {
+ com_err(prog_name, errno,
+ _("while looking for credentials cache %s"),
+ cc_source_tag_tmp);
+ exit (1);
+ }
+ }
+ else {
+ fprintf(stderr, _("malformed credential cache name %s\n"),
+ cc_source_tag);
+ errflg++;
+ }
+
+ } else {
+ fprintf(stderr, _("Only one -c option allowed\n"));
+ errflg++;
+ }
+ break;
+ case 'e':
+ cmd = xstrdup(optarg);
+ if(auth_debug){printf("Before get_params optind=%d\n", optind);}
+ if ((retval = get_params( & optind, pargc, pargv, &params))){
+ com_err(prog_name, retval, _("when gathering parameters"));
+ errflg++;
+ }
+ if(auth_debug){printf("After get_params optind=%d\n", optind);}
+ done = 1;
+
+ if (auth_debug){
+ fprintf(stderr,"Command to be executed: %s\n", cmd);
+ }
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if (errflg) {
+ usage();
+ exit(2);
+ }
+
+ if (optind != pargc ){
+ usage();
+ exit(2);
+ }
+
+ if (auth_debug){
+ for(j=1; params[j] != NULL; j++){
+ fprintf (stderr,"params[%d]= %s\n", j,params[j]);
+ }
+ }
+
+ /***********************************/
+ source_user = getlogin(); /*checks for the the login name in /etc/utmp*/
+
+ /* verify that that the user exists and get his passwd structure */
+
+ if (source_user == NULL ||(pwd = getpwnam(source_user)) == NULL ||
+ pwd->pw_uid != ruid){
+ pwd = getpwuid(ruid);
+ }
+
+ if (pwd == NULL) {
+ fprintf(stderr, _("ksu: who are you?\n"));
+ exit(1);
+ }
+ if (pwd->pw_uid != ruid) {
+ fprintf (stderr, _("Your uid doesn't match your passwd entry?!\n"));
+ exit (1);
+ }
+ /* Okay, now we have *some* passwd entry that matches the
+ current real uid. */
+
+ /* allocate space and copy the usernamane there */
+ source_user = xstrdup(pwd->pw_name);
+ source_uid = pwd->pw_uid;
+
+ if (!strcmp(SOURCE_USER_LOGIN, target_user)){
+ target_user = xstrdup (source_user);
+ }
+
+ if ((target_pwd = getpwnam(target_user)) == NULL){
+ fprintf(stderr, _("ksu: unknown login %s\n"), target_user);
+ exit(1);
+ }
+ target_uid = target_pwd->pw_uid;
+
+ init_auth_names(target_pwd->pw_dir);
+
+ /***********************************/
+
+ if (cc_source_tag == NULL){
+ cc_source_tag = krb5_cc_default_name(ksu_context);
+ cc_source_tag_tmp = strchr(cc_source_tag, ':');
+ if (cc_source_tag_tmp == 0)
+ cc_source_tag_tmp = cc_source_tag;
+ else
+ cc_source_tag_tmp++;
+ }
+
+ /* get a handle for the cache */
+ if ((retval = krb5_cc_resolve(ksu_context, cc_source_tag, &cc_source))){
+ com_err(prog_name, retval, _("while getting source cache"));
+ exit(1);
+ }
+
+ if ((retval = get_best_princ_for_target(ksu_context, source_uid,
+ target_uid, source_user,
+ target_user, cc_source,
+ options, cmd, localhostname,
+ &client, &hp))){
+ com_err(prog_name,retval, _("while selecting the best principal"));
+ exit(1);
+ }
+
+ /* We may be running as either source or target, depending on
+ what happened; become source.*/
+ if ( geteuid() != source_uid) {
+ if (krb5_seteuid(0) || krb5_seteuid(source_uid) ) {
+ com_err(prog_name, errno, _("while returning to source uid after "
+ "finding best principal"));
+ exit(1);
+ }
+ }
+
+ if (auth_debug){
+ if (hp){
+ fprintf(stderr,
+ "GET_best_princ_for_target result: NOT AUTHORIZED\n");
+ }else{
+ fprintf(stderr,
+ "GET_best_princ_for_target result-best principal ");
+ plain_dump_principal (ksu_context, client);
+ fprintf(stderr,"\n");
+ }
+ }
+
+ if (hp){
+ if (gb_err) fprintf(stderr, "%s", gb_err);
+ fprintf(stderr, _("account %s: authorization failed\n"), target_user);
+ exit(1);
+ }
+
+ if (auth_debug)
+ fprintf(stderr, " source cache = %s\n", cc_source_tag);
+
+ /*
+ * After proper authentication and authorization, populate a cache for the
+ * target user.
+ */
+
+ /*
+ * We read the set of creds we want to copy from the source ccache as the
+ * source uid, become root for authentication, and then become the target
+ * user to handle authorization and creating the target user's cache.
+ */
+
+ /* if root ksu's to a regular user, then
+ then only the credentials for that particular user
+ should be copied */
+
+ restrict_creds = (source_uid == 0) && (target_uid != 0);
+ retval = krb5_parse_name(ksu_context, KS_TEMPORARY_PRINC, &tmp_princ);
+ if (retval) {
+ com_err(prog_name, retval, _("while parsing temporary name"));
+ exit(1);
+ }
+ retval = krb5_cc_resolve(ksu_context, KS_TEMPORARY_CACHE, &cc_tmp);
+ if (retval) {
+ com_err(prog_name, retval, _("while creating temporary cache"));
+ exit(1);
+ }
+ retval = krb5_ccache_copy(ksu_context, cc_source, tmp_princ, cc_tmp,
+ restrict_creds, client, &stored);
+ if (retval) {
+ com_err(prog_name, retval, _("while copying cache %s to %s"),
+ krb5_cc_get_name(ksu_context, cc_source), KS_TEMPORARY_CACHE);
+ exit(1);
+ }
+ krb5_cc_close(ksu_context, cc_source);
+
+ krb5_get_init_creds_opt_set_out_ccache(ksu_context, options, cc_tmp);
+
+ /* Become root for authentication*/
+
+ if (krb5_seteuid(0)) {
+ com_err(prog_name, errno, _("while reclaiming root uid"));
+ exit(1);
+ }
+
+ if ((source_uid == 0) || (target_uid == source_uid)){
+#ifdef GET_TGT_VIA_PASSWD
+ if (!all_rest_copy && given_princ && client != NULL && !stored) {
+ fprintf(stderr, _("WARNING: Your password may be exposed if you "
+ "enter it here and are logged\n"));
+ fprintf(stderr, _(" in remotely using an unsecure "
+ "(non-encrypted) channel.\n"));
+ if (ksu_get_tgt_via_passwd(ksu_context, client, options,
+ &zero_password, NULL) == FALSE) {
+
+ if (zero_password == FALSE){
+ fprintf(stderr, _("Goodbye\n"));
+ exit(1);
+ }
+
+ fprintf(stderr, _("Could not get a tgt for "));
+ plain_dump_principal (ksu_context, client);
+ fprintf(stderr, "\n");
+
+ }
+ stored = TRUE;
+ }
+#endif /* GET_TGT_VIA_PASSWD */
+ }
+
+ /* if the user is root or same uid then authentication is not neccesary,
+ root gets in automatically */
+
+ if (source_uid && (source_uid != target_uid)) {
+ char * client_name;
+
+ auth_val = krb5_auth_check(ksu_context, client, localhostname,
+ options, target_user, cc_tmp,
+ &path_passwd, target_uid);
+
+ /* if Kerberos authentication failed then exit */
+ if (auth_val ==FALSE){
+ fprintf(stderr, _("Authentication failed.\n"));
+ syslog(LOG_WARNING, "'%s %s' authentication failed for %s%s",
+ prog_name,target_user,source_user,ontty());
+ exit(1);
+ }
+ stored = TRUE;
+
+ if ((retval = krb5_unparse_name(ksu_context, client, &client_name))) {
+ com_err(prog_name, retval, _("When unparsing name"));
+ exit(1);
+ }
+
+ print_status(_("Authenticated %s\n"), client_name);
+ syslog(LOG_NOTICE,"'%s %s' authenticated %s for %s%s",
+ prog_name,target_user,client_name,
+ source_user,ontty());
+
+ /* Run authorization as target.*/
+ if (krb5_seteuid(target_uid)) {
+ com_err(prog_name, errno, _("while switching to target for "
+ "authorization check"));
+ exit(1);
+ }
+
+ if ((retval = krb5_authorization(ksu_context, client,target_user,
+ cmd, &authorization_val, &exec_cmd))){
+ com_err(prog_name,retval, _("while checking authorization"));
+ krb5_seteuid(0); /*So we have some chance of sweeping up*/
+ exit(1);
+ }
+
+ if (krb5_seteuid(0)) {
+ com_err(prog_name, errno, _("while switching back from target "
+ "after authorization check"));
+ exit(1);
+ }
+ if (authorization_val == TRUE){
+
+ if (cmd) {
+ print_status(_("Account %s: authorization for %s for "
+ "execution of\n"), target_user, client_name);
+ print_status(_(" %s successful\n"), exec_cmd);
+ syslog(LOG_NOTICE,
+ "Account %s: authorization for %s for execution of %s successful",
+ target_user, client_name, exec_cmd);
+
+ }else{
+ print_status(_("Account %s: authorization for %s "
+ "successful\n"), target_user, client_name);
+ syslog(LOG_NOTICE,
+ "Account %s: authorization for %s successful",
+ target_user, client_name);
+ }
+ }else {
+ if (cmd){
+ if (exec_cmd){ /* was used to pass back the error msg */
+ fprintf(stderr, "%s", exec_cmd );
+ syslog(LOG_WARNING, "%s",exec_cmd);
+ }
+ fprintf(stderr, _("Account %s: authorization for %s for "
+ "execution of %s failed\n"),
+ target_user, client_name, cmd );
+ syslog(LOG_WARNING,
+ "Account %s: authorization for %s for execution of %s failed",
+ target_user, client_name, cmd );
+
+ }else{
+ fprintf(stderr, _("Account %s: authorization of %s failed\n"),
+ target_user, client_name);
+ syslog(LOG_WARNING,
+ "Account %s: authorization of %s failed",
+ target_user, client_name);
+
+ }
+
+ exit(1);
+ }
+ }
+
+ if( some_rest_copy){
+ retval = krb5_ccache_filter(ksu_context, cc_tmp, client);
+ if (retval) {
+ com_err(prog_name,retval, _("while calling cc_filter"));
+ exit(1);
+ }
+ }
+
+ if (all_rest_copy){
+ retval = krb5_cc_initialize(ksu_context, cc_tmp, tmp_princ);
+ if (retval) {
+ com_err(prog_name, retval, _("while erasing target cache"));
+ exit(1);
+ }
+ stored = FALSE;
+ }
+
+ /* get the shell of the user, this will be the shell used by su */
+ target_pwd = getpwnam(target_user);
+
+ if (target_pwd->pw_shell)
+ shell = xstrdup(target_pwd->pw_shell);
+ else {
+ shell = _DEF_CSH; /* default is cshell */
+ }
+
+#ifdef HAVE_GETUSERSHELL
+
+ /* insist that the target login uses a standard shell (root is omited) */
+
+ if (!standard_shell(target_pwd->pw_shell) && source_uid) {
+ fprintf(stderr, _("ksu: permission denied (shell).\n"));
+ exit(1);
+ }
+#endif /* HAVE_GETUSERSHELL */
+
+ if (target_pwd->pw_uid){
+
+ if(set_env_var("USER", target_pwd->pw_name)){
+ fprintf(stderr,
+ _("ksu: couldn't set environment variable USER\n"));
+ exit(1);
+ }
+ }
+
+ if(set_env_var( "HOME", target_pwd->pw_dir)){
+ fprintf(stderr, _("ksu: couldn't set environment variable HOME\n"));
+ exit(1);
+ }
+
+ if(set_env_var( "SHELL", shell)){
+ fprintf(stderr, _("ksu: couldn't set environment variable SHELL\n"));
+ exit(1);
+ }
+
+ /* set permissions */
+ if (setgid(target_pwd->pw_gid) < 0) {
+ perror("ksu: setgid");
+ exit(1);
+ }
+
+ if (initgroups(target_user, target_pwd->pw_gid)) {
+ fprintf(stderr, _("ksu: initgroups failed.\n"));
+ exit(1);
+ }
+
+ if ( ! strcmp(target_user, source_user)){
+ print_status(_("Leaving uid as %s (%ld)\n"),
+ target_user, (long) target_pwd->pw_uid);
+ }else{
+ print_status(_("Changing uid to %s (%ld)\n"),
+ target_user, (long) target_pwd->pw_uid);
+ }
+
+#ifdef HAVE_SETLUID
+ /*
+ * If we're on a system which keeps track of login uids, then
+ * set the login uid. If this fails this opens up a problem on DEC OSF
+ * with C2 enabled.
+ */
+ if (setluid((uid_t) pwd->pw_uid) < 0) {
+ perror("setluid");
+ exit(1);
+ }
+#endif /* HAVE_SETLUID */
+
+ if (setuid(target_pwd->pw_uid) < 0) {
+ perror("ksu: setuid");
+ exit(1);
+ }
+
+ retval = resolve_target_cache(ksu_context, client, &cc_target, &cc_reused);
+ if (retval)
+ exit(1);
+ retval = krb5_cc_get_full_name(ksu_context, cc_target, &cc_target_tag);
+ if (retval) {
+ com_err(prog_name, retval, _("while getting name of target ccache"));
+ sweep_up(ksu_context, cc_target);
+ exit(1);
+ }
+ if (auth_debug)
+ fprintf(stderr, " target cache = %s\n", cc_target_tag);
+ if (cc_reused)
+ keep_target_cache = TRUE;
+
+ if (stored) {
+ retval = krb5_ccache_copy(ksu_context, cc_tmp, client, cc_target,
+ FALSE, client, &stored);
+ if (retval) {
+ com_err(prog_name, retval, _("while copying cache %s to %s"),
+ KS_TEMPORARY_CACHE, cc_target_tag);
+ exit(1);
+ }
+
+ if (!ks_ccache_is_initialized(ksu_context, cc_target)) {
+ com_err(prog_name, errno,
+ _("%s does not have correct permissions for %s, "
+ "%s aborted"), target_user, cc_target_tag, prog_name);
+ exit(1);
+ }
+ }
+
+ krb5_free_string(ksu_context, cc_target_tag);
+
+ /* Set the cc env name to target. */
+ retval = set_ccname_env(ksu_context, cc_target);
+ if (retval != 0) {
+ sweep_up(ksu_context, cc_target);
+ exit(1);
+ }
+
+ if (cmd){
+ if ((source_uid == 0) || (source_uid == target_uid )){
+ exec_cmd = cmd;
+ }
+
+ if( !exec_cmd){
+ fprintf(stderr, _("Internal error: command %s did not get "
+ "resolved\n"), cmd);
+ exit(1);
+ }
+
+ params[0] = exec_cmd;
+ }
+ else{
+ params[0] = shell;
+ }
+
+ if (auth_debug){
+ fprintf(stderr, "program to be execed %s\n",params[0]);
+ }
+
+ if( keep_target_cache ) {
+ execv(params[0], params);
+ com_err(prog_name, errno, _("while trying to execv %s"), params[0]);
+ sweep_up(ksu_context, cc_target);
+ exit(1);
+ }else{
+ statusp = 1;
+ switch ((child_pid = fork())) {
+ default:
+ if (auth_debug){
+ printf(" The child pid is %ld\n", (long) child_pid);
+ printf(" The parent pid is %ld\n", (long) getpid());
+ }
+ while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
+ if (WIFSTOPPED(statusp)) {
+ child_pgrp = tcgetpgrp(1);
+ kill(getpid(), SIGSTOP);
+ tcsetpgrp(1, child_pgrp);
+ kill(child_pid, SIGCONT);
+ statusp = 1;
+ continue;
+ }
+ break;
+ }
+ if (auth_debug){
+ printf("The exit status of the child is %d\n", statusp);
+ }
+ if (ret_pid == -1) {
+ com_err(prog_name, errno, _("while calling waitpid"));
+ }
+ sweep_up(ksu_context, cc_target);
+ exit (statusp);
+ case -1:
+ com_err(prog_name, errno, _("while trying to fork."));
+ sweep_up(ksu_context, cc_target);
+ exit (1);
+ case 0:
+ execv(params[0], params);
+ com_err(prog_name, errno, _("while trying to execv %s"),
+ params[0]);
+ exit (1);
+ }
+ }
+}
+
+/* Set KRB5CCNAME in the environment to point to ccache. Print an error
+ * message on failure. */
+static krb5_error_code
+set_ccname_env(krb5_context ksu_context, krb5_ccache ccache)
+{
+ krb5_error_code retval;
+ char *ccname;
+
+ retval = krb5_cc_get_full_name(ksu_context, ccache, &ccname);
+ if (retval) {
+ com_err(prog_name, retval, _("while reading cache name from ccache"));
+ return retval;
+ }
+ if (set_env_var(KRB5_ENV_CCNAME, ccname)) {
+ retval = errno;
+ fprintf(stderr,
+ _("ksu: couldn't set environment variable %s\n"),
+ KRB5_ENV_CCNAME);
+ }
+ krb5_free_string(ksu_context, ccname);
+ return retval;
+}
+
+/*
+ * Get the configured default ccache name. Unset KRB5CCNAME and force a
+ * recomputation so we don't use values for the source user. Print an error
+ * message on failure.
+ */
+static krb5_error_code
+get_configured_defccname(krb5_context context, char **target_out)
+{
+ krb5_error_code retval;
+ const char *defname;
+ char *target = NULL;
+
+ *target_out = NULL;
+
+ unsetenv(KRB5_ENV_CCNAME);
+
+ /* Make sure we don't have a cached value for a different uid. */
+ retval = krb5_cc_set_default_name(context, NULL);
+ if (retval != 0) {
+ com_err(prog_name, retval, _("while resetting target ccache name"));
+ return retval;
+ }
+
+ defname = krb5_cc_default_name(context);
+ if (defname != NULL) {
+ if (strchr(defname, ':') != NULL) {
+ target = strdup(defname);
+ } else {
+ if (asprintf(&target, "FILE:%s", defname) < 0)
+ target = NULL;
+ }
+ }
+ if (target == NULL) {
+ com_err(prog_name, ENOMEM, _("while determining target ccache name"));
+ return ENOMEM;
+ }
+ *target_out = target;
+ return 0;
+}
+
+/* Determine where the target user's creds should be stored. Print an error
+ * message on failure. */
+static krb5_error_code
+resolve_target_cache(krb5_context context, krb5_principal princ,
+ krb5_ccache *ccache_out, krb5_boolean *ccache_reused)
+{
+ krb5_error_code retval;
+ krb5_boolean switchable, reused = FALSE;
+ krb5_ccache ccache = NULL;
+ char *sep, *ccname = NULL, *sym = NULL, *target;
+
+ *ccache_out = NULL;
+ *ccache_reused = FALSE;
+
+ retval = get_configured_defccname(context, &target);
+ if (retval != 0)
+ return retval;
+
+ /* Check if the configured default name uses a switchable type. */
+ sep = strchr(target, ':');
+ *sep = '\0';
+ switchable = krb5_cc_support_switch(context, target);
+ *sep = ':';
+
+ if (!switchable) {
+ /* Try to avoid destroying an in-use target ccache by coming up with
+ * the name of a cache that doesn't exist yet. */
+ do {
+ free(ccname);
+ retval = gen_sym(context, &sym);
+ if (retval) {
+ com_err(prog_name, retval,
+ _("while generating part of the target ccache name"));
+ return retval;
+ }
+ if (asprintf(&ccname, "%s.%s", target, sym) < 0) {
+ retval = ENOMEM;
+ free(sym);
+ com_err(prog_name, retval, _("while allocating memory for the "
+ "target ccache name"));
+ goto cleanup;
+ }
+ free(sym);
+ } while (ks_ccache_name_is_initialized(context, ccname));
+ retval = krb5_cc_resolve(context, ccname, &ccache);
+ } else {
+ /* Look for a cache in the collection that we can reuse. */
+ retval = krb5_cc_cache_match(context, princ, &ccache);
+ if (retval == 0) {
+ reused = TRUE;
+ } else {
+ /* There isn't one, so create a new one. */
+ *sep = '\0';
+ retval = krb5_cc_new_unique(context, target, NULL, &ccache);
+ *sep = ':';
+ if (retval) {
+ com_err(prog_name, retval,
+ _("while creating new target ccache"));
+ goto cleanup;
+ }
+ retval = krb5_cc_initialize(context, ccache, princ);
+ if (retval) {
+ com_err(prog_name, retval,
+ _("while initializing target cache"));
+ goto cleanup;
+ }
+ }
+ }
+
+ *ccache_out = ccache;
+ *ccache_reused = reused;
+
+cleanup:
+ free(target);
+ return retval;
+}
+
+#ifdef HAVE_GETUSERSHELL
+
+int standard_shell(sh)
+ char *sh;
+{
+ register char *cp;
+ char *getusershell();
+
+ while ((cp = getusershell()) != NULL)
+ if (!strcmp(cp, sh))
+ return (1);
+ return (0);
+}
+
+#endif /* HAVE_GETUSERSHELL */
+
+static char * ontty()
+{
+ char *p, *ttyname();
+ static char buf[MAXPATHLEN + 5];
+ int result;
+
+ buf[0] = 0;
+ if ((p = ttyname(STDERR_FILENO))) {
+ result = snprintf(buf, sizeof(buf), " on %s", p);
+ if (SNPRINTF_OVERFLOW(result, sizeof(buf))) {
+ fprintf(stderr, _("terminal name %s too long\n"), p);
+ exit (1);
+ }
+ }
+ return (buf);
+}
+
+
+static int set_env_var(name, value)
+ char *name;
+ char *value;
+{
+ char * env_var_buf;
+
+ asprintf(&env_var_buf,"%s=%s",name, value);
+ return putenv(env_var_buf);
+
+}
+
+static void sweep_up(context, cc)
+ krb5_context context;
+ krb5_ccache cc;
+{
+ krb5_error_code retval;
+
+ krb5_seteuid(0);
+ if (krb5_seteuid(target_uid) < 0) {
+ com_err(prog_name, errno,
+ _("while changing to target uid for destroying ccache"));
+ exit(1);
+ }
+
+ if (ks_ccache_is_initialized(context, cc)) {
+ if ((retval = krb5_cc_destroy(context, cc)))
+ com_err(prog_name, retval, _("while destroying cache"));
+ }
+}
+
+/*****************************************************************
+get_params is to be called for the -a option or -e option to
+ collect all params passed in for the shell or for
+ cmd. An aray is returned containing all params.
+ optindex is incremented accordingly and the first
+ element in the returned array is reserved for the
+ name of the command to be executed or the name of the
+ shell.
+*****************************************************************/
+
+krb5_error_code
+get_params(optindex, pargc, pargv, params)
+ int *optindex;
+ int pargc;
+ char **pargv;
+ char ***params;
+{
+
+ int i,j;
+ char ** ret_params;
+ int size = pargc - *optindex + 2;
+
+ if ((ret_params = (char **) calloc(size, sizeof (char *)))== NULL ){
+ return ENOMEM;
+ }
+
+ for (i = *optindex, j=1; i < pargc; i++,j++){
+ ret_params[j] = pargv[i];
+ *optindex = *optindex + 1;
+ }
+
+ ret_params[size-1] = NULL;
+ *params = ret_params;
+ return 0;
+}
+
+static
+void print_status(const char *fmt, ...)
+{
+ va_list ap;
+ if (! quiet){
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+krb5_error_code
+ksu_tgtname(context, server, client, tgtprinc)
+ krb5_context context;
+ const krb5_data *server, *client;
+ krb5_principal *tgtprinc;
+{
+ return krb5_build_principal_ext(context, tgtprinc, client->length, client->data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ server->length, server->data,
+ 0);
+}
diff --git a/src/clients/ksu/setenv.c b/src/clients/ksu/setenv.c
new file mode 100644
index 000000000000..a7895c6b47ca
--- /dev/null
+++ b/src/clients/ksu/setenv.c
@@ -0,0 +1,174 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* based on @(#)setenv.c 5.2 (Berkeley) 6/27/88 */
+
+#include "autoconf.h"
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *_findenv(char *, int *);
+
+#ifndef HAVE_SETENV
+extern int setenv(char *, char *, int);
+#endif
+#ifndef HAVE_UNSETENV
+extern void unsetenv(char *);
+#endif
+
+/*
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ */
+#ifndef HAVE_SETENV
+int
+setenv(name, value, rewrite)
+ register char *name, *value;
+ int rewrite;
+{
+ extern char **environ;
+ static int alloced; /* if allocated space before */
+ register char *C;
+ int l_value, offset;
+
+ if (*value == '=') /* no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((C = _findenv(name, &offset))) { /* find if already exists */
+ if (!rewrite)
+ return(0);
+ if (strlen(C) >= l_value) { /* old larger; copy over */
+ while ((*C++ = *value++));
+ return(0);
+ }
+ }
+ else { /* create new slot */
+ register int cnt;
+ register char **P;
+
+ for (P = environ, cnt = 0; *P; ++P, ++cnt);
+ if (alloced) { /* just increase size */
+ environ = (char **)realloc((char *)environ,
+ (u_int)(sizeof(char *) * (cnt + 2)));
+ if (!environ)
+ return(-1);
+ }
+ else { /* get new space */
+ alloced = 1; /* copy old entries into it */
+ P = (char **)malloc((u_int)(sizeof(char *) *
+ (cnt + 2)));
+ if (!P)
+ return(-1);
+ memcpy(P, environ, cnt * sizeof(char *));
+ environ = P;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (C = name; *C && *C != '='; ++C); /* no `=' in name */
+ if (!(environ[offset] = /* name + `=' + value */
+ malloc((u_int)((int)(C - name) + l_value + 2))))
+ return(-1);
+ for (C = environ[offset]; (*C = *name++) &&( *C != '='); ++C);
+ for (*C++ = '='; (*C++ = *value++) != NULL;);
+ return(0);
+}
+#endif
+
+/*
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+#ifndef HAVE_UNSETENV
+void
+unsetenv(name)
+ char *name;
+{
+ extern char **environ;
+ register char **P;
+ int offset;
+
+ while (_findenv(name, &offset)) /* if set multiple times */
+ for (P = &environ[offset];; ++P)
+ if (!(*P = *(P + 1)))
+ break;
+}
+#endif
+
+/* based on @(#)getenv.c 5.5 (Berkeley) 6/27/88 */
+
+/*
+ * getenv --
+ * Returns ptr to value associated with name, if any, else NULL.
+ */
+#ifndef HAVE_GETENV
+char *
+getenv(name)
+ char *name;
+{
+ int offset;
+
+ return(_findenv(name, &offset));
+}
+#endif
+
+/*
+ * _findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by setenv(3) and unsetenv(3).
+ * Explicitly removes '=' in argument name.
+ *
+ */
+static char *
+_findenv(name, offset)
+ register char *name;
+ int *offset;
+{
+ extern char **environ;
+ register int len;
+ register char **P, *C;
+
+ for (C = name, len = 0; *C && *C != '='; ++C, ++len);
+ for (P = environ; *P; ++P)
+ if (!strncmp(*P, name, len))
+ if (*(C = *P + len) == '=') {
+ *offset = P - environ;
+ return(++C);
+ }
+ return(NULL);
+}
diff --git a/src/clients/ksu/xmalloc.c b/src/clients/ksu/xmalloc.c
new file mode 100644
index 000000000000..430a6b3bf415
--- /dev/null
+++ b/src/clients/ksu/xmalloc.c
@@ -0,0 +1,80 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/ksu/xmalloc.c - Exit-on-failure allocation wrappers */
+/*
+ * Copyright 1999 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-platform.h"
+#include "ksu.h"
+
+void *xmalloc (size_t sz)
+{
+ void *ret = malloc (sz);
+ if (ret == 0 && sz != 0) {
+ perror (prog_name);
+ exit (1);
+ }
+ return ret;
+}
+
+void *xrealloc (void *old, size_t newsz)
+{
+ void *ret = realloc (old, newsz);
+ if (ret == 0 && newsz != 0) {
+ perror (prog_name);
+ exit (1);
+ }
+ return ret;
+}
+
+void *xcalloc (size_t nelts, size_t eltsz)
+{
+ void *ret = calloc (nelts, eltsz);
+ if (ret == 0 && nelts != 0 && eltsz != 0) {
+ perror (prog_name);
+ exit (1);
+ }
+ return ret;
+}
+
+char *xstrdup (const char *src)
+{
+ size_t len = strlen (src) + 1;
+ char *dst = xmalloc (len);
+ memcpy (dst, src, len);
+ return dst;
+}
+
+char *xasprintf (const char *format, ...)
+{
+ char *out;
+ va_list args;
+
+ va_start (args, format);
+ if (vasprintf(&out, format, args) < 0) {
+ perror (prog_name);
+ exit (1);
+ }
+ va_end(args);
+ return out;
+}
diff --git a/src/clients/kswitch/Makefile.in b/src/clients/kswitch/Makefile.in
new file mode 100644
index 000000000000..e3d3680624ad
--- /dev/null
+++ b/src/clients/kswitch/Makefile.in
@@ -0,0 +1,33 @@
+mydir=clients$(S)kswitch
+BUILDTOP=$(REL)..$(S)..
+
+SRCS=kswitch.c
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KSWITCH=$(OUTPRE)kswitch.exe
+
+##WIN32##EXERES=$(KSWITCH:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKSWITCH_APP -fo $@ -r $**
+
+all-unix: kswitch
+##WIN32##all-windows: $(KSWITCH)
+
+kswitch: kswitch.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ kswitch.o $(KRB5_BASE_LIBS)
+
+##WIN32##$(KSWITCH): $(OUTPRE)kswitch.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) -out:$@ $**
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) kswitch.o kswitch
+
+install-unix:
+ for f in kswitch; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/kswitch/deps b/src/clients/kswitch/deps
new file mode 100644
index 000000000000..0bb88b627db2
--- /dev/null
+++ b/src/clients/kswitch/deps
@@ -0,0 +1,13 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kswitch.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kswitch.c
diff --git a/src/clients/kswitch/kswitch.c b/src/clients/kswitch/kswitch.c
new file mode 100644
index 000000000000..f26ecea03293
--- /dev/null
+++ b/src/clients/kswitch/kswitch.c
@@ -0,0 +1,132 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* clients/kswitch/kswitch.c - Switch primary credential cache */
+/*
+ * Copyright 2011 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include <locale.h>
+
+extern int optind;
+extern char *optarg;
+
+#ifndef _WIN32
+#define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
+#else
+#define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
+#endif
+
+static char *progname;
+
+static void
+usage(void)
+{
+ fprintf(stderr, _("Usage: %s {-c cache_name | -p principal}\n"), progname);
+ fprintf(stderr, _("\t-c specify name of credentials cache\n"));
+ fprintf(stderr, _("\t-p specify name of principal\n"));
+ exit(2);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int c;
+ krb5_ccache cache = NULL;
+ krb5_principal princ = NULL;
+ const char *cache_name = NULL, *princ_name = NULL;
+ krb5_boolean errflag = FALSE;
+
+ setlocale(LC_ALL, "");
+ progname = GET_PROGNAME(argv[0]);
+
+ while ((c = getopt(argc, argv, "c:p:")) != -1) {
+ switch (c) {
+ case 'c':
+ case 'p':
+ if (cache_name || princ_name) {
+ fprintf(stderr, _("Only one -c or -p option allowed\n"));
+ errflag = TRUE;
+ } else if (c == 'c') {
+ cache_name = optarg;
+ } else {
+ princ_name = optarg;
+ }
+ break;
+ case '?':
+ default:
+ errflag = TRUE;
+ break;
+ }
+ }
+
+ if (optind != argc)
+ errflag = TRUE;
+
+ if (!cache_name && !princ_name) {
+ fprintf(stderr, _("One of -c or -p must be specified\n"));
+ errflag = TRUE;
+ }
+
+ if (errflag)
+ usage();
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ com_err(progname, ret, _("while initializing krb5"));
+ exit(1);
+ }
+
+ if (cache_name) {
+ ret = krb5_cc_resolve(context, cache_name, &cache);
+ if (ret != 0) {
+ com_err(progname, ret, _("while resolving %s"), cache_name);
+ exit(1);
+ }
+ } else {
+ ret = krb5_parse_name(context, princ_name, &princ);
+ if (ret) {
+ com_err(progname, ret, _("while parsing principal name %s"),
+ princ_name);
+ exit(1);
+ }
+ ret = krb5_cc_cache_match(context, princ, &cache);
+ if (ret) {
+ com_err(progname, ret, _("while searching for ccache for %s"),
+ princ_name);
+ exit(1);
+ }
+ krb5_free_principal(context, princ);
+ }
+
+ ret = krb5_cc_switch(context, cache);
+ if (ret != 0) {
+ com_err(progname, ret, _("while switching to credential cache"));
+ exit(1);
+ }
+
+ krb5_cc_close(context, cache);
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/src/clients/kvno/Makefile.in b/src/clients/kvno/Makefile.in
new file mode 100644
index 000000000000..1c3f79392d16
--- /dev/null
+++ b/src/clients/kvno/Makefile.in
@@ -0,0 +1,36 @@
+mydir=clients$(S)kvno
+BUILDTOP=$(REL)..$(S)..
+
+##WIN32##LOCALINCLUDES=-I$(BUILDTOP)\util\windows\
+
+SRCS=kvno.c
+
+##WIN32##VERSIONRC = $(BUILDTOP)\windows\version.rc
+##WIN32##RCFLAGS=$(CPPFLAGS) -I$(top_srcdir) -D_WIN32 -DRES_ONLY
+
+##WIN32##KVNO=$(OUTPRE)kvno.exe
+
+##WIN32##EXERES=$(KVNO:.exe=.res)
+
+##WIN32##$(EXERES): $(VERSIONRC)
+##WIN32## $(RC) $(RCFLAGS) -DKVNO_APP -fo $@ -r $**
+
+all-unix: kvno
+
+##WIN32##all-windows: $(KVNO)
+
+kvno: kvno.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ kvno.o $(KRB5_BASE_LIBS)
+
+##WIN32##$(KVNO): $(OUTPRE)kvno.obj $(SLIB) $(KLIB) $(CLIB) $(EXERES)
+##WIN32## link $(EXE_LINKOPTS) /out:$@ $**
+##WIN32## $(_VC_MANIFEST_EMBED_EXE)
+
+clean-unix::
+ $(RM) kvno.o kvno
+
+install-unix:
+ for f in kvno; do \
+ $(INSTALL_PROGRAM) $$f \
+ $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+ done
diff --git a/src/clients/kvno/deps b/src/clients/kvno/deps
new file mode 100644
index 000000000000..e9d597a8a6ca
--- /dev/null
+++ b/src/clients/kvno/deps
@@ -0,0 +1,13 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kvno.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kvno.c
diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c
new file mode 100644
index 000000000000..80bee59e2337
--- /dev/null
+++ b/src/clients/kvno/kvno.c
@@ -0,0 +1,354 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "k5-platform.h"
+#include <locale.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+
+extern int optind;
+extern char *optarg;
+
+static char *prog;
+
+static void xusage()
+{
+ fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog);
+ fprintf(stderr, _("\t[-k keytab] [-S sname] [-U for_user [-P]]\n"));
+ fprintf(stderr, _("\tservice1 service2 ...\n"));
+ exit(1);
+}
+
+int quiet = 0;
+
+static void do_v5_kvno (int argc, char *argv[],
+ char *ccachestr, char *etypestr, char *keytab_name,
+ char *sname, int canon, int unknown,
+ char *for_user, int proxy);
+
+#include <com_err.h>
+static void extended_com_err_fn (const char *, errcode_t, const char *,
+ va_list);
+
+int main(int argc, char *argv[])
+{
+ int option;
+ char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL;
+ char *sname = NULL, *for_user = NULL;
+ int canon = 0, unknown = 0, proxy = 0;
+
+ setlocale(LC_ALL, "");
+ set_com_err_hook (extended_com_err_fn);
+
+ prog = strrchr(argv[0], '/');
+ prog = prog ? (prog + 1) : argv[0];
+
+ while ((option = getopt(argc, argv, "uCc:e:hk:qPS:U:")) != -1) {
+ switch (option) {
+ case 'C':
+ canon = 1;
+ break;
+ case 'c':
+ ccachestr = optarg;
+ break;
+ case 'e':
+ etypestr = optarg;
+ break;
+ case 'h':
+ xusage();
+ break;
+ case 'k':
+ keytab_name = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'P':
+ proxy = 1; /* S4U2Proxy - constrained delegation */
+ break;
+ case 'S':
+ sname = optarg;
+ if (unknown == 1){
+ fprintf(stderr,
+ _("Options -u and -S are mutually exclusive\n"));
+ xusage();
+ }
+ break;
+ case 'u':
+ unknown = 1;
+ if (sname){
+ fprintf(stderr,
+ _("Options -u and -S are mutually exclusive\n"));
+ xusage();
+ }
+ break;
+ case 'U':
+ for_user = optarg; /* S4U2Self - protocol transition */
+ break;
+ default:
+ xusage();
+ break;
+ }
+ }
+
+ if (proxy) {
+ if (keytab_name == NULL) {
+ fprintf(stderr, _("Option -P (constrained delegation) "
+ "requires keytab to be specified\n"));
+ xusage();
+ } else if (for_user == NULL) {
+ fprintf(stderr, _("Option -P (constrained delegation) requires "
+ "option -U (protocol transition)\n"));
+ xusage();
+ }
+ }
+
+ if ((argc - optind) < 1)
+ xusage();
+
+ do_v5_kvno(argc - optind, argv + optind,
+ ccachestr, etypestr, keytab_name, sname,
+ canon, unknown, for_user, proxy);
+ return 0;
+}
+
+#include <k5-int.h>
+static krb5_context context;
+static void extended_com_err_fn (const char *myprog, errcode_t code,
+ const char *fmt, va_list args)
+{
+ const char *emsg;
+ emsg = krb5_get_error_message (context, code);
+ fprintf (stderr, "%s: %s ", myprog, emsg);
+ krb5_free_error_message (context, emsg);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, "\n");
+}
+
+static void do_v5_kvno (int count, char *names[],
+ char * ccachestr, char *etypestr, char *keytab_name,
+ char *sname, int canon, int unknown, char *for_user,
+ int proxy)
+{
+ krb5_error_code ret;
+ int i, errors;
+ krb5_enctype etype;
+ krb5_ccache ccache;
+ krb5_principal me;
+ krb5_creds in_creds;
+ krb5_keytab keytab = NULL;
+ krb5_principal for_user_princ = NULL;
+ krb5_flags options;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ com_err(prog, ret, _("while initializing krb5 library"));
+ exit(1);
+ }
+
+ if (etypestr) {
+ ret = krb5_string_to_enctype(etypestr, &etype);
+ if (ret) {
+ com_err(prog, ret, _("while converting etype"));
+ exit(1);
+ }
+ } else {
+ etype = 0;
+ }
+
+ if (ccachestr)
+ ret = krb5_cc_resolve(context, ccachestr, &ccache);
+ else
+ ret = krb5_cc_default(context, &ccache);
+ if (ret) {
+ com_err(prog, ret, _("while opening ccache"));
+ exit(1);
+ }
+
+ if (keytab_name) {
+ ret = krb5_kt_resolve(context, keytab_name, &keytab);
+ if (ret) {
+ com_err(prog, ret, _("resolving keytab %s"), keytab_name);
+ exit(1);
+ }
+ }
+
+ if (for_user) {
+ ret = krb5_parse_name_flags(context, for_user,
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE,
+ &for_user_princ);
+ if (ret) {
+ com_err(prog, ret, _("while parsing principal name %s"), for_user);
+ exit(1);
+ }
+ }
+
+ ret = krb5_cc_get_principal(context, ccache, &me);
+ if (ret) {
+ com_err(prog, ret, _("while getting client principal name"));
+ exit(1);
+ }
+
+ errors = 0;
+
+ options = 0;
+ if (canon)
+ options |= KRB5_GC_CANONICALIZE;
+
+ for (i = 0; i < count; i++) {
+ krb5_principal server = NULL;
+ krb5_ticket *ticket = NULL;
+ krb5_creds *out_creds = NULL;
+ char *princ = NULL;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+
+ if (sname != NULL) {
+ ret = krb5_sname_to_principal(context, names[i],
+ sname, KRB5_NT_SRV_HST,
+ &server);
+ } else {
+ ret = krb5_parse_name(context, names[i], &server);
+ }
+ if (ret) {
+ if (!quiet) {
+ com_err(prog, ret, _("while parsing principal name %s"),
+ names[i]);
+ }
+ goto error;
+ }
+ if (unknown == 1) {
+ krb5_princ_type(context, server) = KRB5_NT_UNKNOWN;
+ }
+
+ ret = krb5_unparse_name(context, server, &princ);
+ if (ret) {
+ com_err(prog, ret, _("while formatting parsed principal name for "
+ "'%s'"), names[i]);
+ goto error;
+ }
+
+ in_creds.keyblock.enctype = etype;
+
+ if (for_user) {
+ if (!proxy &&
+ !krb5_principal_compare(context, me, server)) {
+ com_err(prog, EINVAL,
+ _("client and server principal names must match"));
+ goto error;
+ }
+
+ in_creds.client = for_user_princ;
+ in_creds.server = me;
+
+ ret = krb5_get_credentials_for_user(context, options, ccache,
+ &in_creds, NULL, &out_creds);
+ } else {
+ in_creds.client = me;
+ in_creds.server = server;
+ ret = krb5_get_credentials(context, options, ccache,
+ &in_creds, &out_creds);
+ }
+
+ if (ret) {
+ com_err(prog, ret, _("while getting credentials for %s"), princ);
+ goto error;
+ }
+
+ /* we need a native ticket */
+ ret = krb5_decode_ticket(&out_creds->ticket, &ticket);
+ if (ret) {
+ com_err(prog, ret, _("while decoding ticket for %s"), princ);
+ goto error;
+ }
+
+ if (keytab) {
+ ret = krb5_server_decrypt_ticket_keytab(context, keytab, ticket);
+ if (ret) {
+ if (!quiet) {
+ fprintf(stderr, "%s: kvno = %d, keytab entry invalid\n",
+ princ, ticket->enc_part.kvno);
+ }
+ com_err(prog, ret, _("while decrypting ticket for %s"), princ);
+ goto error;
+ }
+ if (!quiet) {
+ printf(_("%s: kvno = %d, keytab entry valid\n"),
+ princ, ticket->enc_part.kvno);
+ }
+ if (proxy) {
+ krb5_free_creds(context, out_creds);
+ out_creds = NULL;
+
+ in_creds.client = ticket->enc_part2->client;
+ in_creds.server = server;
+
+ ret = krb5_get_credentials_for_proxy(context,
+ KRB5_GC_CANONICALIZE,
+ ccache,
+ &in_creds,
+ ticket,
+ &out_creds);
+ if (ret) {
+ com_err(prog, ret,
+ _("%s: constrained delegation failed"), princ);
+ goto error;
+ }
+ }
+ } else {
+ if (!quiet)
+ printf(_("%s: kvno = %d\n"), princ, ticket->enc_part.kvno);
+ }
+
+ continue;
+
+ error:
+ if (server != NULL)
+ krb5_free_principal(context, server);
+ if (ticket != NULL)
+ krb5_free_ticket(context, ticket);
+ if (out_creds != NULL)
+ krb5_free_creds(context, out_creds);
+ if (princ != NULL)
+ krb5_free_unparsed_name(context, princ);
+ errors++;
+ }
+
+ if (keytab)
+ krb5_kt_close(context, keytab);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, for_user_princ);
+ krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+
+ if (errors)
+ exit(1);
+
+ exit(0);
+}