summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2011-09-05 16:26:48 +0000
committerGreg Hudson <ghudson@mit.edu>2011-09-05 16:26:48 +0000
commit916555623ea3c0cd8976718f0b989280df9260ce (patch)
treeb93331f1e121874fae9e6e31eabe9c46f0ce10a4
parent45360c9688ca963f75a2480f2cf818424fc3dc7b (diff)
downloadkrb5-916555623ea3c0cd8976718f0b989280df9260ce.tar.gz
krb5-916555623ea3c0cd8976718f0b989280df9260ce.tar.xz
krb5-916555623ea3c0cd8976718f0b989280df9260ce.zip
Add ccache collection support to tools
* "kdestroy -A" destroys all caches in collection. * "kinit princ" searches the collection for a matching cache and overwrites it, or creates a new cache in the collection, if the type of the default cache is collection-enabled. The chosen cache also becomes the primary cache for the collection. * "klist -l" lists (in summary form) the caches in the collection. * "klist -A" lists the content of all of the caches in the collection. * "kswitch -c cache" (new command) makes cache the primary cache. * "kswitch -p princ" makes the cache for princ the primary cache. ticket: 6956 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25157 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--doc/rst_source/conf.py1
-rw-r--r--doc/rst_source/krb_users/user_commands/index.rst1
-rw-r--r--doc/rst_source/krb_users/user_commands/kdestroy.rst14
-rw-r--r--doc/rst_source/krb_users/user_commands/kinit.rst20
-rw-r--r--doc/rst_source/krb_users/user_commands/klist.rst19
-rw-r--r--doc/rst_source/krb_users/user_commands/kswitch.rst53
-rw-r--r--src/clients/Makefile.in4
-rw-r--r--src/clients/kdestroy/kdestroy.M12
-rw-r--r--src/clients/kdestroy/kdestroy.c34
-rw-r--r--src/clients/kinit/kinit.M15
-rw-r--r--src/clients/kinit/kinit.c80
-rw-r--r--src/clients/klist/klist.M17
-rw-r--r--src/clients/klist/klist.c171
-rw-r--r--src/clients/kswitch/Makefile.in39
-rw-r--r--src/clients/kswitch/deps14
-rw-r--r--src/clients/kswitch/kswitch.M61
-rw-r--r--src/clients/kswitch/kswitch.c127
-rw-r--r--src/configure.in2
-rw-r--r--src/lib/krb5/ccache/cccursor.c2
-rw-r--r--src/tests/Makefile.in1
-rw-r--r--src/tests/t_cccol.py77
-rw-r--r--src/util/k5test.py2
-rw-r--r--src/util/testrealm.py1
23 files changed, 700 insertions, 67 deletions
diff --git a/doc/rst_source/conf.py b/doc/rst_source/conf.py
index 6ea2c6d338..abc63e9ce1 100644
--- a/doc/rst_source/conf.py
+++ b/doc/rst_source/conf.py
@@ -221,6 +221,7 @@ man_pages = [
('krb_users/user_commands/kinit', 'kinit', u'obtain and cache Kerberos ticket-granting ticket', [u'MIT'], 1),
('krb_users/user_commands/klist', 'klist', u'list cached Kerberos tickets', [u'MIT'], 1),
('krb_users/user_commands/kdestroy', 'kdestroy', u'destroy Kerberos tickets', [u'MIT'], 1),
+ ('krb_users/user_commands/kswitch', 'kswitch', u'switch primary ticket cache', [u'MIT'], 1),
('krb_users/user_commands/kpasswd', 'kpasswd', u'change a user\'s Kerberos password', [u'MIT'], 1),
('krb_users/user_commands/kvno', 'kvno', u'print key version numbers of Kerberos principals', [u'MIT'], 1),
('krb_users/user_commands/ksu', 'ksu', u'Kerberized super-user', [u'MIT'], 1),
diff --git a/doc/rst_source/krb_users/user_commands/index.rst b/doc/rst_source/krb_users/user_commands/index.rst
index 5232eb818e..4c68c08993 100644
--- a/doc/rst_source/krb_users/user_commands/index.rst
+++ b/doc/rst_source/krb_users/user_commands/index.rst
@@ -10,6 +10,7 @@ User commands
kinit.rst
klist.rst
kdestroy.rst
+ kswitch.rst
kpasswd.rst
kvno.rst
ksu.rst
diff --git a/doc/rst_source/krb_users/user_commands/kdestroy.rst b/doc/rst_source/krb_users/user_commands/kdestroy.rst
index 78a3014a97..b86137afe2 100644
--- a/doc/rst_source/krb_users/user_commands/kdestroy.rst
+++ b/doc/rst_source/krb_users/user_commands/kdestroy.rst
@@ -5,6 +5,7 @@ SYNOPSIS
~~~~~~~~~~~~~
*kdestroy*
+ [**-A**]
[**-q**]
[**-c** *cache_name*]
@@ -21,6 +22,10 @@ cache is not specified, the default credentials cache is destroyed.
OPTIONS
~~~~~~~~~~~~~
+ **-A**
+ Destroys all caches in the collection, if a cache collection is
+ available.
+
**-q**
Run quietly. Normally *kdestroy* beeps if it fails to destroy the user's tickets. The *-q* flag suppresses this behavior.
@@ -45,7 +50,14 @@ ENVIRONMENT
*kdestroy* uses the following environment variables:
- **KRB5CCNAME** - Location of the Kerberos 5 credentials (ticket) cache.
+ **KRB5CCNAME**
+ Location of the default Kerberos 5 credentials (ticket)
+ cache, in the form *type*:*residual*. If no type prefix is
+ present, the **FILE** type is assumed. The type of the
+ default cache may determine the availability of a cache
+ collection; for instance, a default cache of type **DIR**
+ causes caches within the directory to be present in the
+ collection.
FILES
diff --git a/doc/rst_source/krb_users/user_commands/kinit.rst b/doc/rst_source/krb_users/user_commands/kinit.rst
index 3e816d3529..49a2564707 100644
--- a/doc/rst_source/krb_users/user_commands/kinit.rst
+++ b/doc/rst_source/krb_users/user_commands/kinit.rst
@@ -129,8 +129,15 @@ OPTIONS
use *cache_name* as the Kerberos 5 credentials (ticket) cache name and location;
if this option is not used, the default cache name and location are used.
- The default credentials cache may vary between systems. If the **KRB5CCNAME** environment variable is set, its
- value is used to name the default ticket cache. Any existing contents of the cache are destroyed by kinit.
+ The default credentials cache may vary between systems. If
+ the **KRB5CCNAME** environment variable is set, its value is
+ used to name the default ticket cache. If a principal name
+ is specified and the type of the default credentials cache
+ supports a collection (such as the DIR type), an existing
+ cache containing credentials for the principal is selected
+ or a new one is created and becomes the new primary cache.
+ Otherwise, any existing contents of the default cache are
+ destroyed by kinit.
**-S** *service_name*
specify an alternate service name to use when getting initial tickets.
@@ -162,7 +169,14 @@ ENVIRONMENT
*kinit* uses the following environment variables:
- **KRB5CCNAME** Location of the Kerberos 5 credentials (ticket) cache.
+ **KRB5CCNAME**
+ Location of the default Kerberos 5 credentials (ticket)
+ cache, in the form *type*:*residual*. If no type prefix is
+ present, the **FILE** type is assumed. The type of the
+ default cache may determine the availability of a cache
+ collection; for instance, a default cache of type **DIR**
+ causes caches within the directory to be present in the
+ collection.
FILES
diff --git a/doc/rst_source/krb_users/user_commands/klist.rst b/doc/rst_source/krb_users/user_commands/klist.rst
index 86a514c8ed..3886a9ff0e 100644
--- a/doc/rst_source/krb_users/user_commands/klist.rst
+++ b/doc/rst_source/krb_users/user_commands/klist.rst
@@ -7,7 +7,7 @@ SYNOPSIS
**klist**
[**-e**]
- [[**-c**] [**-f**] [**-s**] [**-a** [**-n**]]]
+ [[**-c**] [**-l**] [**-A**] [**-f**] [**-s**] [**-a** [**-n**]]]
[**-k** [**-t**] [**-K**]]
[**-V**]
[*cache_name* | *keytab_name*]
@@ -26,6 +26,14 @@ OPTIONS
Displays the encryption types of the session key and the ticket for each credential in the credential cache,
or each key in the keytab file.
+ **-l**
+ If a cache collection is available, displays a table
+ summarizing the caches present in the collection.
+
+ **-A**
+ If a cache collection is available, displays the contents of
+ all of the caches in the collection.
+
**-c**
List tickets held in a credentials cache. This is the default if neither *-c* nor *-k* is specified.
@@ -79,7 +87,14 @@ ENVIRONMENT
*klist* uses the following environment variables:
- **KRB5CCNAME** - Location of the Kerberos 5 credentials (ticket) cache.
+ **KRB5CCNAME**
+ Location of the default Kerberos 5 credentials (ticket)
+ cache, in the form *type*:*residual*. If no type prefix is
+ present, the **FILE** type is assumed. The type of the
+ default cache may determine the availability of a cache
+ collection; for instance, a default cache of type **DIR**
+ causes caches within the directory to be present in the
+ collection.
FILES
diff --git a/doc/rst_source/krb_users/user_commands/kswitch.rst b/doc/rst_source/krb_users/user_commands/kswitch.rst
new file mode 100644
index 0000000000..fad2d9aee8
--- /dev/null
+++ b/doc/rst_source/krb_users/user_commands/kswitch.rst
@@ -0,0 +1,53 @@
+kswitch - switch primary credential cache
+=========================================
+
+
+SYNOPSIS
+~~~~~~~~
+
+**kswitch** {**-c** *cachename* | **-p** *principal*}
+
+DESCRIPTION
+~~~~~~~~~~~
+
+*kswitch* makes the specified credential cache the primary cache for
+the collection, if a cache collection is available.
+
+
+OPTIONS
+~~~~~~~
+
+ **-c** *cachename*
+ Directly specifies the credential cache to be made primary.
+
+ **-p** *principal*
+ Causes the cache collection to be searched for a cache
+ containing credentials for *principal*. If one is found,
+ that collection is made primary.
+
+
+ENVIRONMENT
+~~~~~~~~~~~
+
+*kswitch* uses the following environment variables:
+
+ **KRB5CCNAME**
+ Location of the default Kerberos 5 credentials (ticket)
+ cache, in the form *type*:*residual*. If no type prefix is
+ present, the **FILE** type is assumed. The type of the
+ default cache may determine the availability of a cache
+ collection; for instance, a default cache of type **DIR**
+ causes caches within the directory to be present in the
+ collection.
+
+
+FILES
+~~~~~
+
+/tmp/krb5cc_[uid] - Default location of Kerberos 5 credentials cache ([*uid*] is the decimal UID of the user).
+
+
+SEE ALSO
+~~~~~~~~
+
+kinit(1), kdestroy(1), klist(1), kerberos(1)
diff --git a/src/clients/Makefile.in b/src/clients/Makefile.in
index 4ae8361946..4beb32a618 100644
--- a/src/clients/Makefile.in
+++ b/src/clients/Makefile.in
@@ -1,7 +1,7 @@
mydir=clients
BUILDTOP=$(REL)..
-SUBDIRS= klist kinit kdestroy kpasswd ksu kvno kcpytkt kdeltkt
-WINSUBDIRS= klist kinit kdestroy kpasswd kvno kcpytkt kdeltkt
+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/kdestroy/kdestroy.M b/src/clients/kdestroy/kdestroy.M
index ada2ae3dcf..4deaa5fde2 100644
--- a/src/clients/kdestroy/kdestroy.M
+++ b/src/clients/kdestroy/kdestroy.M
@@ -26,7 +26,7 @@
kdestroy \- destroy Kerberos tickets
.SH SYNOPSIS
.B kdestroy
-[\fB\-q\fP] [\fB\-c\fP \fIcache_name]
+[\fB\-A\fP] [\fB\-q\fP] [\fB\-c\fP \fIcache_name]
.br
.SH DESCRIPTION
The
@@ -37,6 +37,9 @@ the credentials cache is not specified, the default credentials cache is
destroyed.
.SH OPTIONS
.TP
+.B \-A
+Destroys all caches in the collection, if a cache collection is
+available.
.B \-q
Run quietly. Normally
.B kdestroy
@@ -65,7 +68,12 @@ file, so that your tickets are destroyed automatically when you log out.
uses the following environment variables:
.TP "\w'.SM KRB5CCNAME\ \ 'u"
.SM KRB5CCNAME
-Location of the Kerberos 5 credentials (ticket) cache.
+Location of the default Kerberos 5 credentials (ticket) cache, in the
+form \fItype\fP:\fIresidual\fP. If no type prefix is present, the
+\fBFILE\fP type is assumed. The type of the default cache may
+determine the availability of a cache collection; for instance, a
+default cache of type \fBDIR\fP causes caches within the directory to
+be present in the collection.
.SH FILES
.TP "\w'/tmp/krb5cc_[uid]\ \ 'u"
/tmp/krb5cc_[uid]
diff --git a/src/clients/kdestroy/kdestroy.c b/src/clients/kdestroy/kdestroy.c
index 73ce04459b..abe49533d4 100644
--- a/src/clients/kdestroy/kdestroy.c
+++ b/src/clients/kdestroy/kdestroy.c
@@ -55,7 +55,8 @@ static void usage()
{
#define KRB_AVAIL_STRING(x) ((x)?"available":"not available")
- fprintf(stderr, _("Usage: %s [-q] [-c cache_name]\n"), progname);
+ 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);
@@ -70,16 +71,21 @@ main(argc, argv)
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_MESSAGES, "");
progname = GET_PROGNAME(argv[0]);
- while ((c = getopt(argc, argv, "54qc:")) != -1) {
+ while ((c = getopt(argc, argv, "54Aqc:")) != -1) {
switch (c) {
+ case 'A':
+ all = 1;
+ break;
case 'q':
quiet = 1;
break;
@@ -117,6 +123,30 @@ main(argc, argv)
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);
+ return 0;
+ }
+
if (cache_name) {
code = krb5_cc_resolve (kcontext, cache_name, &cache);
if (code != 0) {
diff --git a/src/clients/kinit/kinit.M b/src/clients/kinit/kinit.M
index 3d95a62b36..0a919c09f1 100644
--- a/src/clients/kinit/kinit.M
+++ b/src/clients/kinit/kinit.M
@@ -182,7 +182,11 @@ option is not used, the default cache name and location are used.
The default credentials cache may vary between systems. If the
.B KRB5CCNAME
environment variable is set, its value is used to name the default
-ticket cache. Any existing contents of the cache are destroyed by
+ticket cache. If a principal name is specified and the type of the
+default credentials cache supports a collection (such as the DIR
+type), an existing cache containing credentials for the principal is
+selected or a new one is created and becomes the new primary cache.
+Otherwise, any existing contents of the default cache are destroyed by
.IR kinit .
.TP
\fB\-S\fP \fIservice_name\fP
@@ -215,7 +219,12 @@ pre-authentication mechanism:
uses the following environment variables:
.TP "\w'.SM KRB5CCNAME\ \ 'u"
.SM KRB5CCNAME
-Location of the Kerberos 5 credentials (ticket) cache.
+Location of the default Kerberos 5 credentials (ticket) cache, in the
+form \fItype\fP:\fIresidual\fP. If no type prefix is present, the
+\fBFILE\fP type is assumed. The type of the default cache may
+determine the availability of a cache collection; for instance, a
+default cache of type \fBDIR\fP causes caches within the directory to
+be present in the collection.
.SH FILES
.TP "\w'/tmp/krb5cc_[uid]\ \ 'u"
/tmp/krb5cc_[uid]
@@ -227,4 +236,4 @@ default location for the local host's
.B keytab
file.
.SH SEE ALSO
-klist(1), kdestroy(1), kerberos(1)
+klist(1), kdestroy(1), kswitch(1), kerberos(1)
diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c
index 802229ae00..1bf7564a12 100644
--- a/src/clients/kinit/kinit.c
+++ b/src/clients/kinit/kinit.c
@@ -133,6 +133,7 @@ struct k5_data
krb5_ccache cc;
krb5_principal me;
char* name;
+ krb5_boolean switch_to_cache;
};
#ifdef GETOPT_LONG
@@ -438,6 +439,8 @@ k5_begin(opts, k5)
{
krb5_error_code code = 0;
int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
+ krb5_ccache defcache;
+ const char *deftype;
code = krb5_init_context(&k5->ctx);
if (code) {
@@ -445,8 +448,18 @@ k5_begin(opts, k5)
return 0;
}
errctx = k5->ctx;
- if (opts->k5_cache_name)
- {
+
+ /* Parse specified principal name now if we got one. */
+ if (opts->principal_name) {
+ if ((code = krb5_parse_name_flags(k5->ctx, opts->principal_name,
+ flags, &k5->me))) {
+ com_err(progname, code, _("when parsing name %s"),
+ opts->principal_name);
+ return 0;
+ }
+ }
+
+ if (opts->k5_cache_name) {
code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc);
if (code != 0) {
com_err(progname, code, _("resolving ccache %s"),
@@ -457,31 +470,48 @@ k5_begin(opts, k5)
fprintf(stderr, _("Using specified cache: %s\n"),
opts->k5_cache_name);
}
- }
- else
- {
- if ((code = krb5_cc_default(k5->ctx, &k5->cc))) {
+ } else {
+ if ((code = krb5_cc_default(k5->ctx, &defcache))) {
com_err(progname, code, _("while getting default ccache"));
return 0;
}
- if (opts->verbose) {
- fprintf(stderr, _("Using default cache: %s\n"),
- krb5_cc_get_name(k5->ctx, k5->cc));
+ deftype = krb5_cc_get_type(k5->ctx, defcache);
+ if (k5->me != NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
+ /* Use an existing cache for the specified principal if we can. */
+ code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->cc);
+ if (code != 0 && code != KRB5_CC_NOTFOUND) {
+ com_err(progname, code, _("while searching for ccache for %s"),
+ opts->principal_name);
+ krb5_cc_close(k5->ctx, defcache);
+ return 0;
+ }
+ if (code == KRB5_CC_NOTFOUND) {
+ code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->cc);
+ if (code) {
+ com_err(progname, code, _("while generating new ccache"));
+ krb5_cc_close(k5->ctx, defcache);
+ return 0;
+ }
+ if (opts->verbose) {
+ fprintf(stderr, _("Using new cache: %s\n"),
+ krb5_cc_get_name(k5->ctx, k5->cc));
+ }
+ } else if (opts->verbose) {
+ fprintf(stderr, _("Using existing cache: %s\n"),
+ krb5_cc_get_name(k5->ctx, k5->cc));
+ }
+ krb5_cc_close(k5->ctx, defcache);
+ k5->switch_to_cache = 1;
+ } else {
+ k5->cc = defcache;
+ if (opts->verbose) {
+ fprintf(stderr, _("Using default cache: %s\n"),
+ krb5_cc_get_name(k5->ctx, k5->cc));
+ }
}
}
- if (opts->principal_name)
- {
- /* Use specified name */
- if ((code = krb5_parse_name_flags(k5->ctx, opts->principal_name,
- flags, &k5->me))) {
- com_err(progname, code, _("when parsing name %s"),
- opts->principal_name);
- return 0;
- }
- }
- else
- {
+ if (!k5->me) {
/* No principal name specified */
if (opts->anonymous) {
char *defrealm;
@@ -756,6 +786,14 @@ k5_kinit(opts, k5)
}
notix = 0;
+ if (k5->switch_to_cache) {
+ code = krb5_cc_switch(k5->ctx, k5->cc);
+ if (code) {
+ com_err(progname, code, _("while switching to new ccache"));
+ goto cleanup;
+ }
+ }
+
cleanup:
if (options)
krb5_get_init_creds_opt_free(k5->ctx, options);
diff --git a/src/clients/klist/klist.M b/src/clients/klist/klist.M
index 0f9bb03cc6..32aed10ac9 100644
--- a/src/clients/klist/klist.M
+++ b/src/clients/klist/klist.M
@@ -25,7 +25,7 @@
.SH NAME
klist \- list cached Kerberos tickets
.SH SYNOPSIS
-\fBklist\fP [\fB\-e\fP] [[\fB\-c\fP] [\fB\-f\fP]
+\fBklist\fP [\fB\-e\fP] [[\fB\-c\fP] [\fB\-l\fP] [\fB\-A\fP] [\fB\-f\fP]
[\fB\-s\fP] [\fB\-a\fP [\fB\-n\fP]]]
[\fB\-k\fP [\fB\-t\fP] [\fB\-K\fP]]
[\fIcache_name\fP | \fIkeytab_name\fP]
@@ -50,6 +50,14 @@ nor
.B \-k
is specified.
.TP
+.B \-l
+If a cache collection is available, displays a table summarizing the
+caches present in the collection.
+.TP
+.B \-A
+If a cache collection is available, displays the contents of all of
+the caches in the collection.
+.TP
.B \-f
shows the flags present in the credentials, using the following
abbreviations:
@@ -119,7 +127,12 @@ ticket cache.
uses the following environment variables:
.TP "\w'.SM KRB5CCNAME\ \ 'u"
.SM KRB5CCNAME
-Location of the Kerberos 5 credentials (ticket) cache.
+Location of the default Kerberos 5 credentials (ticket) cache, in the
+form \fItype\fP:\fIresidual\fP. If no type prefix is present, the
+\fBFILE\fP type is assumed. The type of the default cache may
+determine the availability of a cache collection; for instance, a
+default cache of type \fBDIR\fP causes caches within the directory to
+be present in the collection.
.SH FILES
.TP "\w'/tmp/krb5cc_[uid]\ \ 'u"
/tmp/krb5cc_[uid]
diff --git a/src/clients/klist/klist.c b/src/clients/klist/klist.c
index d191f85c82..4757f381a4 100644
--- a/src/clients/klist/klist.c
+++ b/src/clients/klist/klist.c
@@ -54,7 +54,7 @@ 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;
+int show_adtype = 0, show_all = 0, list_all = 0;
char *defname;
char *progname;
krb5_int32 now;
@@ -65,7 +65,11 @@ krb5_context kcontext;
char * etype_string (krb5_enctype );
void show_credential (krb5_creds *);
-void do_ccache (char *);
+void list_all_ccaches (void);
+int list_ccache (krb5_ccache);
+void show_all_ccaches (void);
+void do_ccache_name (char *);
+int do_ccache (krb5_ccache);
void do_keytab (char *);
void printtime (time_t);
void one_addr (krb5_address *);
@@ -79,11 +83,13 @@ static void usage()
{
#define KRB_AVAIL_STRING(x) ((x)?"available":"not available")
- fprintf(stderr, _("Usage: %s [-e] [-V] [[-c] [-d] [-f] [-s] [-a [-n]]] "
- "[-k [-t] [-K]] [name]\n"), progname);
+ 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-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"));
@@ -115,7 +121,7 @@ main(argc, argv)
name = NULL;
mode = DEFAULT;
/* V=version so v can be used for verbose later if desired. */
- while ((c = getopt(argc, argv, "dfetKsnack45V")) != -1) {
+ while ((c = getopt(argc, argv, "dfetKsnack45lAV")) != -1) {
switch (c) {
case 'd':
show_adtype = 1;
@@ -155,6 +161,12 @@ main(argc, argv)
break;
case '5':
break;
+ case 'l':
+ list_all = 1;
+ break;
+ case 'A':
+ show_all = 1;
+ break;
case 'V':
print_version = 1;
break;
@@ -171,8 +183,11 @@ main(argc, argv)
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)
+ if (show_flags || status_only || show_addresses ||
+ show_all || list_all)
usage();
}
@@ -213,8 +228,12 @@ main(argc, argv)
exit(1);
}
- if (mode == DEFAULT || mode == CCACHE)
- do_ccache(name);
+ 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);
}
@@ -306,20 +325,103 @@ void do_keytab(name)
}
exit(0);
}
-void do_ccache(name)
- char *name;
+
+void
+list_all_ccaches(void)
{
- krb5_ccache cache = NULL;
- krb5_cc_cursor cur;
- krb5_creds creds;
- krb5_principal princ;
- krb5_flags flags;
krb5_error_code code;
- int exit_status = 0;
+ 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;
+
+ status_only = 1;
+ expired = do_ccache(cache);
+
+ printf("%-30.30s %s", princname, ccname);
+ if (expired)
+ printf(" %s", _("(Expired)"));
+ printf("\n");
+
+ status = 0;
+cleanup:
+ krb5_free_principal(kcontext, princ);
+ free(princname);
+ free(ccname);
+ return status;
+}
- if (status_only)
- /* exit_status is set back to 0 if a valid tgt is found */
- exit_status = 1;
+void
+show_all_ccaches(void)
+{
+ krb5_error_code code;
+ krb5_ccache cache;
+ krb5_cccol_cursor cursor;
+ krb5_boolean first;
+ 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);
+ }
+ exit_status = 1;
+ first = TRUE;
+ while (!(code = krb5_cccol_cursor_next(kcontext, cursor, &cache)) &&
+ cache != NULL) {
+ if (!first)
+ printf("\n");
+ first = FALSE;
+ exit_status = do_ccache(cache) && 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))) {
@@ -335,6 +437,21 @@ void do_ccache(name)
exit(1);
}
}
+ exit(do_ccache(cache));
+}
+
+int
+do_ccache(krb5_ccache cache)
+{
+ krb5_cc_cursor cur;
+ krb5_creds creds;
+ krb5_principal princ;
+ krb5_flags flags;
+ krb5_error_code code;
+ int exit_status = 0;
+
+ /* For status_only, exit_status is reset to 0 if a valid tgt is found. */
+ exit_status = (status_only) ? 1 : 0;
flags = 0; /* turns off OPENCLOSE mode */
if ((code = krb5_cc_set_flags(kcontext, cache, flags))) {
@@ -355,17 +472,17 @@ void do_ccache(name)
krb5_cc_get_type(kcontext, cache),
krb5_cc_get_name(kcontext, cache));
}
- exit(1);
+ return 1;
}
if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) {
if (!status_only)
com_err(progname, code, _("while retrieving principal name"));
- exit(1);
+ return 1;
}
if ((code = krb5_unparse_name(kcontext, princ, &defname))) {
if (!status_only)
com_err(progname, code, _("while unparsing principal name"));
- exit(1);
+ return 1;
}
if (!status_only) {
printf(_("Ticket cache: %s:%s\nDefault principal: %s\n\n"),
@@ -383,7 +500,7 @@ void do_ccache(name)
if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) {
if (!status_only)
com_err(progname, code, _("while starting to retrieve tickets"));
- exit(1);
+ return 1;
}
while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
if (krb5_is_config_principal(kcontext, creds.server))
@@ -404,23 +521,23 @@ void do_ccache(name)
if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) {
if (!status_only)
com_err(progname, code, _("while finishing ticket retrieval"));
- exit(1);
+ return 1;
}
flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */
if ((code = krb5_cc_set_flags(kcontext, cache, flags))) {
if (!status_only)
com_err(progname, code, _("while closing ccache"));
- exit(1);
+ return 1;
}
#ifdef KRB5_KRB4_COMPAT
if (name == NULL && !status_only)
do_v4_ccache(0);
#endif
- exit(exit_status);
+ return exit_status;
} else {
if (!status_only)
com_err(progname, code, _("while retrieving a ticket"));
- exit(1);
+ return 1;
}
}
diff --git a/src/clients/kswitch/Makefile.in b/src/clients/kswitch/Makefile.in
new file mode 100644
index 0000000000..c82d8d6090
--- /dev/null
+++ b/src/clients/kswitch/Makefile.in
@@ -0,0 +1,39 @@
+mydir=clients$(S)kswitch
+BUILDTOP=$(REL)..$(S)..
+DEFS=
+
+SRCS=kswitch.c
+
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+
+##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 $(BUILDTOP)\util\windows\$(OUTPRE)getopt.obj $(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)'`; \
+ $(INSTALL_DATA) $(srcdir)/$$f.M \
+ $(DESTDIR)$(CLIENT_MANDIR)/`echo $$f|sed '$(transform)'`.1; \
+ done
diff --git a/src/clients/kswitch/deps b/src/clients/kswitch/deps
new file mode 100644
index 0000000000..734317139e
--- /dev/null
+++ b/src/clients/kswitch/deps
@@ -0,0 +1,14 @@
+#
+# 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/krb5/preauth_plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kswitch.c
diff --git a/src/clients/kswitch/kswitch.M b/src/clients/kswitch/kswitch.M
new file mode 100644
index 0000000000..4076975517
--- /dev/null
+++ b/src/clients/kswitch/kswitch.M
@@ -0,0 +1,61 @@
+.\" clients/kswitch/kswitch.M
+.\"
+.\" Copyright 2011 by the Massachusetts Institute of Technology.
+.\"
+.\" 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.
+.\" "
+.TH KSWITCH 1
+.SH NAME
+kswitch \- switch primary credential cache
+.SH SYNOPSIS
+\fBkswitch\fP {\fB\-c\fP \fIcachename\fP | \fB\-p\fP \fIprincipal\fP}
+.SH DESCRIPTION
+.I kswitch
+makes the specified credential cache the primary cache for the
+collection, if a cache collection is available.
+.SH OPTIONS
+.TP
+.B \-c
+.I cachename
+directly specifies the credential cache to be made primary.
+.TP
+.B \-p
+.I principal
+causes the cache collection to be searched for a cache containing
+credentials for \fIprincipal\fP. If one is found, that collection is
+made primary.
+.SH ENVIRONMENT
+.B kswitch
+uses the following environment variables:
+.TP "\w'.SM KRB5CCNAME\ \ 'u"
+.SM KRB5CCNAME
+Location of the default Kerberos 5 credentials (ticket) cache, in the
+form \fItype\fP:\fIresidual\fP. If no type prefix is present, the
+\fBFILE\fP type is assumed. The type of the default cache may
+determine the availability of a cache collection; for instance, a
+default cache of type \fBDIR\fP causes caches within the directory to
+be present in the collection.
+.SH FILES
+.TP "\w'/tmp/krb5cc_[uid]\ \ 'u"
+/tmp/krb5cc_[uid]
+default location of Kerberos 5 credentials cache
+([uid] is the decimal UID of the user).
+.SH SEE ALSO
+kinit(1), kdestroy(1), klist(1), kerberos(1)
diff --git a/src/clients/kswitch/kswitch.c b/src/clients/kswitch/kswitch.c
new file mode 100644
index 0000000000..42cc9d6644
--- /dev/null
+++ b/src/clients/kswitch/kswitch.c
@@ -0,0 +1,127 @@
+/* -*- 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"
+
+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_MESSAGES, "");
+ 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);
+ }
+ }
+
+ ret = krb5_cc_switch(context, cache);
+ if (ret != 0) {
+ com_err(progname, ret, _("while switching to credential cache"));
+ exit(1);
+ }
+ return 0;
+}
diff --git a/src/configure.in b/src/configure.in
index c765c91e33..38889cf529 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1239,7 +1239,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
plugins/authdata/greet_server
clients clients/klist clients/kinit clients/kvno
- clients/kdestroy clients/kpasswd clients/ksu
+ clients/kdestroy clients/kpasswd clients/ksu clients/kswitch
kadmin kadmin/cli kadmin/dbutil kadmin/ktutil kadmin/server
kadmin/testing kadmin/testing/scripts kadmin/testing/util
diff --git a/src/lib/krb5/ccache/cccursor.c b/src/lib/krb5/ccache/cccursor.c
index 2efaa322c7..23681f99f1 100644
--- a/src/lib/krb5/ccache/cccursor.c
+++ b/src/lib/krb5/ccache/cccursor.c
@@ -181,7 +181,7 @@ krb5_cc_cache_match(krb5_context context, krb5_principal client,
{
krb5_error_code ret;
krb5_cccol_cursor cursor;
- krb5_ccache cache;
+ krb5_ccache cache = NULL;
krb5_principal princ;
char *name;
krb5_boolean eq;
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 987238cfbc..0d6f0b7950 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -68,6 +68,7 @@ check-pytests::
$(RUNPYTEST) $(srcdir)/t_keyrollover.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_renew.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_renprinc.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_cccol.py $(PYTESTFLAGS)
clean::
$(RM) kdc.conf
diff --git a/src/tests/t_cccol.py b/src/tests/t_cccol.py
new file mode 100644
index 0000000000..d8db275ce3
--- /dev/null
+++ b/src/tests/t_cccol.py
@@ -0,0 +1,77 @@
+# Copyright (C) 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.
+
+#!/usr/bin/python
+from k5test import *
+
+realm = K5Realm(start_kadmind=False, create_user=False, create_host=False)
+
+# Make a directory collection and use it for client commands in both realms.
+ccdir = os.path.join(realm.testdir, 'cc')
+ccname = 'DIR:' + ccdir
+os.mkdir(ccdir)
+realm.env_client['KRB5CCNAME'] = ccname
+
+realm.addprinc('alice', password('alice'))
+realm.addprinc('bob', password('bob'))
+realm.addprinc('carol', password('carol'))
+
+realm.kinit('alice', password('alice'))
+output = realm.run_as_client([klist])
+if 'Default principal: alice@' not in output:
+ fail('Initial kinit failed to get credentials for alice.')
+realm.run_as_client([kdestroy])
+output = realm.run_as_client([klist], expected_code=1)
+if 'No credentials cache found' not in output:
+ fail('Initial kdestroy failed to destroy primary cache.')
+output = realm.run_as_client([klist, '-l'], expected_code=1)
+if not output.endswith('---\n') or output.count('\n') != 2:
+ fail('Initial kdestroy failed to empty cache collection.')
+
+realm.kinit('alice', password('alice'))
+realm.kinit('carol', password('carol'))
+output = realm.run_as_client([klist, '-l'])
+if '---\ncarol@' not in output or '\nalice@' not in output:
+ fail('klist -l did not show expected output after two kinits.')
+realm.kinit('alice', password('alice'))
+output = realm.run_as_client([klist, '-l'])
+if '---\nalice@' not in output or output.count('\n') != 4:
+ fail('klist -l did not show expected output after re-kinit for alice.')
+realm.kinit('bob', password('bob'))
+output = realm.run_as_client([klist, '-A'])
+if 'bob@' not in output.splitlines()[1] or 'alice@' not in output or \
+ 'carol' not in output or output.count('Default principal:') != 3:
+ fail('klist -A did not show expected output after kinit for bob.')
+realm.run_as_client([kswitch, '-p', 'carol'])
+output = realm.run_as_client([klist, '-l'])
+if '---\ncarol@' not in output or output.count('\n') != 5:
+ fail('klist -l did not show expected output after kswitch to carol.')
+realm.run_as_client([kdestroy])
+output = realm.run_as_client([klist, '-l'])
+if 'carol@' in output or 'bob@' not in output or output.count('\n') != 4:
+ fail('kdestroy failed to remove only primary ccache.')
+realm.run_as_client([kdestroy, '-A'])
+output = realm.run_as_client([klist, '-l'], expected_code=1)
+if not output.endswith('---\n') or output.count('\n') != 2:
+ fail('kdestroy -a failed to empty cache collection.')
+
+success('Credential cache collection tests.')
diff --git a/src/util/k5test.py b/src/util/k5test.py
index cc3a7a1d63..ba4f8302bf 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -181,6 +181,7 @@ Scripts may use the following functions and variables:
- ktutil
- kinit
- klist
+ - kswitch
- kvno
- kdestroy
- kpasswd
@@ -1074,6 +1075,7 @@ kdb5_util = os.path.join(buildtop, 'kadmin', 'dbutil', 'kdb5_util')
ktutil = os.path.join(buildtop, 'kadmin', 'ktutil', 'ktutil')
kinit = os.path.join(buildtop, 'clients', 'kinit', 'kinit')
klist = os.path.join(buildtop, 'clients', 'klist', 'klist')
+kswitch = os.path.join(buildtop, 'clients', 'kswitch', 'kswitch')
kvno = os.path.join(buildtop, 'clients', 'kvno', 'kvno')
kdestroy = os.path.join(buildtop, 'clients', 'kdestroy', 'kdestroy')
kpasswd = os.path.join(buildtop, 'clients', 'kpasswd', 'kpasswd')
diff --git a/src/util/testrealm.py b/src/util/testrealm.py
index 30466314c7..30b3256edd 100644
--- a/src/util/testrealm.py
+++ b/src/util/testrealm.py
@@ -39,6 +39,7 @@ progpaths = [
os.path.join('clients', 'kpasswd'),
os.path.join('clients', 'ksu'),
os.path.join('clients', 'kvno'),
+ os.path.join('clients', 'kswitch'),
'slave'
]