krb5 commit: Improve gss_store_cred() behavior

Greg Hudson ghudson at mit.edu
Mon Sep 13 23:55:07 EDT 2021


https://github.com/krb5/krb5/commit/3f5a348287646d65700854650fe668b9c4249013
commit 3f5a348287646d65700854650fe668b9c4249013
Author: Greg Hudson <ghudson at mit.edu>
Date:   Fri Sep 3 11:36:01 2021 -0400

    Improve gss_store_cred() behavior
    
    Select an output credential cache using similar logic to kinit.  Do
    not require the target cache to be initialized.
    
    Try to use the per-thread cache set by gss_krb5_ccache_name() if no
    output cache was specified via a cred store.
    
    When the destination is a collection, honor the default_cred flag by
    switching the primary cache to the selected output cache.  When the
    destination is not a collection, ignore the default_cred flag.
    (Previously the default_cred flag was mandatory for gss_store_cred()
    even though it is an advisory flag, and ignored for
    gss_store_cred_into() even if no ccache was specified in the cred
    store.)
    
    Honor the overwrite_cred flag by refusing to replace an initialized
    cache if it is not set.  Stop using gss_acquire_cred() for this
    purpose as it could go out and fetch credentials from a client keytab.
    
    Perform atomic replacement of the target cache when possible, using
    krb5_cc_move().
    
    Add a test harness for calling gss_store_cred() or
    gss_store_cred_into() and a suite of tests.  Fix a broken trace log
    message for krb5_cc_move() and update the expected trace logs for an
    existing t_credstore.py test.
    
    ticket: 8010

 .gitignore                       |    1 +
 doc/appdev/gssapi.rst            |   15 +++-
 src/include/k5-trace.h           |    2 +-
 src/lib/gssapi/krb5/store_cred.c |  145 +++++++++++++++++--------------------
 src/tests/gssapi/Makefile.in     |   14 +++--
 src/tests/gssapi/t_credstore.py  |    4 +-
 src/tests/gssapi/t_store_cred.c  |  114 ++++++++++++++++++++++++++++++
 src/tests/gssapi/t_store_cred.py |   80 +++++++++++++++++++++
 8 files changed, 285 insertions(+), 90 deletions(-)

diff --git a/.gitignore b/.gitignore
index 45c8bba..a1ba832 100644
--- a/.gitignore
+++ b/.gitignore
@@ -470,6 +470,7 @@ local.properties
 /src/tests/gssapi/t_saslname
 /src/tests/gssapi/t_spnego
 /src/tests/gssapi/t_srcattrs
+/src/tests/gssapi/t_store_cred
 /src/tests/gssapi/t_inq_ctx
 
 /src/tests/hammer/kdc5_hammer
diff --git a/doc/appdev/gssapi.rst b/doc/appdev/gssapi.rst
index d26c9fe..339fd6c 100644
--- a/doc/appdev/gssapi.rst
+++ b/doc/appdev/gssapi.rst
@@ -252,10 +252,8 @@ The following options are supported by the krb5 mechanism:
 
 * **ccache**: For acquiring initiator credentials, the name of the
   :ref:`credential cache <ccache_definition>` to which the handle will
-  refer.  For storing credentials, the name of the cache where the
-  credentials should be stored.  If a collection name is given, the
-  primary cache of the collection will be used; this behavior may
-  change in future releases to select a cache from the collection.
+  refer.  For storing credentials, the name of the cache or collection
+  where the credentials will be stored (see below).
 
 * **client_keytab**: For acquiring initiator credentials, the name of
   the :ref:`keytab <keytab_definition>` which will be used, if
@@ -285,6 +283,15 @@ The following options are supported by the krb5 mechanism:
   the empty string.  If the empty string is given, any ``host``
   service principal in the keytab may be used.  (New in release 1.19.)
 
+In release 1.20 or later, if a collection name is specified for
+**cache** in a call to gss_store_cred_into(), an existing cache for
+the client principal within the collection will be selected, or a new
+cache will be created within the collection.  If *overwrite_cred* is
+false and the selected credential cache already exists, a
+**GSS_S_DUPLICATE_ELEMENT** error will be returned.  If *default_cred*
+is true, the primary cache of the collection will be switched to the
+selected cache.
+
 
 Importing and exporting credentials
 -----------------------------------
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 79b5a7a..7bf0c45 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -119,7 +119,7 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
     TRACE(c, "Initializing {ccache} with default princ {princ}",    \
           cache, princ)
 #define TRACE_CC_MOVE(c, src, dst)                                      \
