diff options
Diffstat (limited to 'src/clients')
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, ¤ttime))){ + 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, ¶ms))){ + 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, ¶ms))){ + 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); +} |