krb5 commit: Improve gss_store_cred() behavior

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/3f5a348287646d65700854650fe668b9c4249013
commit 3f5a348287646d65700854650fe668b9c4249013
Author: Greg Hudson <[email protected]>
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')
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.