svn rev #25157: trunk/ doc/rst_source/ doc/rst_source/krb_users/user_commands/ ...

ghudson@MIT.EDU ghudson at MIT.EDU
Mon Sep 5 12:26:48 EDT 2011


http://src.mit.edu/fisheye/changelog/krb5/?cs=25157
Commit By: ghudson
Log Message:
ticket: 6956
subject: 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.


Changed Files:
U   trunk/doc/rst_source/conf.py
U   trunk/doc/rst_source/krb_users/user_commands/index.rst
U   trunk/doc/rst_source/krb_users/user_commands/kdestroy.rst
U   trunk/doc/rst_source/krb_users/user_commands/kinit.rst
U   trunk/doc/rst_source/krb_users/user_commands/klist.rst
A   trunk/doc/rst_source/krb_users/user_commands/kswitch.rst
U   trunk/src/clients/Makefile.in
U   trunk/src/clients/kdestroy/kdestroy.M
U   trunk/src/clients/kdestroy/kdestroy.c
U   trunk/src/clients/kinit/kinit.M
U   trunk/src/clients/kinit/kinit.c
U   trunk/src/clients/klist/klist.M
U   trunk/src/clients/klist/klist.c
A   trunk/src/clients/kswitch/
A   trunk/src/clients/kswitch/Makefile.in
A   trunk/src/clients/kswitch/deps
A   trunk/src/clients/kswitch/kswitch.M
A   trunk/src/clients/kswitch/kswitch.c
U   trunk/src/configure.in
U   trunk/src/lib/krb5/ccache/cccursor.c
U   trunk/src/tests/Makefile.in
A   trunk/src/tests/t_cccol.py
U   trunk/src/util/k5test.py
U   trunk/src/util/testrealm.py
Modified: trunk/doc/rst_source/conf.py
===================================================================
--- trunk/doc/rst_source/conf.py	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/doc/rst_source/conf.py	2011-09-05 16:26:48 UTC (rev 25157)
@@ -221,6 +221,7 @@
     ('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),

Modified: trunk/doc/rst_source/krb_users/user_commands/index.rst
===================================================================
--- trunk/doc/rst_source/krb_users/user_commands/index.rst	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/doc/rst_source/krb_users/user_commands/index.rst	2011-09-05 16:26:48 UTC (rev 25157)
@@ -10,6 +10,7 @@
    kinit.rst
    klist.rst
    kdestroy.rst
+   kswitch.rst
    kpasswd.rst
    kvno.rst
    ksu.rst

Modified: trunk/doc/rst_source/krb_users/user_commands/kdestroy.rst
===================================================================
--- trunk/doc/rst_source/krb_users/user_commands/kdestroy.rst	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/doc/rst_source/krb_users/user_commands/kdestroy.rst	2011-09-05 16:26:48 UTC (rev 25157)
@@ -5,6 +5,7 @@
 ~~~~~~~~~~~~~
 
 *kdestroy*
+         [**-A**]
          [**-q**]
          [**-c** *cache_name*]
 
@@ -21,6 +22,10 @@
 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 @@
 
 *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

Modified: trunk/doc/rst_source/krb_users/user_commands/kinit.rst
===================================================================
--- trunk/doc/rst_source/krb_users/user_commands/kinit.rst	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/doc/rst_source/krb_users/user_commands/kinit.rst	2011-09-05 16:26:48 UTC (rev 25157)
@@ -129,8 +129,15 @@
           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 @@
 
 *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

Modified: trunk/doc/rst_source/krb_users/user_commands/klist.rst
===================================================================
--- trunk/doc/rst_source/krb_users/user_commands/klist.rst	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/doc/rst_source/krb_users/user_commands/klist.rst	2011-09-05 16:26:48 UTC (rev 25157)
@@ -7,7 +7,7 @@
 
 **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 @@
           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 @@
 
 *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

Added: trunk/doc/rst_source/krb_users/user_commands/kswitch.rst
===================================================================
--- trunk/doc/rst_source/krb_users/user_commands/kswitch.rst	                        (rev 0)
+++ trunk/doc/rst_source/krb_users/user_commands/kswitch.rst	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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)

Modified: trunk/src/clients/Makefile.in
===================================================================
--- trunk/src/clients/Makefile.in	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/Makefile.in	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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

Modified: trunk/src/clients/kdestroy/kdestroy.M
===================================================================
--- trunk/src/clients/kdestroy/kdestroy.M	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/kdestroy/kdestroy.M	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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 @@
 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 @@
 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]