-    TRACE(c, "Moving contents of ccache {src} to {dst}", src, dst)
+    TRACE(c, "Moving ccache {ccache} to {ccache}", src, dst)
 #define TRACE_CC_NEW_UNIQUE(c, type)                            \
     TRACE(c, "Resolving unique ccache of type {str}", type)
 #define TRACE_CC_REMOVE(c, cache, creds)                        \
diff --git a/src/lib/gssapi/krb5/store_cred.c b/src/lib/gssapi/krb5/store_cred.c
index 96eb1c9..1e168bc 100644
--- a/src/lib/gssapi/krb5/store_cred.c
+++ b/src/lib/gssapi/krb5/store_cred.c
@@ -27,36 +27,6 @@
 #include "k5-int.h"
 #include "gssapiP_krb5.h"
 
-static int
-has_unexpired_creds(krb5_gss_cred_id_t kcred,
-                    const gss_OID desired_mech,
-                    int default_cred,
-                    gss_const_key_value_set_t cred_store)
-{
-    OM_uint32 major_status, minor;
-    gss_name_t cred_name;
-    gss_OID_set_desc desired_mechs;
-    gss_cred_id_t tmp_cred = GSS_C_NO_CREDENTIAL;
-    OM_uint32 time_rec;
-
-    desired_mechs.count = 1;
-    desired_mechs.elements = (gss_OID)desired_mech;
-
-    if (default_cred)
-        cred_name = GSS_C_NO_NAME;
-    else
-        cred_name = (gss_name_t)kcred->name;
-
-    major_status = krb5_gss_acquire_cred_from(&minor, cred_name, 0,
-                                              &desired_mechs, GSS_C_INITIATE,
-                                              cred_store, &tmp_cred, NULL,
-                                              &time_rec);
-
-    krb5_gss_release_cred(&minor, &tmp_cred);
-
-    return (GSS_ERROR(major_status) || time_rec);
-}
-
 static OM_uint32
 copy_initiator_creds(OM_uint32 *minor_status,
                      gss_cred_id_t input_cred_handle,
@@ -66,26 +36,19 @@ copy_initiator_creds(OM_uint32 *minor_status,
                      gss_const_key_value_set_t cred_store)
 {
     OM_uint32 major_status;
-    krb5_error_code code;
+    krb5_error_code ret;
     krb5_gss_cred_id_t kcred = NULL;
     krb5_context context = NULL;
-    krb5_ccache ccache = NULL;
-    const char *ccache_name;
+    krb5_ccache cache = NULL, defcache = NULL, mcc = NULL;
+    krb5_principal princ = NULL;
+    krb5_boolean switch_to_cache = FALSE;
+    const char *ccache_name, *deftype;
 
     *minor_status = 0;
 
-    if (!default_cred && cred_store == GSS_C_NO_CRED_STORE) {
-        *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP;
-        major_status = GSS_S_FAILURE;
-        goto cleanup;
-    }
-
-    code = krb5_gss_init_context(&context);
-    if (code != 0) {
-        *minor_status = code;
-        major_status = GSS_S_FAILURE;
-        goto cleanup;
-    }
+    ret = krb5_gss_init_context(&context);
+    if (ret)
+        goto kerr_cleanup;
 
     major_status = krb5_gss_validate_cred_1(minor_status,
                                             input_cred_handle,
@@ -101,52 +64,69 @@ copy_initiator_creds(OM_uint32 *minor_status,
         goto cleanup;
     }
 
-    if (!overwrite_cred &&
-        has_unexpired_creds(kcred, desired_mech, default_cred, cred_store)) {
-        major_status = GSS_S_DUPLICATE_ELEMENT;
-        goto cleanup;
-    }
-
     major_status = kg_value_from_cred_store(cred_store,
                                             KRB5_CS_CCACHE_URN, &ccache_name);
     if (GSS_ERROR(major_status))
         goto cleanup;
 
     if (ccache_name != NULL) {
-        code = krb5_cc_resolve(context, ccache_name, &ccache);
-        if (code != 0) {
-            *minor_status = code;
-            major_status = GSS_S_FAILURE;
+        ret = krb5_cc_set_default_name(context, ccache_name);
+        if (ret)
+            goto kerr_cleanup;
+    } else {
+        major_status = kg_sync_ccache_name(context, minor_status);
+        if (major_status != GSS_S_COMPLETE)
             goto cleanup;
-        }
-        code = krb5_cc_initialize(context, ccache,
-                                  kcred->name->princ);
-        if (code != 0) {
-            *minor_status = code;
-            major_status = GSS_S_FAILURE;
-            goto cleanup;
-        }
     }
 
-    if (ccache == NULL) {
-        if (!default_cred) {
-            *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP;
-            major_status = GSS_S_FAILURE;
+    /* Resolve the default ccache and get its type. */
+    ret = krb5_cc_default(context, &defcache);
+    if (ret)
+        goto kerr_cleanup;
+    deftype = krb5_cc_get_type(context, defcache);
+
+    if (krb5_cc_support_switch(context, deftype)) {
+        /* Use an existing or new cache within the collection. */
+        ret = krb5_cc_cache_match(context, kcred->name->princ, &cache);
+        if (!ret && !overwrite_cred) {
+            major_status = GSS_S_DUPLICATE_ELEMENT;
             goto cleanup;
         }
-        code = krb5int_cc_default(context, &ccache);
-        if (code != 0) {
-            *minor_status = code;
-            major_status = GSS_S_FAILURE;
+        if (ret == KRB5_CC_NOTFOUND)
+            ret = krb5_cc_new_unique(context, deftype, NULL, &cache);
+        if (ret)
+            goto kerr_cleanup;
+        switch_to_cache = default_cred;
+    } else {
+        /* Use the default cache. */
+        cache = defcache;
+        defcache = NULL;
+        ret = krb5_cc_get_principal(context, cache, &princ);
+        krb5_free_principal(context, princ);
+        if (!ret && !overwrite_cred) {
+            major_status = GSS_S_DUPLICATE_ELEMENT;
             goto cleanup;
         }
     }
 
-    code = krb5_cc_copy_creds(context, kcred->ccache, ccache);
-    if (code != 0) {
-        *minor_status = code;
-        major_status = GSS_S_FAILURE;
-        goto cleanup;
+    ret = krb5_cc_new_unique(context, "MEMORY", NULL, &mcc);
+    if (ret)
+        goto kerr_cleanup;
+    ret = krb5_cc_initialize(context, mcc, kcred->name->princ);
+    if (ret)
+        goto kerr_cleanup;
+    ret = krb5_cc_copy_creds(context, kcred->ccache, mcc);
+    if (ret)
+        goto kerr_cleanup;
+    ret = krb5_cc_move(context, mcc, cache);
+    if (ret)
+        goto kerr_cleanup;
+    mcc = NULL;
+
+    if (switch_to_cache) {
+        ret = krb5_cc_switch(context, cache);
+        if (ret)
+            goto kerr_cleanup;
     }
 
     *minor_status = 0;
@@ -155,11 +135,20 @@ copy_initiator_creds(OM_uint32 *minor_status,
 cleanup:
     if (kcred != NULL)
         k5_mutex_unlock(&kcred->lock);
-    if (ccache != NULL)
-        krb5_cc_close(context, ccache);
+    if (defcache != NULL)
+        krb5_cc_close(context, defcache);
+    if (cache != NULL)
+        krb5_cc_close(context, cache);
+    if (mcc != NULL)
+        krb5_cc_destroy(context, mcc);
     krb5_free_context(context);
 
     return major_status;
+
+kerr_cleanup:
+    *minor_status = ret;
+    major_status = GSS_S_FAILURE;
+    goto cleanup;
 }
 
 OM_uint32 KRB5_CALLCONV
diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in
index 23f7d0e..4cac8cb 100644
--- a/src/tests/gssapi/Makefile.in
+++ b/src/tests/gssapi/Makefile.in
@@ -19,7 +19,7 @@ SRCS=	$(srcdir)/ccinit.c $(srcdir)/ccrefresh.c $(srcdir)/common.c \
 	$(srcdir)/t_lifetime.c $(srcdir)/t_namingexts.c $(srcdir)/t_oid.c \
 	$(srcdir)/t_pcontok.c $(srcdir)/t_prf.c $(srcdir)/t_s4u.c \
 	$(srcdir)/t_s4u2proxy_krb5.c $(srcdir)/t_saslname.c \
-	$(srcdir)/t_spnego.c $(srcdir)/t_srcattrs.c
+	$(srcdir)/t_spnego.c $(srcdir)/t_srcattrs.c $(srcdir)/t_store_cred.c
 
 OBJS=	ccinit.o ccrefresh.o common.o reload.o t_accname.o t_add_cred.o \
 	t_bindings.o t_ccselect.o t_ciflags.o t_context.o t_credstore.o \
@@ -27,7 +27,7 @@ OBJS=	ccinit.o ccrefresh.o common.o reload.o t_accname.o t_add_cred.o \
 	t_imp_cred.o t_imp_name.o t_invalid.o t_inq_cred.o t_inq_ctx.o \
 	t_inq_mechs_name.o t_iov.o t_lifetime.o t_namingexts.o t_oid.o \
 	t_pcontok.o t_prf.o t_s4u.o t_s4u2proxy_krb5.o t_saslname.o \
-	t_spnego.o t_srcattrs.o
+	t_spnego.o t_srcattrs.o t_store_cred.o
 
 COMMON_DEPS= common.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
 COMMON_LIBS= common.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
@@ -36,7 +36,7 @@ all: ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect t_ciflags \
 	t_context t_credstore t_enctypes t_err t_export_cred t_export_name \
 	t_gssexts t_imp_cred t_imp_name t_invalid t_inq_cred t_inq_ctx \
 	t_inq_mechs_name t_iov t_lifetime t_namingexts t_oid t_pcontok t_prf \
-	t_s4u t_s4u2proxy_krb5 t_saslname t_spnego t_srcattrs
+	t_s4u t_s4u2proxy_krb5 t_saslname t_spnego t_srcattrs t_store_cred
 
 check-unix: t_oid reload
 	$(RUN_TEST) ./t_invalid
@@ -48,8 +48,10 @@ check-unix: t_oid reload
 check-pytests: ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect \
 	t_ciflags t_context t_credstore t_enctypes t_err t_export_cred \
 	t_export_name t_imp_cred t_inq_cred t_inq_ctx t_inq_mechs_name t_iov \
-	t_lifetime t_pcontok t_s4u t_s4u2proxy_krb5 t_spnego t_srcattrs
+	t_lifetime t_pcontok t_s4u t_s4u2proxy_krb5 t_spnego t_srcattrs \
+	t_store_cred
 	$(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS)
+	$(RUNPYTEST) $(srcdir)/t_store_cred.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_credstore.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_bindings.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_ccselect.py $(PYTESTFLAGS)
@@ -124,6 +126,8 @@ t_spnego: t_spnego.o $(COMMON_DEPS)
 	$(CC_LINK) -o $@ t_spnego.o $(COMMON_LIBS)
 t_srcattrs: t_srcattrs.o $(COMMON_DEPS)
 	$(CC_LINK) -o $@ t_srcattrs.o $(COMMON_LIBS)
+t_store_cred: t_store_cred.o $(COMMON_DEPS)
+	$(CC_LINK) -o $@ t_store_cred.o $(COMMON_LIBS)
 
 clean:
 	$(RM) ccinit ccrefresh reload t_accname t_add_cred t_bindings
@@ -131,4 +135,4 @@ clean:
 	$(RM) t_export_cred t_export_name t_gssexts t_imp_cred t_imp_name
 	$(RM) t_invalid t_inq_cred t_inq_ctx t_inq_mechs_name t_iov t_lifetime
 	$(RM) t_namingexts t_oid t_pcontok t_prf t_s4u t_s4u2proxy_krb5
-	$(RM) t_saslname t_spnego t_srcattrs
+	$(RM) t_saslname t_spnego t_srcattrs t_store_cred
diff --git a/src/tests/gssapi/t_credstore.py b/src/tests/gssapi/t_credstore.py
index f666e5e..ec59dd8 100644
--- a/src/tests/gssapi/t_credstore.py
+++ b/src/tests/gssapi/t_credstore.py
@@ -9,8 +9,8 @@ service_cs = 'service/cs@%s' % realm.realm
 realm.addprinc(service_cs)
 realm.extract_keytab(service_cs, servicekeytab)
 realm.kinit(service_cs, None, ['-k', '-t', servicekeytab])
-msgs = ('Storing %s -> %s in %s' % (service_cs, realm.krbtgt_princ,
-                                    storagecache),
+msgs = ('Storing %s -> %s in MEMORY:' % (service_cs, realm.krbtgt_princ),
+        'Moving ccache MEMORY:',
         'Retrieving %s from FILE:%s' % (service_cs, servicekeytab))
 realm.run(['./t_credstore', '-s', 'p:' + service_cs, 'ccache', storagecache,
            'keytab', servicekeytab], expected_trace=msgs)
diff --git a/src/tests/gssapi/t_store_cred.c b/src/tests/gssapi/t_store_cred.c
new file mode 100644
index 0000000..b053e52
--- /dev/null
+++ b/src/tests/gssapi/t_store_cred.c
@@ -0,0 +1,114 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_store_cred.c - gss_store_cred() test harness */
+/*
+ * Copyright (C) 2021 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Usage: t_store_cred [-d] [-i] [-o] src_ccname [dest_ccname]
+ *
+ * Acquires creds from src_ccname using gss_acquire_cred_from() and then stores
+ * them, using gss_store_cred_into() if -i is specified or gss_store_cred()
+ * otherwise.  If dest_ccname is specified with -i, it is included in the cred
+ * store for the store operation; if it is specified without -i, it is set with
+ * gss_krb5_ccache_name() before the store operation.  If -d and/or -o are
+ * specified they set the default_cred and overwrite_cred flags to true
+ * respectively.
+ */
+
+#include "k5-platform.h"
+#include <gssapi/gssapi_ext.h>
+#include "common.h"
+
+int
+main(int argc, char *argv[])
+{
+    OM_uint32 major, minor;
+    gss_key_value_set_desc store;
+    gss_key_value_element_desc elem;
+    gss_cred_id_t cred;
+    krb5_boolean def = FALSE, into = FALSE, overwrite = FALSE;
+    const char *src_ccname, *dest_ccname;
+    int c;
+
+    /* Parse arguments. */
+    while ((c = getopt(argc, argv, "dio")) != -1) {
+        switch (c) {
+        case 'd':
+            def = TRUE;
+            break;
+        case 'i':
+            into = TRUE;
+            break;
+        case 'o':
+            overwrite = TRUE;
+            break;
+        default:
+            abort();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    assert(argc == 1 || argc == 2);
+    src_ccname = argv[0];
+    dest_ccname = argv[1];
+
+    elem.key = "ccache";
+    elem.value = src_ccname;
+    store.count = 1;
+    store.elements = &elem;
+    major = gss_acquire_cred_from(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+                                  &mechset_krb5, GSS_C_INITIATE, &store, &cred,
+                                  NULL, NULL);
+    check_gsserr("acquire_cred", major, minor);
+
+    if (into) {
+        if (dest_ccname != NULL) {
+            elem.key = "ccache";
+            elem.value = dest_ccname;
+            store.count = 1;
+        } else {
+            store.count = 0;
+        }
+        major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, &mech_krb5,
+                                    overwrite, def, &store, NULL, NULL);
+        check_gsserr("store_cred_into", major, minor);
+    } else {
+        if (dest_ccname != NULL) {
+            major = gss_krb5_ccache_name(&minor, dest_ccname, NULL);
+            check_gsserr("ccache_name", major, minor);
+        }
+        major = gss_store_cred(&minor, cred, GSS_C_INITIATE, &mech_krb5,
+                               overwrite, def, NULL, NULL);
+        check_gsserr("store_cred", major, minor);
+    }
+
+    gss_release_cred(&minor, &cred);
+    return 0;
+}
diff --git a/src/tests/gssapi/t_store_cred.py b/src/tests/gssapi/t_store_cred.py
new file mode 100644
index 0000000..00e80cf
--- /dev/null
+++ b/src/tests/gssapi/t_store_cred.py
@@ -0,0 +1,80 @@
+from k5test import *
+
+realm = K5Realm(create_user=False)
+
+alice = 'alice@' + realm.realm
+bob = 'bob@' + realm.realm
+cc_alice = realm.ccache + '.alice'
+cc_bob = realm.ccache + '.bob'
+realm.addprinc(alice)
+realm.addprinc(bob)
+realm.extract_keytab(alice, realm.keytab)
+realm.extract_keytab(bob, realm.keytab)
+realm.kinit(alice, flags=['-k', '-c', cc_alice])
+realm.kinit(bob, flags=['-k', '-c', cc_bob])
+
+mark('FILE, default output ccache')
+realm.run(['./t_store_cred', cc_alice])
+realm.klist(alice)
+# Overwriting should fail by default, whether or not the principal matches.
+realm.run(['./t_store_cred', cc_alice], expected_code=1,
+          expected_msg='The requested credential element already exists')
+realm.run(['./t_store_cred', cc_bob], expected_code=1,
+          expected_msg='The requested credential element already exists')
+# Overwriting should succeed with overwrite_cred set.
+realm.run(['./t_store_cred', '-o', cc_bob])
+realm.klist(bob)
+# default_cred has no effect without a collection.
+realm.run(['./t_store_cred', '-d', '-o', cc_alice])
+realm.klist(alice)
+
+mark('FILE, gss_krb5_ccache_name()')
+cc_alternate = realm.ccache + '.alternate'
+realm.run(['./t_store_cred', cc_alice, cc_alternate])
+realm.klist(alice, ccache=cc_alternate)
+realm.run(['./t_store_cred', cc_bob, cc_alternate], expected_code=1,
+          expected_msg='The requested credential element already exists')
+
+mark('FILE, gss_store_cred_into()')
+os.remove(cc_alternate)
+realm.run(['./t_store_cred', '-i', cc_alice, cc_alternate])
+realm.klist(alice, ccache=cc_alternate)
+realm.run(['./t_store_cred', '-i', cc_bob, cc_alternate], expected_code=1,
+          expected_msg='The requested credential element already exists')
+
+mark('DIR, gss_krb5_ccache_name()')
+cc_dir = 'DIR:' + os.path.join(realm.testdir, 'cc')
+realm.run(['./t_store_cred', cc_alice, cc_dir])
+realm.run([klist, '-c', cc_dir], expected_code=1,
+          expected_msg='No credentials cache found')
+realm.run([klist, '-l', '-c', cc_dir], expected_msg=alice)
+realm.run(['./t_store_cred', cc_alice, cc_dir], expected_code=1,
+          expected_msg='The requested credential element already exists')
+realm.run(['./t_store_cred', '-o', cc_alice, cc_dir])
+realm.run([klist, '-c', cc_dir], expected_code=1,
+          expected_msg='No credentials cache found')
+realm.run([klist, '-l', cc_dir], expected_msg=alice)
+realm.run(['./t_store_cred', '-d', cc_bob, cc_dir])
+# The k5test klist method does not currently work with a collection name.
+realm.run([klist, cc_dir], expected_msg=bob)
+realm.run([klist, '-l', cc_dir], expected_msg=alice)
+realm.run(['./t_store_cred', '-o', '-d', cc_alice, cc_dir])
+realm.run([klist, cc_dir], expected_msg=alice)
+realm.run([kdestroy, '-A', '-c', cc_dir])
+
+mark('DIR, gss_store_cred_into()')
+realm.run(['./t_store_cred', '-i', cc_alice, cc_dir])
+realm.run(['./t_store_cred', '-i', '-d', cc_bob, cc_dir])
+realm.run([klist, cc_dir], expected_msg=bob)
+realm.run([klist, '-l', cc_dir], expected_msg=alice)
+realm.run([kdestroy, '-A', '-c', cc_dir])
+
+mark('DIR, default output ccache')
+realm.ccache = cc_dir
+realm.env['KRB5CCNAME'] = cc_dir
+realm.run(['./t_store_cred', '-i', cc_alice, cc_dir])
+realm.run(['./t_store_cred', '-i', '-d', cc_bob, cc_dir])
+realm.run([klist], expected_msg=bob)
+realm.run([klist, '-l'], expected_msg=alice)
+
+success('gss_store_cred() tests')


More information about the cvs-krb5 mailing list