Modified: trunk/src/clients/kdestroy/kdestroy.c
===================================================================
--- trunk/src/clients/kdestroy/kdestroy.c	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/kdestroy/kdestroy.c	2011-09-05 16:26:48 UTC (rev 25157)
@@ -55,7 +55,8 @@
 {
 #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 @@
     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 @@
         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) {

Modified: trunk/src/clients/kinit/kinit.M
===================================================================
--- trunk/src/clients/kinit/kinit.M	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/kinit/kinit.M	2011-09-05 16:26:48 UTC (rev 25157)
@@ -182,7 +182,11 @@
 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 @@
 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 @@
 .B keytab
 file.
 .SH SEE ALSO
-klist(1), kdestroy(1), kerberos(1)
+klist(1), kdestroy(1), kswitch(1), kerberos(1)

Modified: trunk/src/clients/kinit/kinit.c
===================================================================
--- trunk/src/clients/kinit/kinit.c	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/kinit/kinit.c	2011-09-05 16:26:48 UTC (rev 25157)
@@ -133,6 +133,7 @@
     krb5_ccache cc;
     krb5_principal me;
     char* name;
+    krb5_boolean switch_to_cache;
 };
 
 #ifdef GETOPT_LONG
@@ -438,6 +439,8 @@
 {
     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 @@
         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 @@
             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 @@
     }
     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);

Modified: trunk/src/clients/klist/klist.M
===================================================================
--- trunk/src/clients/klist/klist.M	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/klist/klist.M	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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 @@
 .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 @@
 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]

Modified: trunk/src/clients/klist/klist.c
===================================================================
--- trunk/src/clients/klist/klist.c	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/clients/klist/klist.c	2011-09-05 16:26:48 UTC (rev 25157)
@@ -54,7 +54,7 @@
 
 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 @@
 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 @@
 {
 #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 @@
     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 @@
             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 @@
     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 @@
             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,21 +325,104 @@
     }
     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;
 
-    if (status_only)
-        /* exit_status is set back to 0 if a valid tgt is found */
-        exit_status = 1;
+    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;
+}
+
+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))) {
             if (!status_only)
@@ -335,7 +437,22 @@
             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))) {
         if (code == KRB5_FCC_NOFILE) {
@@ -355,17 +472,17 @@
                         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 @@
     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 @@
         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;
     }
 }
 

Added: trunk/src/clients/kswitch/Makefile.in
===================================================================
--- trunk/src/clients/kswitch/Makefile.in	                        (rev 0)
+++ trunk/src/clients/kswitch/Makefile.in	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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

Added: trunk/src/clients/kswitch/deps
===================================================================
--- trunk/src/clients/kswitch/deps	                        (rev 0)
+++ trunk/src/clients/kswitch/deps	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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

Copied: trunk/src/clients/kswitch/kswitch.M (from rev 25156, trunk/src/clients/kdestroy/kdestroy.M)
===================================================================
--- trunk/src/clients/kswitch/kswitch.M	                        (rev 0)
+++ trunk/src/clients/kswitch/kswitch.M	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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)

Added: trunk/src/clients/kswitch/kswitch.c
===================================================================
--- trunk/src/clients/kswitch/kswitch.c	                        (rev 0)
+++ trunk/src/clients/kswitch/kswitch.c	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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;
+}

Modified: trunk/src/configure.in
===================================================================
--- trunk/src/configure.in	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/configure.in	2011-09-05 16:26:48 UTC (rev 25157)
@@ -1239,7 +1239,7 @@
 	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

Modified: trunk/src/lib/krb5/ccache/cccursor.c
===================================================================
--- trunk/src/lib/krb5/ccache/cccursor.c	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/lib/krb5/ccache/cccursor.c	2011-09-05 16:26:48 UTC (rev 25157)
@@ -181,7 +181,7 @@
 {
     krb5_error_code ret;
     krb5_cccol_cursor cursor;
-    krb5_ccache cache;
+    krb5_ccache cache = NULL;
     krb5_principal princ;
     char *name;
     krb5_boolean eq;

Modified: trunk/src/tests/Makefile.in
===================================================================
--- trunk/src/tests/Makefile.in	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/tests/Makefile.in	2011-09-05 16:26:48 UTC (rev 25157)
@@ -68,6 +68,7 @@
 	$(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

Added: trunk/src/tests/t_cccol.py
===================================================================
--- trunk/src/tests/t_cccol.py	                        (rev 0)
+++ trunk/src/tests/t_cccol.py	2011-09-05 16:26:48 UTC (rev 25157)
@@ -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.')

Modified: trunk/src/util/k5test.py
===================================================================
--- trunk/src/util/k5test.py	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/util/k5test.py	2011-09-05 16:26:48 UTC (rev 25157)
@@ -181,6 +181,7 @@
   - ktutil
   - kinit
   - klist
+  - kswitch
   - kvno
   - kdestroy
   - kpasswd
@@ -1074,6 +1075,7 @@
 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')

Modified: trunk/src/util/testrealm.py
===================================================================
--- trunk/src/util/testrealm.py	2011-09-05 16:26:43 UTC (rev 25156)
+++ trunk/src/util/testrealm.py	2011-09-05 16:26:48 UTC (rev 25157)
@@ -39,6 +39,7 @@
     os.path.join('clients', 'kpasswd'),
     os.path.join('clients', 'ksu'),
     os.path.join('clients', 'kvno'),
+    os.path.join('clients', 'kswitch'),
     'slave'
 ]
 




More information about the cvs-krb5 mailing list