[SSSD] [PATCH] Support DIR: credential caches

Jakub Hrozek jhrozek at redhat.com
Wed Jun 13 13:48:09 UTC 2012


[PATCH 01/11] Two small krb5_child fixes
ssia

[PATCH 02/11] Provide more debugging in krb5_child and ldap_child
I started this patch before Nick did, maybe it would still be useful, at
least the parts that get rid of level-9 DEBUG messages.

[PATCH 03/11] Allow redefining the KRB5_CHILD path
The krb5-child-test will want to run the child from the build directory.

[PATCH 04/11] Split parse_krb5_child_response so it can be reused
krb5-child-test will be another consumer. It also makes the code more
readable by splitting a huge function.

[PATCH 05/11] Add a krb5_child test tool
https://fedorahosted.org/sssd/ticket/1127

[PATCH 06/11] Residual util functions
Kerberos credential caches can be specified by TYPE:RESIDUAL. This pa
tch adds a couple of utilities to support parsing if ccache locations,
checking types etc.

[PATCH 07/11] Handle trailing slash in the ccname template
With the DIR cache support, it's perfectly legal to specify a ccname
directory that ends with a slash. The create_dir function did not han dle
that situation correctly. The unit test is included in the DIR: cache
patch, because it uses the cc_dir_create() function.

[PATCH 08/11] Add a credential cache back end structure
To be able to add support for new credential cache types easily, this
patch creates a new structure sss_krb5_cc_be that defines common with
a credential cache, such as create, check if used or remove.

[PATCH 09/11] Add support for storing credential caches in the DIR: back end
https://fedorahosted.org/sssd/ticket/974

Please note that only the TGTs acquired by the krb5_child have changed,
the ldap_child still puts its ccache into /var/lib/sss/db.

The cc_dir_remove() function is a little odd, I tried to use the krb5
API directly, but I think I found a bug in libkrb5. For a subsidiary
cache that does not exist (DIR::/no/such/path), the following code would
segfault:

    krb5_cc_resolve(context, location, &ccache); // returns EOK
    krberr = krb5_cc_destroy(context, ccache);   // KRB5_FCC_NOFILE
    if (krberr) {
        if (ccache) krb5_cc_close(context, ccache); // SIGSEGV
    }

[PATCH 10/11] Use Kerberos context in KRB5_DEBUG
Passing Kerberos context to sss_krb5_get_error_message will allow us to
get better error messages. This patch technically belong earlier, but
rebasing would be hard at this point.

[PATCH 11/11] Switch Kerberos cache default to DIR
Just switches the defaults.
-------------- next part --------------
>From a4f14243936dafaa1a3083b311775665fa07a2b8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 28 May 2012 16:39:15 +0200
Subject: [PATCH 01/11] Two small krb5_child fixes

* Allocation check was missing
* a DEBUG statement overwrote errno
---
 src/providers/krb5/krb5_child.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 199ef67bb36c36d632ac6006871a55ebcf88123c..a9c08d6dc987e97b2af6bb4b6d1741539cfad541 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -262,12 +262,17 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
         goto done;
     }
     tmp_ccname = talloc_asprintf_append(tmp_ccname, "/.krb5cc_dummy_XXXXXX");
+    if (tmp_ccname == NULL) {
+        kerr = ENOMEM;
+        goto done;
+    }
 
     old_umask = umask(077);
     fd = mkstemp(tmp_ccname);
     umask(old_umask);
     if (fd == -1) {
-        DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
         kerr = errno;
         goto done;
     }
@@ -315,8 +320,9 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
     if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len-6), "XXXXXX")==0 ) {
         fd = mkstemp(cc_file_name);
         if (fd == -1) {
-            DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
             kerr = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("mkstemp failed [%d][%s].\n", kerr, strerror(kerr)));
             goto done;
         }
     }
-- 
1.7.10.2

-------------- next part --------------
>From 58605fd8ba06a5b780efa466bf01c62f5d6bf637 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 18 May 2012 18:57:08 +0200
Subject: [PATCH 02/11] Provide more debugging in krb5_child and ldap_child

https://fedorahosted.org/sssd/ticket/1225
---
 src/providers/krb5/krb5_child.c |   78 ++++++++++++++++++++++++++++++++-------
 src/providers/ldap/ldap_child.c |    9 ++++-
 2 files changed, 73 insertions(+), 14 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index a9c08d6dc987e97b2af6bb4b6d1741539cfad541..b6c456efb5f1776b40bd9940dddbffe503a009b6 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -163,7 +163,7 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
         return EOK;
     }
 
-    DEBUG(9, ("Prompter called with [%s].\n", banner));
+    DEBUG(SSSDBG_FUNC_DATA, ("Prompter called with [%s].\n", banner));
 
     ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
                            (const uint8_t *) banner);
@@ -236,6 +236,8 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
     TALLOC_CTX *tmp_ctx = NULL;
     mode_t old_umask;
 
+    DEBUG(SSSDBG_FUNC_DATA, ("Creating ccache at [%s]\n", ccname));
+
     if (strncmp(ccname, "FILE:", 5) == 0) {
         cc_file_name = ccname + 5;
     } else {
@@ -332,7 +334,7 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
         DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno)));
     }
 
-    DEBUG(SSSDBG_TRACE_LIBS, ("Created ccache file: [%s]\n", cc_file_name));
+    DEBUG(SSSDBG_TRACE_LIBS, ("Created ccache file: [%s]\n", ccname));
 
 done:
     if (fd != -1) {
@@ -411,6 +413,8 @@ static struct response *prepare_response_message(struct krb5_req *kr,
         return NULL;
     }
 
+    DEBUG(SSSDBG_TRACE_FUNC, ("Building response for result [%d]\n", kerr));
+
     if (kerr == 0) {
         if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
             pam_status = PAM_SUCCESS;
@@ -542,7 +546,8 @@ static krb5_error_code validate_tgt(struct krb5_req *kr)
     memset(&entry, 0, sizeof(entry));
     while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) {
         if (krb5_realm_compare(kr->ctx, entry.principal, kr->princ)) {
-            DEBUG(9, ("Found keytab entry with the realm of the credential.\n"));
+            DEBUG(SSSDBG_TRACE_INTERNAL,
+                  ("Found keytab entry with the realm of the credential.\n"));
             break;
         }
 
@@ -614,6 +619,8 @@ static void krb5_set_canonicalize(krb5_get_init_creds_opt *opts)
     if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
         canonicalize = 1;
     }
+    DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+          SSSD_KRB5_CANONICALIZE, tmp_str ? tmp_str : "not set"));
     sss_krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
 }
 
@@ -706,7 +713,7 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         }
 
     } else {
-        DEBUG(9, ("TGT validation is disabled.\n"));
+        DEBUG(SSSDBG_CONF_SETTINGS, ("TGT validation is disabled.\n"));
     }
 
     if (kr->validate || kr->fast_ccname != NULL) {
@@ -759,6 +766,8 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     char *changepw_princ = NULL;
     krb5_prompter_fct prompter = sss_krb5_prompter;
 
+    DEBUG(SSSDBG_TRACE_LIBS, ("Password change operation\n"));
+
     if (kr->pd->authtok_type != SSS_AUTHTOK_TYPE_PASSWORD) {
         pam_status = PAM_CRED_INSUFFICIENT;
         kerr = KRB5KRB_ERR_GENERIC;
@@ -780,6 +789,8 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
         kerr = KRB5KRB_ERR_GENERIC;
         goto sendresponse;
     }
+    DEBUG(SSSDBG_FUNC_DATA,
+          ("Created a changepw principal [%s]\n", changepw_princ));
 
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
         /* We do not need a password expiration warning here. */
@@ -803,8 +814,9 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     memset(kr->pd->authtok, 0, kr->pd->authtok_size);
 
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
-        DEBUG(9, ("Initial authentication for change password operation "
-                  "successfull.\n"));
+        DEBUG(SSSDBG_TRACE_LIBS,
+              ("Initial authentication for change password operation "
+               "successfull.\n"));
         krb5_free_cred_contents(kr->ctx, kr->creds);
         pam_status = PAM_SUCCESS;
         goto sendresponse;
@@ -905,7 +917,10 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
     char *changepw_princ = NULL;
     int pam_status = PAM_SYSTEM_ERR;
 
+    DEBUG(SSSDBG_TRACE_LIBS, ("Attempting to get a TGT\n"));
+
     if (kr->pd->authtok_type != SSS_AUTHTOK_TYPE_PASSWORD) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Unknown authtok type\n"));
         pam_status = PAM_CRED_INSUFFICIENT;
         kerr = KRB5KRB_ERR_GENERIC;
         goto sendresponse;
@@ -926,6 +941,8 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
         kerr = KRB5KRB_ERR_GENERIC;
         goto sendresponse;
     }
+    DEBUG(SSSDBG_FUNC_DATA,
+          ("Created a changepw principal [%s]\n", changepw_princ));
 
     kerr = get_and_save_tgt(kr, pass_str);
 
@@ -934,6 +951,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
        not. In general the password can still be used to get a changepw ticket.
        So we validate the password by trying to get a changepw ticket. */
     if (kerr == KRB5KDC_ERR_KEY_EXP) {
+        DEBUG(SSSDBG_TRACE_LIBS, ("Password was expired\n"));
         kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx,
                                                                kr->options,
                                                                NULL, NULL);
@@ -988,6 +1006,8 @@ static errno_t kuserok_child(int fd, struct krb5_req *kr)
     int ret;
     krb5_error_code kerr;
 
+    DEBUG(SSSDBG_TRACE_LIBS, ("Verifying if principal can log in as user\n"));
+
     /* krb5_kuserok tries to verify that kr->pd->user is a locally known
      * account, so we have to unset _SSS_LOOPS to make getpwnam() work. */
     ret = unsetenv("_SSS_LOOPS");
@@ -1003,6 +1023,8 @@ static errno_t kuserok_child(int fd, struct krb5_req *kr)
     }
 
     access_allowed = krb5_kuserok(kr->ctx, kr->princ, kr->pd->user);
+    DEBUG(SSSDBG_TRACE_LIBS,
+          ("Access was %s\n", access_allowed ? "allowed" : "denied"));
 
     status = access_allowed ? 0 : 1;
 
@@ -1022,6 +1044,8 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
     char *ccname;
     krb5_ccache ccache = NULL;
 
+    DEBUG(SSSDBG_TRACE_LIBS, ("Renewing a ticket\n"));
+
     if (kr->pd->authtok_type != SSS_AUTHTOK_TYPE_CCFILE) {
         DEBUG(1, ("Unsupported authtok type for TGT renewal [%d].\n",
                   kr->pd->authtok_type));
@@ -1060,7 +1084,7 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
         }
 
     } else {
-        DEBUG(9, ("TGT validation is disabled.\n"));
+        DEBUG(SSSDBG_CONF_SETTINGS, ("TGT validation is disabled.\n"));
     }
 
     if (kr->validate || kr->fast_ccname != NULL) {
@@ -1115,6 +1139,8 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
     int ret;
     int pam_status = PAM_SUCCESS;
 
+    DEBUG(SSSDBG_TRACE_LIBS, ("Creating empty ccache\n"));
+
     ret = create_ccache_file(kr->ctx, kr->princ, kr->ccname, NULL);
     if (ret != 0) {
         KRB5_DEBUG(1, ret);
@@ -1145,6 +1171,12 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
     kr->validate = (validate == 0) ? false : true;
     SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
 
+    DEBUG(SSSDBG_CONF_SETTINGS,
+          ("cmd [%d] uid [%llu] gid [%llu] validate [%s] offline [%s] "
+           "UPN [%s]\n", pd->cmd, kr->uid, kr->gid,
+           kr->validate ? "true" : "false", offline ? "true" : "false",
+           kr->upn ? kr->upn : "none"));
+
     SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
     if ((p + len ) > size) return EINVAL;
     kr->upn = talloc_strndup(pd, (char *)(buf + p), len);
@@ -1173,6 +1205,9 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
         if (pd->authtok == NULL) return ENOMEM;
         pd->authtok_size = len + 1;
         p += len;
+
+        DEBUG(SSSDBG_CONF_SETTINGS, ("ccname: [%s] keytab: [%s]\n",
+              kr->ccname, kr->keytab));
     } else {
         kr->ccname = NULL;
         kr->keytab = NULL;
@@ -1200,6 +1235,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
         pd->user = talloc_strndup(pd, (char *)(buf + p), len);
         if (pd->user == NULL) return ENOMEM;
         p += len;
+        DEBUG(SSSDBG_CONF_SETTINGS, ("user: [%s]\n", pd->user));
     } else {
         pd->user = NULL;
     }
@@ -1402,27 +1438,33 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 
     kr->krb5_ctx->realm = getenv(SSSD_KRB5_REALM);
     if (kr->krb5_ctx->realm == NULL) {
-        DEBUG(2, ("Cannot read [%s] from environment.\n", SSSD_KRB5_REALM));
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Cannot read [%s] from environment.\n", SSSD_KRB5_REALM));
     }
 
     switch(kr->pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
             /* If we are offline, we need to create an empty ccache file */
             if (offline) {
+                DEBUG(SSSDBG_TRACE_FUNC, ("Will perform offline auth\n"));
                 kr->child_req = create_empty_ccache;
             } else {
+                DEBUG(SSSDBG_TRACE_FUNC, ("Will perform online auth\n"));
                 kr->child_req = tgt_req_child;
             }
             break;
         case SSS_PAM_CHAUTHTOK:
         case SSS_PAM_CHAUTHTOK_PRELIM:
+            DEBUG(SSSDBG_TRACE_FUNC, ("Will perform password change\n"));
             kr->child_req = changepw_child;
             break;
         case SSS_PAM_ACCT_MGMT:
+            DEBUG(SSSDBG_TRACE_FUNC, ("Will perform account management\n"));
             kr->child_req = kuserok_child;
             break;
         case SSS_CMD_RENEW:
             if (!offline) {
+                DEBUG(SSSDBG_TRACE_FUNC, ("Will perform ticket renewal\n"));
                 kr->child_req = renew_tgt_child;
             } else {
                 DEBUG(1, ("Cannot renew TGT while offline.\n"));
@@ -1480,8 +1522,8 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 
     lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
     if (lifetime_str == NULL) {
-        DEBUG(7, ("Cannot read [%s] from environment.\n",
-                  SSSD_KRB5_RENEWABLE_LIFETIME));
+        DEBUG(SSSDBG_CONF_SETTINGS, ("Cannot read [%s] from environment.\n",
+              SSSD_KRB5_RENEWABLE_LIFETIME));
     } else {
         kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
         if (kerr != 0) {
@@ -1490,13 +1532,15 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
             KRB5_DEBUG(1, kerr);
             goto failed;
         }
+        DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+              SSSD_KRB5_RENEWABLE_LIFETIME, lifetime_str));
         krb5_get_init_creds_opt_set_renew_life(kr->options, lifetime);
     }
 
     lifetime_str = getenv(SSSD_KRB5_LIFETIME);
     if (lifetime_str == NULL) {
-        DEBUG(7, ("Cannot read [%s] from environment.\n",
-                  SSSD_KRB5_LIFETIME));
+        DEBUG(SSSDBG_CONF_SETTINGS, ("Cannot read [%s] from environment.\n",
+              SSSD_KRB5_LIFETIME));
     } else {
         kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
         if (kerr != 0) {
@@ -1505,6 +1549,8 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
             KRB5_DEBUG(1, kerr);
             goto failed;
         }
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              ("%s is set to [%s]\n", SSSD_KRB5_LIFETIME, lifetime_str));
         krb5_get_init_creds_opt_set_tkt_life(kr->options, lifetime);
     }
 
@@ -1513,15 +1559,19 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 
         use_fast_str = getenv(SSSD_KRB5_USE_FAST);
         if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
-            DEBUG(9, ("Not using FAST.\n"));
+            DEBUG(SSSDBG_CONF_SETTINGS, ("Not using FAST.\n"));
         } else if (strcasecmp(use_fast_str, "try") == 0 ||
                    strcasecmp(use_fast_str, "demand") == 0) {
 
+            DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+                  SSSD_KRB5_LIFETIME, lifetime_str));
             tmp_str = getenv(SSSD_KRB5_FAST_PRINCIPAL);
             if (!tmp_str) {
                 fast_principal = NULL;
                 fast_principal_realm = kr->krb5_ctx->realm;
             } else {
+                DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+                      SSSD_KRB5_FAST_PRINCIPAL, lifetime_str));
                 kerr = krb5_parse_name(kr->ctx, tmp_str, &fast_princ_struct);
                 if (kerr) {
                     DEBUG(1, ("krb5_parse_name failed.\n"));
@@ -1711,12 +1761,14 @@ int main(int argc, const char *argv[])
         goto fail;
     }
 
+    DEBUG(SSSDBG_TRACE_FUNC, ("krb5_child completed successfully\n"));
     close(STDOUT_FILENO);
     talloc_free(pd);
 
     return 0;
 
 fail:
+    DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_child failed!\n"));
     close(STDOUT_FILENO);
     talloc_free(pd);
     exit(-1);
diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
index 13e12e93c4fb28a2b4ff4eba5db8106249b446f4..83ce468fa9227f7c98e225660d2a803bd9aaf6a6 100644
--- a/src/providers/ldap/ldap_child.c
+++ b/src/providers/ldap/ldap_child.c
@@ -118,6 +118,10 @@ static int pack_buffer(struct response *r, int result, krb5_error_code krberr,
         return ENOMEM;
     }
 
+    DEBUG(SSSDBG_TRACE_LIBS,
+          ("result [%d] krberr [%d] msgsize [%d] msg [%s]\n",
+           result, krberr, len, msg));
+
     /* result */
     SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
 
@@ -183,7 +187,6 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
             krberr = KRB5KRB_ERR_GENERIC;
             goto done;
         }
-
     } else {
         realm_name = talloc_strdup(memctx, realm_str);
         if (!realm_name) {
@@ -235,6 +238,7 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
     } else {
         krberr = krb5_kt_default(context, &keytab);
     }
+    DEBUG(SSSDBG_CONF_SETTINGS, ("Using keytab [%s]\n", KEYTAB_CLEAN_NAME));
     if (krberr) {
         DEBUG(SSSDBG_FATAL_FAILURE,
               ("Failed to read keytab file [%s]: %s\n",
@@ -275,6 +279,7 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
 
     tmp_str = getenv("KRB5_CANONICALIZE");
     if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
+        DEBUG(SSSDBG_CONF_SETTINGS, ("Will canonicalize principals\n"));
         canonicalize = 1;
     }
     sss_krb5_get_init_creds_opt_set_canonicalize(&options, canonicalize);
@@ -514,11 +519,13 @@ int main(int argc, const char *argv[])
         goto fail;
     }
 
+    DEBUG(SSSDBG_TRACE_FUNC, ("ldap_child completed successfully\n"));
     close(STDOUT_FILENO);
     talloc_free(main_ctx);
     _exit(0);
 
 fail:
+    DEBUG(SSSDBG_CRIT_FAILURE, ("ldap_child failed!\n"));
     close(STDOUT_FILENO);
     talloc_free(main_ctx);
     _exit(-1);
-- 
1.7.10.2

-------------- next part --------------
>From 6ba398745cecaebf1bb59b2dc6db258c85fba3c8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 23 May 2012 17:50:21 +0200
Subject: [PATCH 03/11] Allow redefining the KRB5_CHILD path

The krb5-child-test will want to run the child from the current
directory.
---
 src/providers/krb5/krb5_child_handler.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 990a9eccbb433147bebb324c65ce5c441aeb7f73..87f9d3cab1d36676d07058673a078ee2c6ba907b 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -28,11 +28,15 @@
 #include "providers/krb5/krb5_auth.h"
 #include "src/providers/krb5/krb5_utils.h"
 
+#ifndef KRB5_CHILD_DIR
 #ifndef SSSD_LIBEXEC_PATH
 #error "SSSD_LIBEXEC_PATH not defined"
-#else
-#define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child"
-#endif
+#endif  /* SSSD_LIBEXEC_PATH */
+
+#define KRB5_CHILD_DIR SSSD_LIBEXEC_PATH
+#endif /* KRB5_CHILD_DIR */
+
+#define KRB5_CHILD KRB5_CHILD_DIR"/krb5_child"
 
 struct io {
     int read_from_child_fd;
-- 
1.7.10.2

-------------- next part --------------
>From 3b7d5cf6405a105f5a9520f315ada9fc0519690c Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 23 May 2012 17:51:24 +0200
Subject: [PATCH 04/11] Split parse_krb5_child_response so it can be reused

krb5-child-test will be another consumer. It also makes the code more
readable by splitting a huge function.
---
 src/providers/krb5/krb5_auth.c          |  146 ++++++-------------------------
 src/providers/krb5/krb5_auth.h          |   11 +++
 src/providers/krb5/krb5_child_handler.c |  132 ++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+), 119 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 50028e15d5cf9133b9fac11b45c1adda2e2fceb9..7c622dbea6869f1acf510104498a304f634c21b5 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -39,9 +39,6 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
 
-#define TIME_T_MAX LONG_MAX
-#define int64_to_time_t(val) ((time_t)((val) < TIME_T_MAX ? val : TIME_T_MAX))
-
 static errno_t safe_remove_old_ccache_file(const char *old_ccache_file,
                                            const char *new_ccache_file)
 {
@@ -727,23 +724,7 @@ static void krb5_child_done(struct tevent_req *subreq)
     int ret;
     uint8_t *buf = NULL;
     ssize_t len = -1;
-    ssize_t pref_len;
-    size_t p;
-    int32_t msg_status;
-    int32_t msg_type;
-    int32_t msg_len;
-    int64_t time_data;
-    struct tgt_times tgtt;
-    int pwd_exp_warning;
-    uint32_t *expiration;
-    uint32_t *msg_subtype;
-    bool skip;
-
-    memset(&tgtt, 0, sizeof(tgtt));
-    pwd_exp_warning = state->be_ctx->domain->pwd_expiration_warning;
-    if (pwd_exp_warning < 0) {
-        pwd_exp_warning = KERBEROS_PWEXPIRE_WARNING_TIME;
-    }
+    struct krb5_child_response *res;
 
     ret = handle_child_recv(subreq, pd, &buf, &len);
     talloc_zfree(subreq);
@@ -759,114 +740,41 @@ static void krb5_child_done(struct tevent_req *subreq)
         return;
     }
 
-    /* A buffer with the following structure is expected.
-     * int32_t status of the request (required)
-     * message (zero or more)
-     *
-     * A message consists of:
-     * int32_t type of the message
-     * int32_t length of the following data
-     * uint8_t[len] data
-     */
-
-    if ((size_t) len < sizeof(int32_t)) {
-        DEBUG(1, ("message too short.\n"));
-        ret = EINVAL;
+    ret = parse_krb5_child_response(state, buf, len, pd,
+                        state->be_ctx->domain->pwd_expiration_warning,
+                        &res);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not parse child response [%d]: %s\n",
+              ret, strerror(ret)));
         goto done;
     }
 
-    p=0;
-    SAFEALIGN_COPY_INT32(&msg_status, buf+p, &p);
-
-    while (p < len) {
-        skip = false;
-        SAFEALIGN_COPY_INT32(&msg_type, buf+p, &p);
-        SAFEALIGN_COPY_INT32(&msg_len, buf+p, &p);
-
-        DEBUG(9, ("child response [%d][%d][%d].\n", msg_status, msg_type,
-                                                    msg_len));
-
-        if ((p + msg_len) > len) {
-            DEBUG(1, ("message format error [%d] > [%d].\n", p+msg_len, len));
-            ret = EINVAL;
-            goto done;
-        }
-
-        /* We need to save the name of the credential cache file. To find it
-         * we check if the data part of a message starts with
-         * CCACHE_ENV_NAME"=". pref_len also counts the trailing '=' because
-         * sizeof() counts the trailing '\0' of a string. */
-        pref_len = sizeof(CCACHE_ENV_NAME);
-        if (msg_len > pref_len &&
-            strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) {
-            kr->ccname = talloc_strndup(kr, (char *) &buf[p+pref_len],
-                                        msg_len-pref_len);
-            if (kr->ccname == NULL) {
-                DEBUG(1, ("talloc_strndup failed.\n"));
-                ret = ENOMEM;
-                goto done;
-            }
-        }
-
-        if (msg_type == SSS_KRB5_INFO_TGT_LIFETIME &&
-            msg_len == 4*sizeof(int64_t)) {
-            SAFEALIGN_COPY_INT64(&time_data, buf+p, NULL);
-            tgtt.authtime = int64_to_time_t(time_data);
-            SAFEALIGN_COPY_INT64(&time_data, buf+p+sizeof(int64_t), NULL);
-            tgtt.starttime = int64_to_time_t(time_data);
-            SAFEALIGN_COPY_INT64(&time_data, buf+p+2*sizeof(int64_t), NULL);
-            tgtt.endtime = int64_to_time_t(time_data);
-            SAFEALIGN_COPY_INT64(&time_data, buf+p+3*sizeof(int64_t), NULL);
-            tgtt.renew_till = int64_to_time_t(time_data);
-            DEBUG(7, ("TGT times are [%d][%d][%d][%d].\n", tgtt.authtime,
-                      tgtt.starttime, tgtt.endtime, tgtt.renew_till));
-        }
-
-        if (msg_type == SSS_PAM_USER_INFO) {
-            msg_subtype = (uint32_t *)&buf[p];
-            if (*msg_subtype == SSS_PAM_USER_INFO_EXPIRE_WARN)
-            {
-                expiration = (uint32_t *)&buf[p+sizeof(uint32_t)];
-                if (pwd_exp_warning > 0 &&
-                    difftime(pwd_exp_warning, *expiration) < 0.0) {
-                    skip = true;
-                }
-            }
-        }
-
-        if (!skip) {
-            ret = pam_add_response(pd, msg_type, msg_len, &buf[p]);
-            if (ret != EOK) {
-                /* This is not a fatal error */
-                DEBUG(1, ("pam_add_response failed.\n"));
-            }
-        }
-        p += msg_len;
-
-        if ((p < len) && (p + 2*sizeof(int32_t) >= len)) {
-            DEBUG(1, ("The remainder of the message is too short.\n"));
-            ret = EINVAL;
+    if (res->ccname) {
+        kr->ccname = talloc_strdup(kr, res->ccname);
+        if (!kr->ccname) {
+            ret = ENOMEM;
             goto done;
         }
     }
 
     /* If the child request failed, but did not return an offline error code,
      * return with the status */
-    if (msg_status != PAM_SUCCESS && msg_status != PAM_AUTHINFO_UNAVAIL &&
-        msg_status != PAM_AUTHTOK_LOCK_BUSY &&
-        msg_status != PAM_NEW_AUTHTOK_REQD) {
-        state->pam_status = msg_status;
+    if (res->msg_status != PAM_SUCCESS &&
+        res->msg_status != PAM_AUTHINFO_UNAVAIL &&
+        res->msg_status != PAM_AUTHTOK_LOCK_BUSY &&
+        res->msg_status != PAM_NEW_AUTHTOK_REQD) {
+        state->pam_status = res->msg_status;
         state->dp_err = DP_ERR_OK;
         ret = EOK;
         goto done;
     } else {
-        state->pam_status = msg_status;
+        state->pam_status = res->msg_status;
     }
 
     /* If the password is expired we can safely remove the ccache from the
      * cache and disk if it is not actively used anymore. This will allow to
      * create a new random ccache if sshd with privilege separation is used. */
-    if (msg_status == PAM_NEW_AUTHTOK_REQD) {
+    if (res->msg_status == PAM_NEW_AUTHTOK_REQD) {
         if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache_present) {
             if (kr->old_ccname != NULL) {
                 ret = safe_remove_old_ccache_file(kr->old_ccname, "dummy");
@@ -883,7 +791,7 @@ static void krb5_child_done(struct tevent_req *subreq)
             }
         }
 
-        state->pam_status = msg_status;
+        state->pam_status = res->msg_status;
         state->dp_err = DP_ERR_OK;
         ret = EOK;
         goto done;
@@ -891,7 +799,7 @@ static void krb5_child_done(struct tevent_req *subreq)
 
     /* If the child request was successful and we run the first pass of the
      * change password request just return success. */
-    if (msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
+    if (res->msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) {
         state->pam_status = PAM_SUCCESS;
         state->dp_err = DP_ERR_OK;
         ret = EOK;
@@ -902,7 +810,7 @@ static void krb5_child_done(struct tevent_req *subreq)
     if (kr->kpasswd_srv != NULL &&
         (pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
         /* ..which is unreachable by now.. */
-        if (msg_status == PAM_AUTHTOK_LOCK_BUSY) {
+        if (res->msg_status == PAM_AUTHTOK_LOCK_BUSY) {
             be_fo_set_port_status(state->be_ctx,
                                   kr->kpasswd_srv, PORT_NOT_WORKING);
             /* ..try to resolve next kpasswd server */
@@ -919,8 +827,8 @@ static void krb5_child_done(struct tevent_req *subreq)
     /* if the KDC for auth (PAM_AUTHINFO_UNAVAIL) or
      * chpass (PAM_AUTHTOK_LOCK_BUSY) was not available while using KDC
      * also for chpass operation... */
-    if (msg_status == PAM_AUTHINFO_UNAVAIL ||
-        (kr->kpasswd_srv == NULL && msg_status == PAM_AUTHTOK_LOCK_BUSY)) {
+    if (res->msg_status == PAM_AUTHINFO_UNAVAIL ||
+        (kr->kpasswd_srv == NULL && res->msg_status == PAM_AUTHTOK_LOCK_BUSY)) {
         if (kr->srv != NULL) {
             be_fo_set_port_status(state->be_ctx, kr->srv, PORT_NOT_WORKING);
             /* ..try to resolve next KDC */
@@ -958,14 +866,14 @@ static void krb5_child_done(struct tevent_req *subreq)
         goto done;
     }
 
-    if (msg_status == PAM_SUCCESS &&
+    if (res->msg_status == PAM_SUCCESS &&
         dp_opt_get_int(kr->krb5_ctx->opts, KRB5_RENEW_INTERVAL) > 0 &&
         (pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_CMD_RENEW ||
          pd->cmd == SSS_PAM_CHAUTHTOK) &&
-        tgtt.renew_till > tgtt.endtime && kr->ccname != NULL) {
+        res->tgtt.renew_till > res->tgtt.endtime && kr->ccname != NULL) {
         DEBUG(7, ("Adding [%s] for automatic renewal.\n", kr->ccname));
-        ret = add_tgt_to_renew_table(kr->krb5_ctx, kr->ccname, &tgtt, pd,
-                                     kr->upn);
+        ret = add_tgt_to_renew_table(kr->krb5_ctx, kr->ccname, &(res->tgtt),
+                                     pd, kr->upn);
         if (ret != EOK) {
             DEBUG(1, ("add_tgt_to_renew_table failed, "
                       "automatic renewal not possible.\n"));
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 89b77d3663d2aec8dbf73178ca6827213f94d89d..5cc22dfe8171d1f83a1d6ef63313222f66e302d4 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -74,6 +74,17 @@ struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx,
 int handle_child_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                       uint8_t **buf, ssize_t *len);
 
+struct krb5_child_response {
+    int32_t msg_status;
+    struct tgt_times tgtt;
+    char *ccname;
+};
+
+errno_t
+parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
+                          struct pam_data *pd, int pwd_exp_warning,
+                          struct krb5_child_response **_res);
+
 errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
                                                   struct pam_data *pd,
                                                   uid_t uid);
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 87f9d3cab1d36676d07058673a078ee2c6ba907b..6343f3b5355b62f50b4412a737b81a701538c143 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -38,6 +38,9 @@
 
 #define KRB5_CHILD KRB5_CHILD_DIR"/krb5_child"
 
+#define TIME_T_MAX LONG_MAX
+#define int64_to_time_t(val) ((time_t)((val) < TIME_T_MAX ? val : TIME_T_MAX))
+
 struct io {
     int read_from_child_fd;
     int write_to_child_fd;
@@ -415,3 +418,132 @@ int handle_child_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 
     return EOK;
 }
+
+errno_t
+parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
+                          struct pam_data *pd, int pwd_exp_warning,
+                          struct krb5_child_response **_res)
+{
+    ssize_t pref_len;
+    size_t p;
+    errno_t ret;
+    bool skip;
+    char *ccname = NULL;
+    size_t ccname_len;
+    int32_t msg_status;
+    int32_t msg_type;
+    int32_t msg_len;
+    int64_t time_data;
+    struct tgt_times tgtt;
+    uint32_t *expiration;
+    uint32_t *msg_subtype;
+    struct krb5_child_response *res;
+
+    if ((size_t) len < sizeof(int32_t)) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("message too short.\n"));
+        return EINVAL;
+    }
+
+    if (pwd_exp_warning < 0) {
+        pwd_exp_warning = KERBEROS_PWEXPIRE_WARNING_TIME;
+    }
+
+    /* A buffer with the following structure is expected.
+     * int32_t status of the request (required)
+     * message (zero or more)
+     *
+     * A message consists of:
+     * int32_t type of the message
+     * int32_t length of the following data
+     * uint8_t[len] data
+     */
+
+    p=0;
+    SAFEALIGN_COPY_INT32(&msg_status, buf+p, &p);
+
+    while (p < len) {
+        skip = false;
+        SAFEALIGN_COPY_INT32(&msg_type, buf+p, &p);
+        SAFEALIGN_COPY_INT32(&msg_len, buf+p, &p);
+
+        DEBUG(SSSDBG_TRACE_LIBS, ("child response [%d][%d][%d].\n",
+              msg_status, msg_type, msg_len));
+
+        if ((p + msg_len) > len) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("message format error [%d] > [%d].\n",
+                  p+msg_len, len));
+            return EINVAL;
+        }
+
+        /* We need to save the name of the credential cache file. To find it
+         * we check if the data part of a message starts with
+         * CCACHE_ENV_NAME"=". pref_len also counts the trailing '=' because
+         * sizeof() counts the trailing '\0' of a string. */
+        pref_len = sizeof(CCACHE_ENV_NAME);
+        if (msg_len > pref_len &&
+            strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) {
+            ccname = (char *) &buf[p+pref_len];
+            ccname_len = msg_len-pref_len;
+        }
+
+        if (msg_type == SSS_KRB5_INFO_TGT_LIFETIME &&
+            msg_len == 4*sizeof(int64_t)) {
+            SAFEALIGN_COPY_INT64(&time_data, buf+p, NULL);
+            tgtt.authtime = int64_to_time_t(time_data);
+            SAFEALIGN_COPY_INT64(&time_data, buf+p+sizeof(int64_t), NULL);
+            tgtt.starttime = int64_to_time_t(time_data);
+            SAFEALIGN_COPY_INT64(&time_data, buf+p+2*sizeof(int64_t), NULL);
+            tgtt.endtime = int64_to_time_t(time_data);
+            SAFEALIGN_COPY_INT64(&time_data, buf+p+3*sizeof(int64_t), NULL);
+            tgtt.renew_till = int64_to_time_t(time_data);
+            DEBUG(SSSDBG_TRACE_LIBS, ("TGT times are [%d][%d][%d][%d].\n",
+                  tgtt.authtime, tgtt.starttime, tgtt.endtime, tgtt.renew_till));
+        }
+
+        if (msg_type == SSS_PAM_USER_INFO) {
+            msg_subtype = (uint32_t *)&buf[p];
+            if (*msg_subtype == SSS_PAM_USER_INFO_EXPIRE_WARN)
+            {
+                expiration = (uint32_t *)&buf[p+sizeof(uint32_t)];
+                if (pwd_exp_warning > 0 &&
+                    difftime(pwd_exp_warning, *expiration) < 0.0) {
+                    skip = true;
+                }
+            }
+        }
+
+        if (!skip) {
+            ret = pam_add_response(pd, msg_type, msg_len, &buf[p]);
+            if (ret != EOK) {
+                /* This is not a fatal error */
+                DEBUG(SSSDBG_CRIT_FAILURE, ("pam_add_response failed.\n"));
+            }
+        }
+
+        p += msg_len;
+
+        if ((p < len) && (p + 2*sizeof(int32_t) >= len)) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("The remainder of the message is too short.\n"));
+            return EINVAL;
+        }
+    }
+
+    res = talloc_zero(mem_ctx, struct krb5_child_response);
+    if (!res) return ENOMEM;
+
+    res->msg_status = msg_status;
+    memcpy(&res->tgtt, &tgtt, sizeof(tgtt));
+
+    if (ccname) {
+        res->ccname = talloc_strndup(res, ccname, ccname_len);
+        if (res->ccname == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strndup failed.\n"));
+            talloc_free(res);
+            return ENOMEM;
+        }
+    }
+
+    *_res = res;
+    return EOK;
+}
-- 
1.7.10.2

-------------- next part --------------
>From f0b4ac845cd0a480122340703d5d30066d6393ba Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 22 May 2012 00:17:32 +0200
Subject: [PATCH 05/11] Add a krb5_child test tool

https://fedorahosted.org/sssd/ticket/1127
---
 Makefile.am                 |   24 ++
 src/tests/krb5_child-test.c |  551 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 575 insertions(+)
 create mode 100644 src/tests/krb5_child-test.c

diff --git a/Makefile.am b/Makefile.am
index a6c2f909846011e81270e3f99e771e3f1c11bedb..f5366a36c607a1d2bb75152b64e73e93ba6b7ff9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,7 @@ endif
 
 check_PROGRAMS = \
     stress-tests \
+    krb5-child-test \
     $(non_interactive_check_based_tests)
 
 PYTHON_TESTS =
@@ -972,6 +973,29 @@ stress_tests_LDADD = \
     libsss_util.la \
     libsss_test_common.la
 
+krb5_child_test_SOURCES = \
+    src/tests/krb5_child-test.c \
+    src/providers/krb5/krb5_utils.c \
+    src/providers/krb5/krb5_child_handler.c \
+    src/providers/krb5/krb5_become_user.c \
+    src/providers/krb5/krb5_common.c \
+    src/util/sss_krb5.c \
+    src/providers/data_provider_fo.c \
+    src/providers/data_provider_opts.c \
+    src/providers/data_provider_callbacks.c \
+    $(SSSD_FAILOVER_OBJ)
+krb5_child_test_CFLAGS = \
+    $(AM_CFLAGS) \
+    -DKRB5_CHILD_DIR=\"$(builddir)\" \
+    $(CHECK_CFLAGS)
+krb5_child_test_LDADD = \
+    $(SSSD_LIBS)\
+    $(CARES_LIBS) \
+    $(KRB5_LIBS) \
+    $(CHECK_LIBS) \
+    libsss_util.la \
+    libsss_test_common.la
+
 noinst_PROGRAMS = pam_test_client
 if BUILD_SUDO
 noinst_PROGRAMS += sss_sudo_cli
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
new file mode 100644
index 0000000000000000000000000000000000000000..a1acfbb4509d47fd9f5b1bd5a65e7740849b9bbb
--- /dev/null
+++ b/src/tests/krb5_child-test.c
@@ -0,0 +1,551 @@
+/*
+    SSSD
+
+    Unit tests - exercise the krb5 child
+
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2012 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <popt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "util/util.h"
+#include "src/tools/tools_util.h"
+
+/* Interfaces being tested */
+#include "providers/krb5/krb5_auth.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_utils.h"
+
+extern struct dp_option default_krb5_opts[];
+
+static krb5_context krb5_error_ctx;
+#define KRB5_DEBUG(level, krb5_error) do { \
+    const char * __krb5_error_msg; \
+    __krb5_error_msg = sss_krb5_get_error_message(krb5_error_ctx, krb5_error); \
+    DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
+    sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
+    sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
+} while(0)
+
+#define CHECK_KRET(kret, err) do {              \
+    if (kret) {                                 \
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);    \
+        return err;                             \
+    }                                           \
+} while(0)                                      \
+
+#define CHECK_KRET_L(kret, err, label) do {     \
+    if (kret) {                                 \
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);    \
+        goto label;                             \
+    }                                           \
+} while(0)                                      \
+
+struct krb5_child_test_ctx {
+    struct tevent_context *ev;
+    struct krb5child_req *kr;
+
+    bool done;
+    errno_t child_ret;
+
+    uint8_t *buf;
+    ssize_t len;
+    struct krb5_child_response *res;
+};
+
+static errno_t
+setup_krb5_child_test(TALLOC_CTX *mem_ctx, struct krb5_child_test_ctx **_ctx)
+{
+    struct krb5_child_test_ctx *ctx;
+
+    ctx = talloc_zero(mem_ctx, struct krb5_child_test_ctx);
+    if (!ctx) return ENOMEM;
+
+    ctx->ev = tevent_context_init(ctx);
+    if (ctx->ev == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not init tevent context"));
+        talloc_free(ctx);
+        return EFAULT;
+    }
+
+    *_ctx = ctx;
+    return EOK;
+}
+
+int re_destructor(void *memctx)
+{
+    struct krb5_ctx *ctx = (struct krb5_ctx *) memctx;
+
+    if (ctx->illegal_path_re) {
+        pcre_free(ctx->illegal_path_re);
+        ctx->illegal_path_re = NULL;
+    }
+    return 0;
+}
+
+static struct krb5_ctx *
+create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm)
+{
+    struct krb5_ctx *krb5_ctx;
+    const char *errstr;
+    int errval;
+    int errpos;
+    int i;
+    errno_t ret;
+
+    krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx);
+    if (!krb5_ctx) return NULL;
+
+    krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+                                        &errval, &errstr, &errpos, NULL);
+    if (krb5_ctx->illegal_path_re == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Invalid Regular Expression pattern at position %d. "
+               "(Error: %d [%s])\n", errpos, errval, errstr));
+        goto fail;
+    }
+    talloc_set_destructor((TALLOC_CTX *) krb5_ctx, re_destructor);
+
+    /* Kerberos options */
+    krb5_ctx->opts = talloc_zero_array(krb5_ctx, struct dp_option, KRB5_OPTS);
+    if (!krb5_ctx->opts) goto fail;
+    for (i = 0; i < KRB5_OPTS; i++) {
+        krb5_ctx->opts[i].opt_name = default_krb5_opts[i].opt_name;
+        krb5_ctx->opts[i].type = default_krb5_opts[i].type;
+        krb5_ctx->opts[i].def_val = default_krb5_opts[i].def_val;
+        switch (krb5_ctx->opts[i].type) {
+            case DP_OPT_STRING:
+                ret = dp_opt_set_string(krb5_ctx->opts, i,
+                                        default_krb5_opts[i].def_val.string);
+                break;
+            case DP_OPT_BLOB:
+                ret = dp_opt_set_blob(krb5_ctx->opts, i,
+                                      default_krb5_opts[i].def_val.blob);
+                break;
+            case DP_OPT_NUMBER:
+                ret = dp_opt_set_int(krb5_ctx->opts, i,
+                                     default_krb5_opts[i].def_val.number);
+                break;
+            case DP_OPT_BOOL:
+                ret = dp_opt_set_bool(krb5_ctx->opts, i,
+                                      default_krb5_opts[i].def_val.boolean);
+                break;
+        }
+        if (ret) goto fail;
+    }
+
+    ret = dp_opt_set_string(krb5_ctx->opts, KRB5_REALM, realm);
+    if (ret) goto fail;
+
+    return krb5_ctx;
+
+fail:
+    talloc_free(krb5_ctx);
+    return NULL;
+}
+
+static struct pam_data *
+create_dummy_pam_data(TALLOC_CTX *mem_ctx, const char *user,
+                      const char *password)
+{
+    struct pam_data *pd;
+
+    pd = talloc_zero(mem_ctx, struct pam_data);
+    if (!pd) goto fail;
+
+    pd->cmd = SSS_PAM_AUTHENTICATE;
+    pd->user = talloc_strdup(pd, user);
+    if (!pd->user) goto fail;
+
+    pd->authtok = discard_const(talloc_strdup(pd, password));
+    if (!pd->authtok) goto fail;
+    pd->authtok_size = strlen(password);
+    pd->authtok_type = SSS_AUTHTOK_TYPE_PASSWORD;
+    DEBUG(SSSDBG_FUNC_DATA, ("Authtok [%s] len [%d]\n",
+          pd->authtok, pd->authtok_size));
+
+    return pd;
+
+fail:
+    talloc_free(pd);
+    return NULL;
+}
+
+static struct krb5child_req *
+create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
+                 const char *password, const char *realm,
+                 const char *ccname, const char *ccname_template,
+                 int timeout)
+{
+    struct krb5child_req *kr;
+    struct passwd *pwd;
+    bool private;
+    errno_t ret;
+
+    /* The top level child request */
+    kr = talloc_zero(mem_ctx, struct krb5child_req);
+    if (!kr) return NULL;
+
+    pwd = getpwnam(user);
+    if (!pwd) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              ("Cannot get info on user [%s]\n", user));
+        goto fail;
+    }
+
+    kr->uid = pwd->pw_uid;
+    kr->gid = pwd->pw_gid;
+
+    /* The Kerberos context */
+    kr->krb5_ctx = create_dummy_krb5_ctx(kr, realm);
+    /* PAM Data structure */
+    kr->pd = create_dummy_pam_data(kr, user, password);
+
+    ret = krb5_get_simple_upn(kr, kr->krb5_ctx, kr->pd->user, &kr->upn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_get_simple_upn failed.\n"));
+        goto fail;
+    }
+
+    /* Override options with what was provided by the user */
+    if (ccname_template) {
+        ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL,
+                                ccname_template);
+        if (ret != EOK) goto fail;
+    }
+
+    if (timeout) {
+        dp_opt_set_int(kr->krb5_ctx->opts, KRB5_AUTH_TIMEOUT, timeout);
+    }
+
+    if (!ccname) {
+        kr->ccname = expand_ccname_template(kr, kr,
+                                        dp_opt_get_cstring(kr->krb5_ctx->opts,
+                                                           KRB5_CCNAME_TMPL),
+                                            true, true, &private);
+        if (!kr->ccname) goto fail;
+
+        DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
+              kr->ccname, kr->uid, kr->gid));
+
+        ret = create_ccache_dir(kr, kr->ccname,
+                                kr->krb5_ctx->illegal_path_re,
+                                kr->uid, kr->gid, private);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
+            goto fail;
+        }
+    }
+
+    return kr;
+
+fail:
+    talloc_free(kr);
+    return NULL;
+}
+
+static void
+child_done(struct tevent_req *req)
+{
+    struct krb5_child_test_ctx *ctx = tevent_req_callback_data(req,
+                                    struct krb5_child_test_ctx);
+    errno_t ret;
+
+    ret = handle_child_recv(req, ctx, &ctx->buf, &ctx->len);
+    talloc_free(req);
+    ctx->done = true;
+    ctx->child_ret = ret;
+}
+
+static void
+printtime(krb5_timestamp ts)
+{
+    krb5_error_code kret;
+    char timestring[BUFSIZ];
+    char fill = '\0';
+
+    kret = krb5_timestamp_to_sfstring(ts, timestring, BUFSIZ, &fill);
+    if (kret) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);
+    }
+    printf("%s", timestring);
+}
+
+static void
+print_creds(krb5_context kcontext, krb5_creds *cred, const char *defname)
+{
+    krb5_error_code kret;
+    char *name = NULL;
+    char *sname = NULL;
+
+    kret = krb5_unparse_name(kcontext, cred->client, &name);
+    CHECK_KRET_L(kret, EIO, done);
+
+    kret = krb5_unparse_name(kcontext, cred->server, &sname);
+    CHECK_KRET_L(kret, EIO, done);
+
+    if (!cred->times.starttime) {
+        cred->times.starttime = cred->times.authtime;
+    }
+
+
+    printf("\t\t%s\n", sname);
+    printf("\t\tValid from\t");  printtime(cred->times.starttime);
+    printf("\n\t\tValid until\t"); printtime(cred->times.endtime);
+    printf("\n");
+
+    if (strcmp(name, defname)) {
+        printf("\t\tfor client %s", name);
+    }
+
+done:
+    krb5_free_unparsed_name(kcontext, name);
+    krb5_free_unparsed_name(kcontext, sname);
+}
+
+static errno_t
+print_ccache(const char *cc)
+{
+    krb5_cc_cursor cur;
+    krb5_ccache cache = NULL;
+    krb5_error_code kret;
+    krb5_context kcontext = NULL;
+    krb5_principal_data *princ = NULL;
+    krb5_creds creds;
+    char *defname = NULL;
+    int i = 1;
+    errno_t ret;
+
+    kret = krb5_init_context(&kcontext);
+    CHECK_KRET_L(kret, EIO, done);
+
+    kret = krb5_cc_resolve(kcontext, cc, &cache);
+    CHECK_KRET_L(kret, EIO, done);
+
+    kret = krb5_cc_get_principal(kcontext, cache, &princ);
+    CHECK_KRET_L(kret, EIO, done);
+
+    kret = krb5_unparse_name(kcontext, princ, &defname);
+    CHECK_KRET_L(kret, EIO, done);
+
+    printf("\nTicket cache: %s:%s\nDefault principal: %s\n\n",
+           krb5_cc_get_type(kcontext, cache),
+           krb5_cc_get_name(kcontext, cache), defname);
+
+    kret = krb5_cc_start_seq_get(kcontext, cache, &cur);
+    CHECK_KRET_L(kret, EIO, done);
+
+    while (!(kret = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
+        printf("Ticket #%d:\n", i);
+        print_creds(kcontext, &creds, defname);
+        krb5_free_cred_contents(kcontext, &creds);
+    }
+
+    krb5_cc_end_seq_get(kcontext, cache, &cur);
+    CHECK_KRET_L(kret, EIO, done);
+
+    ret = EOK;
+done:
+    krb5_cc_close(kcontext, cache);
+    krb5_free_unparsed_name(kcontext, defname);
+    krb5_free_principal(kcontext, princ);
+    krb5_free_context(kcontext);
+    return ret;
+}
+
+static void
+remove_ccache(const char *cc)
+{
+    size_t offset = 0;
+    errno_t ret;
+
+    if (strncmp(cc, "FILE:", 5) == 0) {
+        offset = 5;
+    }
+    if (cc[offset] != '/') {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              ("ccname [%s] does not contain absolute path?\n", cc));
+    }
+
+    errno = 0;
+    ret = unlink(cc+offset);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              ("unlink [%s] failed [%d]: %s\n", cc, ret, strerror(ret)));
+    }
+}
+
+int
+main(int argc, const char *argv[])
+{
+    int opt;
+    errno_t ret;
+    struct krb5_child_test_ctx *ctx = NULL;
+    struct tevent_req *req;
+
+    int pc_debug = 0;
+    int pc_timeout = 0;
+    const char *pc_user = NULL;;
+    const char *pc_passwd = NULL;;
+    const char *pc_realm = NULL;;
+    const char *pc_ccname = NULL;;
+    const char *pc_ccname_tp = NULL;;
+    char *password = NULL;
+    bool rm_ccache = true;
+
+    poptContext pc;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0,
+          "The debug level to run with", NULL },
+        { "user", 'u', POPT_ARG_STRING, &pc_user, 0,
+          "The user to log in as", NULL },
+        { "password", 'w', POPT_ARG_STRING, &pc_passwd, 0,
+          "The authtok to use", NULL },
+        { "ask-password", 'W', POPT_ARG_NONE, NULL, 'W',
+          "Ask interactively for authtok", NULL },
+        { "ccname", 'c', POPT_ARG_STRING, &pc_ccname, 0,
+           "Force usage of a certain credential cache", NULL },
+        { "ccname-template", 't', POPT_ARG_STRING, &pc_ccname_tp, 0,
+           "Specify the credential cache template", NULL },
+        { "realm", 'r', POPT_ARG_STRING, &pc_realm, 0,
+          "The Kerberos realm to use", NULL },
+        { "keep-ccache", 'k', POPT_ARG_NONE, NULL, 'k',
+          "Do not delete the ccache when the tool finishes", NULL },
+        { "timeout", '\0', POPT_ARG_INT, &pc_timeout, 0,
+          "The timeout for the child, in secods", NULL },
+        POPT_TABLEEND
+    };
+
+    debug_prg_name = argv[0];
+    pc = poptGetContext(NULL, argc, argv, long_options, 0);
+
+    while ((opt = poptGetNextOpt(pc)) > 0) {
+        switch(opt) {
+        case 'W':
+            errno = 0;
+            password = getpass("Enter password:");
+            if (!password) {
+                return 1;
+            }
+            break;
+        case 'k':
+            rm_ccache = false;
+            break;
+        default:
+            DEBUG(SSSDBG_FATAL_FAILURE, ("Unexpected option\n"));
+            return 1;
+        }
+    }
+
+    debug_level = debug_convert_old_level(pc_debug);
+
+    if (opt != -1) {
+        poptPrintUsage(pc, stderr, 0);
+        fprintf(stderr, "%s", poptStrerror(opt));
+        return 1;
+    }
+
+    if (!pc_user) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Please specify the user\n"));
+        poptPrintUsage(pc, stderr, 0);
+        return 1;
+    }
+
+    if (!pc_realm) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Please specify the realm\n"));
+        poptPrintUsage(pc, stderr, 0);
+        return 1;
+    }
+
+    if (!password && !pc_passwd) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              ("Password was not provided or asked for\n"));
+        poptPrintUsage(pc, stderr, 0);
+        return 1;
+    }
+
+    if (pc_ccname && pc_ccname_tp) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Both ccname and ccname template specified, "
+               "will prefer ccname\n"));
+    }
+
+    ret = setup_krb5_child_test(NULL, &ctx);
+    if (ret != EOK) {
+        poptPrintUsage(pc, stderr, 0);
+        fprintf(stderr, "%s", poptStrerror(opt));
+        return 3;
+    }
+
+    ctx->kr = create_dummy_req(ctx, pc_user, password ? password : pc_passwd,
+                               pc_realm, pc_ccname, pc_ccname_tp, pc_timeout);
+    if (!ctx->kr) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot create Kerberos request\n"));
+        ret = 4;
+        goto done;
+    }
+
+    req = handle_child_send(ctx, ctx->ev, ctx->kr);
+    if (!req) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot create child request\n"));
+        ret = 4;
+        goto done;
+    }
+    tevent_req_set_callback(req, child_done, ctx);
+
+    while (ctx->done == false) {
+         tevent_loop_once(ctx->ev);
+    }
+
+    printf("Child returned %d\n", ctx->child_ret);
+
+    ret = parse_krb5_child_response(ctx, ctx->buf, ctx->len,
+                                    ctx->kr->pd, 0, &ctx->res);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Could not parse child response\n"));
+        ret = 5;
+        goto done;
+    }
+
+    if (!ctx->res->ccname) {
+        fprintf(stderr, "No ccname returned\n");
+        ret = 6;
+        goto done;
+    }
+
+    print_ccache(ctx->res->ccname);
+
+    ret = 0;
+done:
+    if (rm_ccache && ctx->res && ctx->res->ccname) {
+        remove_ccache(ctx->res->ccname);
+    }
+    free(password);
+    talloc_free(ctx);
+    poptFreeContext(pc);
+    return ret;
+}
-- 
1.7.10.2

-------------- next part --------------
>From 988a4df77e3771c7cff1bf656196077c8b583127 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 17 May 2012 13:49:30 +0200
Subject: [PATCH 06/11] Residual util functions

Kerberos credential caches can be specified by TYPE:RESIDUAL. This patch
adds a couple of utilities to support parsing if ccache locations,
checking types etc.
---
 src/util/sss_krb5.c |   86 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/sss_krb5.h |   16 ++++++++++
 2 files changed, 102 insertions(+)

diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index 81a1623ef9df340d7618bdf55c1707ce4cfb1a6a..5bc1140d0778882aa001a4a4504ddca42a63a4f8 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -1124,3 +1124,89 @@ sss_krb5_read_etypes_for_keytab(TALLOC_CTX *mem_ctx,
     talloc_free(tmp_ctx);
     return ret;
 }
+
+#define SSS_KRB5_FILE   "FILE:"
+#define SSS_KRB5_DIR    "DIR:"
+
+enum sss_krb5_cc_type
+sss_krb5_get_type(const char *full_location)
+{
+    if (!full_location) {
+        return SSS_KRB5_TYPE_UNKNOWN;
+    }
+
+    if (strncmp(full_location, SSS_KRB5_FILE,
+                sizeof(SSS_KRB5_FILE)-1) == 0) {
+        return SSS_KRB5_TYPE_FILE;
+    } else if (strncmp(full_location, SSS_KRB5_DIR,
+               sizeof(SSS_KRB5_DIR)-1) == 0) {
+        return SSS_KRB5_TYPE_DIR;
+    } else if (full_location[0] == '/') {
+        return SSS_KRB5_TYPE_FILE;
+    }
+
+    return SSS_KRB5_TYPE_UNKNOWN;
+}
+
+const char *
+sss_krb5_residual_by_type(const char *full_location,
+                          enum sss_krb5_cc_type type)
+{
+    size_t offset;
+
+    if (full_location == NULL) return NULL;
+
+    switch (type) {
+        case SSS_KRB5_TYPE_FILE:
+            if (full_location[0] == '/') {
+                offset = 0;
+            } else {
+                offset = sizeof(SSS_KRB5_FILE)-1;
+            }
+            break;
+        case SSS_KRB5_TYPE_DIR:
+            offset = sizeof(SSS_KRB5_DIR)-1;
+            break;
+        default:
+            return NULL;
+    }
+
+    return full_location + offset;
+}
+
+const char *
+sss_krb5_cc_file_path(const char *full_location)
+{
+    enum sss_krb5_cc_type cc_type;
+    const char *residual;
+
+    cc_type = sss_krb5_get_type(full_location);
+    residual = sss_krb5_residual_by_type(full_location, cc_type);
+
+    switch(cc_type) {
+        case SSS_KRB5_TYPE_FILE:
+            return residual;
+        case SSS_KRB5_TYPE_DIR:
+            /* DIR::/run/user/tkt_foo */
+            if (residual[0] == ':') return residual+1;
+        case SSS_KRB5_TYPE_UNKNOWN:
+            break;
+    }
+
+    return NULL;
+}
+
+const char *
+sss_krb5_residual_check_type(const char *full_location,
+                             enum sss_krb5_cc_type expected_type)
+{
+    enum sss_krb5_cc_type type;
+
+    type = sss_krb5_get_type(full_location);
+    if (type != expected_type) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Unexpected ccache type\n"));
+        return NULL;
+    }
+
+    return sss_krb5_residual_by_type(full_location, type);
+}
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index 12412585f516cd72726faf2de18ccd60c2c2fd84..bba2a7eda38b2da9fa3d875b327026de7cbe50cf 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -122,6 +122,22 @@ sss_krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal
 void sss_krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opts,
                                                   int canonicalize);
 
+enum sss_krb5_cc_type {
+    SSS_KRB5_TYPE_FILE,
+    SSS_KRB5_TYPE_DIR,
+    SSS_KRB5_TYPE_UNKNOWN
+};
+
+enum sss_krb5_cc_type
+sss_krb5_get_type(const char *full_location);
+const char *
+sss_krb5_residual_by_type(const char *full_location, enum sss_krb5_cc_type type);
+const char *
+sss_krb5_cc_file_path(const char *full_location);
+const char *
+sss_krb5_residual_check_type(const char *full_location,
+                             enum sss_krb5_cc_type expected_type);
+
 /* === Compatibility routines for the Heimdal Kerberos implementation === */
 
 void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
-- 
1.7.10.2

-------------- next part --------------
>From a7f5207c328c737a19f0a8c3116a25db46daa980 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 31 May 2012 19:28:29 +0200
Subject: [PATCH 07/11] Handle trailing slash in the ccname template

With the DIR cache support, it's perfectly legal to specify a ccname
directory that ends with a slash. The create_dir function did not handle
that situation correctly.
---
 src/providers/krb5/krb5_utils.c |   19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index c97d58e7c7cfb80b3fd34eaf18a5371669871272..9fe1420281c39ba122491eeda5cefe8e298fb1e0 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -285,14 +285,17 @@ static errno_t find_ccdir_parent_data(TALLOC_CTX *mem_ctx, const char *dirname,
         DEBUG(1, ("talloc_strdup failed.\n"));
         return ENOMEM;
     }
-    end = strrchr(parent, '/');
-    if (end == NULL || end == parent) {
-        DEBUG(1, ("Cannot find parent directory of [%s], / is not allowed.\n",
-                   dirname));
-        ret = EINVAL;
-        goto done;
-    }
-    *end = '\0';
+
+    do {
+        end = strrchr(parent, '/');
+        if (end == NULL || end == parent) {
+            DEBUG(1, ("Cannot find parent directory of [%s], / is not allowed.\n",
+                    dirname));
+            ret = EINVAL;
+            goto done;
+        }
+        *end = '\0';
+    } while (*(end+1) == '\0');
 
     ret = find_ccdir_parent_data(mem_ctx, parent, parent_stat, missing_parents);
 
-- 
1.7.10.2

-------------- next part --------------
>From 18ba187cac20ceb02370504c2e70728e1da26b5a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 17 May 2012 18:06:34 +0200
Subject: [PATCH 08/11] Add a credential cache back end structure

To be able to add support for new credential cache types easily, this
patch creates a new structure sss_krb5_cc_be that defines common with a
credential cache, such as create, check if used or remove.
---
 Makefile.am                           |    3 +
 src/providers/krb5/krb5_auth.c        |  210 ++++++++++++------------
 src/providers/krb5/krb5_auth.h        |    1 +
 src/providers/krb5/krb5_become_user.c |    2 +-
 src/providers/krb5/krb5_common.c      |   12 +-
 src/providers/krb5/krb5_common.h      |    3 +
 src/providers/krb5/krb5_utils.c       |  286 ++++++++++++++++++++++++++++-----
 src/providers/krb5/krb5_utils.h       |   33 +++-
 src/tests/krb5_child-test.c           |   38 ++---
 src/tests/krb5_utils-tests.c          |   44 ++---
 10 files changed, 430 insertions(+), 202 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index f5366a36c607a1d2bb75152b64e73e93ba6b7ff9..3783100e6d74f4982652d8280aa4d7305da9ad3f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -758,6 +758,7 @@ krb5_utils_tests_SOURCES = \
     src/providers/krb5/krb5_utils.c \
     src/providers/krb5/krb5_common.c \
     src/util/sss_krb5.c \
+    src/util/find_uid.c \
     src/providers/data_provider_fo.c \
     src/providers/data_provider_opts.c \
     src/providers/data_provider_callbacks.c \
@@ -980,6 +981,7 @@ krb5_child_test_SOURCES = \
     src/providers/krb5/krb5_become_user.c \
     src/providers/krb5/krb5_common.c \
     src/util/sss_krb5.c \
+    src/util/find_uid.c \
     src/providers/data_provider_fo.c \
     src/providers/data_provider_opts.c \
     src/providers/data_provider_callbacks.c \
@@ -1157,6 +1159,7 @@ libsss_ldap_la_SOURCES = \
     src/util/find_uid.c \
     src/providers/ldap/ldap_init.c \
     src/providers/krb5/krb5_common.c \
+    src/providers/krb5/krb5_utils.c \
     src/util/user_info_msg.c \
     src/util/sss_ldap.c \
     src/util/sss_krb5.c
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 7c622dbea6869f1acf510104498a304f634c21b5..8124626669ad9262fc8efea66b783c3da34168b7 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -39,107 +39,85 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
 
-static errno_t safe_remove_old_ccache_file(const char *old_ccache_file,
-                                           const char *new_ccache_file)
+static errno_t safe_remove_old_ccache_file(struct sss_krb5_cc_be *cc_be,
+                                           const char *princ,
+                                           const char *old_ccache,
+                                           const char *new_ccache)
 {
     int ret;
-    size_t old_offset = 0;
-    size_t new_offset = 0;
+    enum sss_krb5_cc_type old_type;
+    struct sss_krb5_cc_be *old_cc_ops;
+    TALLOC_CTX *tmp_ctx = NULL;
 
-    if (new_ccache_file == NULL) {
-        DEBUG(1, ("Missing new ccache file, "
-                  "old ccache file is not deleted.\n"));
+    if (old_ccache == NULL) {
+        DEBUG(SSSDBG_FUNC_DATA, ("No old ccache, nothing to do\n"));
+        return EOK;
+    }
+
+    if (new_ccache == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Missing new ccache file, old ccache file is not deleted.\n"));
+        return EINVAL;
+    }
+
+    old_type = sss_krb5_get_type(old_ccache);
+    old_cc_ops = get_cc_be_ops(old_type);
+    if (!old_cc_ops) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get ccache operations\n"));
         return EINVAL;
     }
 
-    if (old_ccache_file != NULL) {
-        if (strncmp(old_ccache_file, "FILE:", 5) == 0) {
-            old_offset = 5;
-        }
-        if (strncmp(new_ccache_file, "FILE:", 5) == 0) {
-            new_offset = 5;
-        }
-        if (strcmp(old_ccache_file + old_offset,
-                   new_ccache_file + new_offset) == 0) {
-            DEBUG(7, ("New and old ccache file are the same, "
-                      "no one will be deleted.\n"));
-            return EOK;
-        }
-        if (old_ccache_file[old_offset] != '/') {
-            DEBUG(1, ("Ccache file name [%s] is not an absolute path.\n",
-                      old_ccache_file + old_offset));
-            return EINVAL;
-        }
-        ret = unlink(old_ccache_file + old_offset);
-        if (ret == -1 && errno != ENOENT) {
-            ret = errno;
-            DEBUG(1, ("unlink [%s] failed [%d][%s].\n", old_ccache_file, ret,
-                                                        strerror(ret)));
-            return ret;
-        }
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
+
+    if (cc_be->type == old_type &&
+        strcmp(old_ccache, new_ccache) == 0) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("New and old ccache file are the same, "
+                                  "no one will be deleted.\n"));
+        ret = EOK;
+        goto done;
+    }
+
+    ret = old_cc_ops->remove(old_ccache);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                ("Cannot remove ccache [%s]\n", old_ccache));
+        ret = EIO;
+        goto done;
     }
 
-    return EOK;
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
 }
 
-static errno_t check_if_ccache_file_is_used(uid_t uid, const char *ccname,
-                                            bool *result)
+static errno_t
+check_old_ccache(const char *old_ccache, struct krb5child_req *kr,
+                 const char *realm, bool *active, bool *valid)
 {
-    int ret;
-    size_t offset = 0;
-    struct stat stat_buf;
-    const char *filename;
-    bool active;
-
-    *result = false;
-
-    if (ccname == NULL || *ccname == '\0') {
-        return EINVAL;
-    }
-
-    if (strncmp(ccname, "FILE:", 5) == 0) {
-        offset = 5;
-    }
-
-    filename = ccname + offset;
-
-    if (filename[0] != '/') {
-        DEBUG(1, ("Only absolute path names are allowed.\n"));
+    struct sss_krb5_cc_be *old_cc_ops;
+    errno_t ret;
+
+    /* ccache file might be of a different type if the user changed
+     * configuration
+     */
+    old_cc_ops = get_cc_be_ops_ccache(old_ccache);
+    if (old_cc_ops == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Cannot get operations on saved ccache %s\n", old_ccache));
         return EINVAL;
     }
 
-    ret = lstat(filename, &stat_buf);
-
-    if (ret == -1 && errno != ENOENT) {
-        DEBUG(1, ("stat failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
-    } else if (ret == EOK) {
-        if (stat_buf.st_uid != uid) {
-            DEBUG(1, ("Cache file [%s] exists, but is owned by [%d] instead of "
-                      "[%d].\n", filename, stat_buf.st_uid, uid));
-            return EINVAL;
-        }
-
-        if (!S_ISREG(stat_buf.st_mode)) {
-            DEBUG(1, ("Cache file [%s] exists, but is not a regular file.\n",
-                      filename));
-            return EINVAL;
-        }
-    }
-
-    ret = check_if_uid_is_active(uid, &active);
+    ret = old_cc_ops->check_existing(old_ccache, kr->uid, realm,
+                                     kr->upn, active, valid);
     if (ret != EOK) {
-        DEBUG(1, ("check_if_uid_is_active failed.\n"));
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Cannot check if saved ccache %s is active and valid\n",
+               old_ccache));
         return ret;
     }
 
-    if (!active) {
-        DEBUG(5, ("User [%d] is not active\n", uid));
-    } else {
-        DEBUG(9, ("User [%d] is still active, reusing ccache file [%s].\n",
-                  uid, filename));
-        *result = true;
-    }
     return EOK;
 }
 
@@ -332,7 +310,6 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
     struct krb5child_req *kr = NULL;
     const char *ccache_file = NULL;
     const char *realm;
-    krb5_error_code kerr;
     struct tevent_req *req;
     struct tevent_req *subreq;
     int ret;
@@ -457,22 +434,20 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
+        /* The type of the ccache might change during the request if we
+         * end up reusing an old ccache */
+        kr->cc_be = krb5_ctx->cc_be;
+
         ccache_file = ldb_msg_find_attr_as_string(res->msgs[0],
                                                   SYSDB_CCACHE_FILE,
                                                   NULL);
         if (ccache_file != NULL) {
-            ret = check_if_ccache_file_is_used(kr->uid, ccache_file,
-                                               &kr->active_ccache_present);
+            ret = check_old_ccache(ccache_file, kr, realm,
+                                   &kr->active_ccache_present,
+                                   &kr->valid_tgt_present);
             if (ret != EOK) {
-                DEBUG(1, ("check_if_ccache_file_is_used failed.\n"));
-                goto done;
-            }
-
-            kerr = check_for_valid_tgt(ccache_file, realm, kr->upn,
-                                       &kr->valid_tgt_present);
-            if (kerr != 0) {
-                DEBUG(1, ("check_for_valid_tgt failed.\n"));
-                ret = kerr;
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("check_if_ccache_file_is_used failed.\n"));
                 goto done;
             }
         } else {
@@ -639,13 +614,27 @@ static void krb5_find_ccache_step(struct tevent_req *req)
                 goto done;
             }
 
-            ret = create_ccache_dir(kr, kr->ccname,
+            ret = kr->cc_be->create(kr->ccname,
                                     kr->krb5_ctx->illegal_path_re,
                                     kr->uid, kr->gid, private_path);
             if (ret != EOK) {
-                DEBUG(1, ("create_ccache_dir failed.\n"));
+                DEBUG(SSSDBG_OP_FAILURE, ("ccache creation failed.\n"));
                 goto done;
             }
+    } else {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Saved ccache %s if of different type than ccache in "
+               "configuration file, reusing the old ccache\n"));
+
+        kr->cc_be = get_cc_be_ops_ccache(kr->old_ccname);
+        if (kr->cc_be == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Cannot get operations on saved ccache %s\n",
+                   kr->old_ccname));
+            ret = EINVAL;
+            goto done;
+        }
+
     }
 
     if (kr->is_offline) {
@@ -725,6 +714,7 @@ static void krb5_child_done(struct tevent_req *subreq)
     uint8_t *buf = NULL;
     ssize_t len = -1;
     struct krb5_child_response *res;
+    const char *store_ccname;
 
     ret = handle_child_recv(subreq, pd, &buf, &len);
     talloc_zfree(subreq);
@@ -777,7 +767,8 @@ static void krb5_child_done(struct tevent_req *subreq)
     if (res->msg_status == PAM_NEW_AUTHTOK_REQD) {
         if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache_present) {
             if (kr->old_ccname != NULL) {
-                ret = safe_remove_old_ccache_file(kr->old_ccname, "dummy");
+                ret = safe_remove_old_ccache_file(kr->cc_be, kr->upn,
+                                                  kr->old_ccname, "dummy");
                 if (ret != EOK) {
                     DEBUG(1, ("Failed to remove old ccache file [%s], "
                               "please remove it manually.\n", kr->old_ccname));
@@ -851,16 +842,25 @@ static void krb5_child_done(struct tevent_req *subreq)
         goto done;
     }
 
-    if (kr->old_ccname != NULL) {
-        ret = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname);
-        if (ret != EOK) {
-            DEBUG(1, ("Failed to remove old ccache file [%s], "
-                      "please remove it manually.\n", kr->old_ccname));
-        }
+    store_ccname = kr->cc_be->ccache_for_princ(kr, kr->ccname,
+                                               kr->upn);
+    if (store_ccname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                ("No ccache for %s in %s?\n", kr->upn, kr->ccname));
+        ret = EIO;
+        goto done;
+    }
+
+    ret = safe_remove_old_ccache_file(kr->cc_be, kr->upn,
+                                      kr->old_ccname, store_ccname);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Failed to remove old ccache file [%s], "
+               "please remove it manually.\n", kr->old_ccname));
     }
 
     ret = krb5_save_ccname(state, state->be_ctx->sysdb,
-                           pd->user, kr->ccname);
+                           pd->user, store_ccname);
     if (ret) {
         DEBUG(1, ("krb5_save_ccname failed.\n"));
         goto done;
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 5cc22dfe8171d1f83a1d6ef63313222f66e302d4..cc079ba9352092a2b2dbcfe46ab4538bd120b567 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -41,6 +41,7 @@ struct krb5child_req {
     struct pam_data *pd;
     struct krb5_ctx *krb5_ctx;
 
+    struct sss_krb5_cc_be *cc_be;
     const char *ccname;
     const char *old_ccname;
     const char *homedir;
diff --git a/src/providers/krb5/krb5_become_user.c b/src/providers/krb5/krb5_become_user.c
index 351f539a32e3e0cad56dd0bdde2ab67d9223ebc5..cc4e6a58a3fe84ffb1d2c0c8773f880e5f912e43 100644
--- a/src/providers/krb5/krb5_become_user.c
+++ b/src/providers/krb5/krb5_become_user.c
@@ -31,7 +31,7 @@ errno_t become_user(uid_t uid, gid_t gid)
 {
     int ret;
 
-    DEBUG(9, ("Trying to become user [%d][%d].\n", uid, gid));
+    DEBUG(SSSDBG_FUNC_DATA, ("Trying to become user [%d][%d].\n", uid, gid));
     ret = setgid(gid);
     if (ret == -1) {
         DEBUG(1, ("setgid failed [%d][%s].\n", errno, strerror(errno)));
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 022745d6153c524cf30dbf787c3343d874cde90a..91816662b8a694fbf31933eae05db2793e3a9dbc 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -31,6 +31,7 @@
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_common.h"
 #include "providers/krb5/krb5_opts.h"
+#include "providers/krb5/krb5_utils.h"
 
 errno_t check_and_export_lifetime(struct dp_option *opts, const int opt_id,
                                   const char *env_name)
@@ -95,6 +96,7 @@ errno_t check_and_export_options(struct dp_option *opts,
     const char *dummy;
     char *use_fast_str;
     char *fast_principal;
+    enum sss_krb5_cc_type cc_be;
 
     realm = dp_opt_get_cstring(opts, KRB5_REALM);
     if (realm == NULL) {
@@ -178,11 +180,15 @@ errno_t check_and_export_options(struct dp_option *opts,
         DEBUG(1, ("Missing credential cache name template.\n"));
         return EINVAL;
     }
-    if (dummy[0] != '/' && strncmp(dummy, "FILE:", 5) != 0) {
-        DEBUG(1, ("Currently only file based credential caches are supported "
-                  "and krb5ccname_template must start with '/' or 'FILE:'\n"));
+
+    cc_be = sss_krb5_get_type(dummy);
+    if (cc_be != SSS_KRB5_TYPE_FILE || dummy[0] != '/') {
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              ("Currently only file based credential caches are supported "
+               "and krb5ccname_template must start with '/' or 'FILE:'\n"));
         return EINVAL;
     }
+    krb5_ctx->cc_be = &file_cc;
 
     return EOK;
 }
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 950505f1208f164f9267aacbf49da136b3cc0073..029ebf6ed07e6ee3f6af892eb7fe2f3741ab6350 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -84,6 +84,7 @@ struct krb5_service {
 struct fo_service;
 struct deferred_auth_ctx;
 struct renew_tgt_ctx;
+struct sss_krb5_cc_be;
 
 struct krb5_ctx {
     /* opts taken from kinit */
@@ -115,6 +116,7 @@ struct krb5_ctx {
     struct krb5_service *kpasswd_service;
     int child_debug_fd;
 
+    struct sss_krb5_cc_be *cc_be;
     pcre *illegal_path_re;
 
     struct deferred_auth_ctx *deferred_auth_ctx;
@@ -171,4 +173,5 @@ errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm);
 
 errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
                             const char *username, const char **_upn);
+
 #endif /* __KRB5_COMMON_H__ */
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 9fe1420281c39ba122491eeda5cefe8e298fb1e0..333d4b5f616d7d89094adf50259bb4667ae325f0 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -26,6 +26,7 @@
 
 #include "providers/krb5/krb5_utils.h"
 #include "providers/krb5/krb5_auth.h"
+#include "src/util/find_uid.h"
 #include "util/util.h"
 
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
@@ -304,38 +305,46 @@ done:
     return ret;
 }
 
-errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
-                          pcre *illegal_re, uid_t uid, gid_t gid,
-                          bool private_path)
+static errno_t
+check_ccache_re(const char *filename, pcre *illegal_re)
+{
+    errno_t ret;
+
+    ret = pcre_exec(illegal_re, NULL, filename, strlen(filename),
+                    0, 0, NULL, 0);
+    if (ret == 0) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Illegal pattern in ccache directory name [%s].\n", filename));
+        return EINVAL;
+    } else if (ret == PCRE_ERROR_NOMATCH) {
+        DEBUG(SSSDBG_TRACE_LIBS,
+              ("Ccache directory name [%s] does not contain "
+               "illegal patterns.\n", filename));
+        return EOK;
+    }
+
+    DEBUG(SSSDBG_CRIT_FAILURE, ("pcre_exec failed [%d].\n", ret));
+    return EFAULT;
+}
+
+static errno_t
+create_ccache_dir(const char *dirname, pcre *illegal_re,
+                  uid_t uid, gid_t gid, bool private_path)
 {
     int ret = EFAULT;
-    char *dirname;
-    char *end;
     struct stat parent_stat;
     struct string_list *missing_parents = NULL;
     struct string_list *li = NULL;
     mode_t old_umask;
     mode_t new_dir_mode;
-    size_t offset = 0;
     TALLOC_CTX *tmp_ctx = NULL;
 
-    tmp_ctx = talloc_new(mem_ctx);
+    tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
         DEBUG(1, ("talloc_new failed.\n"));
         return ENOMEM;
     }
 
-    if (strncmp(filename, "FILE:", 5) == 0) {
-        offset = 5;
-    }
-
-    dirname = talloc_strdup(tmp_ctx, filename + offset);
-    if (dirname == NULL) {
-        DEBUG(1, ("talloc_strndup failed.\n"));
-        ret = ENOMEM;
-        goto done;
-    }
-
     if (*dirname != '/') {
         DEBUG(1, ("Only absolute paths are allowed, not [%s] .\n", dirname));
         ret = EINVAL;
@@ -343,31 +352,12 @@ errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
     }
 
     if (illegal_re != NULL) {
-        ret = pcre_exec(illegal_re, NULL, dirname, strlen(dirname),
-                        0, 0, NULL, 0);
-        if (ret == 0) {
-            DEBUG(1, ("Illegal pattern in ccache directory name [%s].\n",
-                      dirname));
-            ret = EINVAL;
-            goto done;
-        } else if ( ret == PCRE_ERROR_NOMATCH) {
-            DEBUG(9, ("Ccache directory name [%s] does not contain "
-                      "illegal patterns.\n", dirname));
-        } else {
-            DEBUG(1, ("pcre_exec failed [%d].\n", ret));
-            ret = EFAULT;
+        ret = check_ccache_re(dirname, illegal_re);
+        if (ret != EOK) {
             goto done;
         }
     }
 
-    end = strrchr(dirname, '/');
-    if (end == NULL || end == dirname) {
-        DEBUG(1, ("Missing filename in [%s].\n", dirname));
-        ret = EINVAL;
-        goto done;
-    }
-    *end = '\0';
-
     ret = find_ccdir_parent_data(tmp_ctx, dirname, &parent_stat,
                                  &missing_parents);
     if (ret != EOK) {
@@ -524,3 +514,221 @@ done:
 
     return EOK;
 }
+
+/*======== ccache back end utilities ========*/
+struct sss_krb5_cc_be *
+get_cc_be_ops(enum sss_krb5_cc_type type)
+{
+    struct sss_krb5_cc_be *be;
+
+    switch (type) {
+        case SSS_KRB5_TYPE_FILE:
+            be = &file_cc;
+            break;
+        case SSS_KRB5_TYPE_UNKNOWN:
+            be = NULL;
+            break;
+    }
+
+    return be;
+}
+
+struct sss_krb5_cc_be *
+get_cc_be_ops_ccache(const char *ccache)
+{
+    enum sss_krb5_cc_type type;
+
+    type = sss_krb5_get_type(ccache);
+    return get_cc_be_ops(type);
+}
+
+/*======== Operations on the FILE: back end ========*/
+errno_t
+cc_file_create(const char *location, pcre *illegal_re,
+               uid_t uid, gid_t gid, bool private_path)
+{
+    const char *filename;
+    char *dirname;
+    char *end;
+    TALLOC_CTX *tmp_ctx = NULL;
+    errno_t ret;
+
+    filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE);
+    if (filename == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Bad ccache type %s\n", location));
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
+    dirname = talloc_strdup(tmp_ctx, filename);
+    if (dirname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    end = strrchr(dirname, '/');
+    if (end == NULL || end == dirname) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Missing filename in [%s].\n", dirname));
+        ret = EINVAL;
+        goto done;
+    }
+    *end = '\0';
+
+    ret = create_ccache_dir(dirname, illegal_re, uid, gid, private_path);
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t
+cc_residual_is_used(uid_t uid, const char *ccname,
+                    enum sss_krb5_cc_type type, bool *result)
+{
+    int ret;
+    struct stat stat_buf;
+    bool active;
+
+    *result = false;
+
+    if (ccname == NULL || *ccname == '\0') {
+        return EINVAL;
+    }
+
+    ret = lstat(ccname, &stat_buf);
+
+    if (ret == -1 && errno != ENOENT) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("stat failed [%d][%s].\n", ret, strerror(ret)));
+        return ret;
+    } else if (ret == EOK) {
+        if (stat_buf.st_uid != uid) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Cache file [%s] exists, but is owned by [%d] instead of "
+                   "[%d].\n", ccname, stat_buf.st_uid, uid));
+            return EINVAL;
+        }
+
+        switch (type) {
+            case SSS_KRB5_TYPE_FILE:
+                ret = S_ISREG(stat_buf.st_mode);
+                break;
+            default:
+                DEBUG(SSSDBG_CRIT_FAILURE, ("Unsupported ccache type\n"));
+                return EINVAL;
+        }
+
+        if (ret == 0) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Cache file [%s] exists, but is not the expected type\n",
+                   ccname));
+            return EINVAL;
+        }
+    }
+
+    ret = check_if_uid_is_active(uid, &active);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("check_if_uid_is_active failed.\n"));
+        return ret;
+    }
+
+    if (!active) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("User [%d] is not active\n", uid));
+    } else {
+        DEBUG(SSSDBG_TRACE_LIBS,
+              ("User [%d] is still active, reusing ccache [%s].\n",
+              uid, ccname));
+        *result = true;
+    }
+    return EOK;
+}
+
+errno_t
+cc_file_check_existing(const char *location, uid_t uid,
+                       const char *realm, const char *princ,
+                       bool *_active, bool *_valid)
+{
+    errno_t ret;
+    bool active;
+    bool valid;
+    const char *filename;
+
+    filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE);
+    if (!filename) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type FILE:\n"));
+        return EINVAL;
+    }
+
+    if (filename[0] != '/') {
+        DEBUG(SSSDBG_OP_FAILURE, ("Only absolute path names are allowed.\n"));
+        return EINVAL;
+    }
+
+    ret = cc_residual_is_used(uid, filename, SSS_KRB5_TYPE_FILE, &active);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not check if ccache is active\n"));
+        return ret;
+    }
+
+    ret = check_for_valid_tgt(location, realm, princ, &valid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not check if ccache contains a valid principal\n"));
+        return ret;
+    }
+
+    *_active = active;
+    *_valid = valid;
+    return EOK;
+}
+
+const char *
+cc_file_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location,
+                        const char *princ)
+{
+    return talloc_strdup(mem_ctx, location);
+}
+
+errno_t
+cc_file_remove(const char *location)
+{
+    errno_t ret;
+    const char *filename;
+
+    filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE);
+    if (!filename) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type FILE:\n"));
+        return EINVAL;
+    }
+
+    if (filename[0] != '/') {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Ccache file name [%s] is not an absolute path.\n", filename));
+        return EINVAL;
+    }
+
+    errno = 0;
+    ret = unlink(filename);
+    if (ret == -1 && errno != ENOENT) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("unlink [%s] failed [%d][%s].\n", filename, ret,
+                                                 strerror(ret)));
+        return ret;
+    }
+    return EOK;
+}
+
+struct sss_krb5_cc_be file_cc = {
+    .type               = SSS_KRB5_TYPE_FILE,
+    .create             = cc_file_create,
+    .check_existing     = cc_file_check_existing,
+    .ccache_for_princ   = cc_file_cache_for_princ,
+    .remove             = cc_file_remove,
+};
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 7cc57d42f460380a97d4c7d149cbedc8e516e977..87bba2bae403b1271f45a7cea685a9bcf02207f8 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -31,16 +31,41 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/data_provider.h"
 
+/* Operations on a credential cache */
+typedef errno_t (*cc_be_create_fn)(const char *location, pcre *illegal_re,
+                                   uid_t uid, gid_t gid, bool private_path);
+typedef errno_t (*cc_be_check_existing)(const char *location, uid_t uid,
+                                        const char *realm, const char *princ,
+                                        bool *active, bool *valid);
+typedef const char * (*cc_be_ccache_for_princ)(TALLOC_CTX *mem_ctx,
+                                               const char *location,
+                                               const char *princ);
+typedef errno_t (*cc_be_remove)(const char *location);
+
+/* A ccache back end */
+struct sss_krb5_cc_be {
+    enum sss_krb5_cc_type type;
+
+    cc_be_create_fn create;
+    cc_be_check_existing check_existing;
+    cc_be_ccache_for_princ ccache_for_princ;
+    cc_be_remove remove;
+};
+
+struct sss_krb5_cc_be file_cc;
+
+errno_t cc_file_create(const char *filename, pcre *illegal_re,
+                       uid_t uid, gid_t gid, bool private_path);
+
+struct sss_krb5_cc_be *get_cc_be_ops(enum sss_krb5_cc_type type);
+struct sss_krb5_cc_be *get_cc_be_ops_ccache(const char *ccache);
+
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                              const char *template, bool file_mode,
                              bool case_sensitive, bool *private_path);
 
 errno_t become_user(uid_t uid, gid_t gid);
 
-errno_t create_ccache_dir(TALLOC_CTX *mem_ctx, const char *filename,
-                          pcre *illegal_re, uid_t uid, gid_t gid,
-                          bool private_path);
-
 errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
                              struct tgt_times *tgtt);
 #endif /* __KRB5_UTILS_H__ */
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index a1acfbb4509d47fd9f5b1bd5a65e7740849b9bbb..64a0289d58470b66be18d17ba04740a716e91a37 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -39,6 +39,7 @@
 #include "providers/krb5/krb5_utils.h"
 
 extern struct dp_option default_krb5_opts[];
+extern struct sss_krb5_cc_be file_cc;
 
 static krb5_context krb5_error_ctx;
 #define KRB5_DEBUG(level, krb5_error) do { \
@@ -118,6 +119,8 @@ create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm)
     krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx);
     if (!krb5_ctx) return NULL;
 
+    krb5_ctx->cc_be = &file_cc;
+
     krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
                                         &errval, &errstr, &errpos, NULL);
     if (krb5_ctx->illegal_path_re == NULL) {
@@ -250,14 +253,16 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
         DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
               kr->ccname, kr->uid, kr->gid));
 
-        ret = create_ccache_dir(kr, kr->ccname,
-                                kr->krb5_ctx->illegal_path_re,
-                                kr->uid, kr->gid, private);
+        ret = kr->krb5_ctx->cc_be->create(kr->ccname,
+                                          kr->krb5_ctx->illegal_path_re,
+                                          kr->uid, kr->gid, private);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
-            goto fail;
         }
+    } else {
+        kr->ccname = talloc_strdup(kr, ccname);
     }
+    if (!kr->ccname) goto fail;
 
     return kr;
 
@@ -375,29 +380,6 @@ done:
     return ret;
 }
 
-static void
-remove_ccache(const char *cc)
-{
-    size_t offset = 0;
-    errno_t ret;
-
-    if (strncmp(cc, "FILE:", 5) == 0) {
-        offset = 5;
-    }
-    if (cc[offset] != '/') {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              ("ccname [%s] does not contain absolute path?\n", cc));
-    }
-
-    errno = 0;
-    ret = unlink(cc+offset);
-    if (ret == -1) {
-        ret = errno;
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              ("unlink [%s] failed [%d]: %s\n", cc, ret, strerror(ret)));
-    }
-}
-
 int
 main(int argc, const char *argv[])
 {
@@ -542,7 +524,7 @@ main(int argc, const char *argv[])
     ret = 0;
 done:
     if (rm_ccache && ctx->res && ctx->res->ccname) {
-        remove_ccache(ctx->res->ccname);
+        ctx->kr->krb5_ctx->cc_be->remove(ctx->res->ccname);
     }
     free(password);
     talloc_free(ctx);
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index aacc384de2e0113fdaed861cf007b10f41b7aa6c..4a572488f0dfc3e8bae59813d646ee0791b1f428 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -117,14 +117,14 @@ START_TEST(test_pub_ccache_dir)
 
     ret = chmod(testpath, 0754);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, false);
-    fail_unless(ret == EINVAL, "create_ccache_dir does not return EINVAL "
+    ret = cc_file_create(filename, NULL, 12345, 12345, false);
+    fail_unless(ret == EINVAL, "cc_file_create does not return EINVAL "
                                "while x-bit is missing.");
 
     ret = chmod(testpath, 0755);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, false);
-    fail_unless(ret == EOK, "create_ccache_dir failed.");
+    ret = cc_file_create(filename, NULL, 12345, 12345, false);
+    fail_unless(ret == EOK, "cc_file_create failed.");
 
     check_dir(subdirname, 0, 0, 01777);
     RMDIR(subdirname);
@@ -158,7 +158,7 @@ START_TEST(test_pub_ccache_dir_in_user_dir)
     filename = talloc_asprintf(tmp_ctx, "%s/ccfile", subdirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
 
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, false);
+    ret = cc_file_create(filename, NULL, 12345, 12345, false);
     fail_unless(ret == EINVAL, "Creating public ccache dir in user dir "
                                "does not failed with EINVAL.");
 
@@ -193,14 +193,14 @@ START_TEST(test_priv_ccache_dir)
 
     ret = chmod(testpath, 0754);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
-    fail_unless(ret == EINVAL, "create_ccache_dir does not return EINVAL "
+    ret = cc_file_create(filename, NULL, uid, gid, true);
+    fail_unless(ret == EINVAL, "cc_file_create does not return EINVAL "
                                "while x-bit is missing.");
 
     ret = chmod(testpath, 0755);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
-    fail_unless(ret == EOK, "create_ccache_dir failed.");
+    ret = cc_file_create(filename, NULL, uid, gid, true);
+    fail_unless(ret == EOK, "cc_file_create failed.");
 
     check_dir(subdir, uid, gid, 0700);
     RMDIR(subdir);
@@ -248,14 +248,14 @@ START_TEST(test_private_ccache_dir_in_user_dir)
 
     ret = chmod(user_dir, 0600);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
-    fail_unless(ret == EINVAL, "create_ccache_dir does not return EINVAL "
+    ret = cc_file_create(filename, NULL, uid, gid, true);
+    fail_unless(ret == EINVAL, "cc_file_create does not return EINVAL "
                                "while x-bit is missing.");
 
     ret = chmod(user_dir, 0700);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, uid, gid, true);
-    fail_unless(ret == EOK, "create_ccache_dir failed.");
+    ret = cc_file_create(filename, NULL, uid, gid, true);
+    fail_unless(ret == EOK, "cc_file_create failed.");
 
     check_dir(dn3, uid, gid, 0700);
     RMDIR(dn3);
@@ -292,7 +292,7 @@ START_TEST(test_private_ccache_dir_in_wrong_user_dir)
     filename = talloc_asprintf(tmp_ctx, "%s/ccfile", subdirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
 
-    ret = create_ccache_dir(tmp_ctx, filename, NULL, 12345, 12345, true);
+    ret = cc_file_create(filename, NULL, 12345, 12345, true);
     fail_unless(ret == EINVAL, "Creating private ccache dir in wrong user "
                                "dir does not failed with EINVAL.");
 
@@ -329,28 +329,28 @@ START_TEST(test_illegal_patterns)
 
     filename = talloc_asprintf(tmp_ctx, "abc/./ccfile");
     fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed relative path [%s].",
+    ret = cc_file_create(filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "cc_file_create allowed relative path [%s].",
                                filename);
 
     filename = talloc_asprintf(tmp_ctx, "%s/abc/./ccfile", dirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+    ret = cc_file_create(filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "cc_file_create allowed "
                                "illegal pattern '/./' in filename [%s].",
                                filename);
 
     filename = talloc_asprintf(tmp_ctx, "%s/abc/../ccfile", dirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+    ret = cc_file_create(filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "cc_file_create allowed "
                                "illegal pattern '/../' in filename [%s].",
                                filename);
 
     filename = talloc_asprintf(tmp_ctx, "%s/abc//ccfile", dirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(tmp_ctx, filename, illegal_re, uid, gid, true);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
+    ret = cc_file_create(filename, illegal_re, uid, gid, true);
+    fail_unless(ret == EINVAL, "cc_file_create allowed "
                                "illegal pattern '//' in filename [%s].",
                                filename);
 
-- 
1.7.10.2

-------------- next part --------------
>From e085431a60a1ef31898c2c9277905d21f70c537a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 5 Jun 2012 23:41:12 +0200
Subject: [PATCH 09/11] Add support for storing credential caches in the DIR:
 back end

https://fedorahosted.org/sssd/ticket/974
---
 src/man/sssd-krb5.5.xml          |   10 +-
 src/providers/krb5/krb5_child.c  |  164 +++++++++++++++++++++-----
 src/providers/krb5/krb5_common.c |   34 +++++-
 src/providers/krb5/krb5_utils.c  |  239 +++++++++++++++++++++++++++++++++++++-
 src/providers/krb5/krb5_utils.h  |    4 +
 src/tests/krb5_child-test.c      |   46 ++++++--
 src/tests/krb5_utils-tests.c     |   46 ++++++++
 src/util/sss_krb5.c              |   21 +---
 src/util/sss_krb5.h              |    3 +-
 9 files changed, 495 insertions(+), 72 deletions(-)

diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 247337a90a80b078d0395b27a02e5ea2c72a5ce8..1baee36e9b0ade76de2990fd4f06bc655b5ce06c 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -157,10 +157,12 @@
                     <term>krb5_ccname_template (string)</term>
                     <listitem>
                         <para>
-                            Location of the user's credential cache. Currently
-                            only file based credential caches are supported. In
-                            the template the following sequences are
-                            substituted:
+                            Location of the user's credential cache. Two credential
+                            cache types are currently supported - <quote>FILE</quote>
+                            and <quote>DIR</quote>. The cache can either be specified
+                            as <replaceable>TYPE:RESIDUAL</replaceable>, or an absolute
+                            path, which implies the <quote>FILE</quote> type. In the
+                            template the following sequences are substituted:
                             <variablelist>
                                 <varlistentry>
                                     <term>%u</term>
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index b6c456efb5f1776b40bd9940dddbffe503a009b6..93844fab9925ef67181105cb73c452f5796f0272 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -221,6 +221,51 @@ done:
     return kerr;
 }
 
+static krb5_error_code
+store_creds_in_ccache(krb5_context ctx, krb5_principal princ,
+                      krb5_ccache cc, krb5_creds *creds)
+{
+    krb5_error_code kerr;
+    krb5_creds *l_cred;
+
+    kerr = krb5_cc_initialize(ctx, cc, princ);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        goto done;
+    }
+
+    if (creds == NULL) {
+        kerr = create_empty_cred(ctx, princ, &l_cred);
+        if (kerr != 0) {
+            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            goto done;
+        }
+    } else {
+        l_cred = creds;
+    }
+
+    kerr = krb5_cc_store_cred(ctx, cc, l_cred);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        goto done;
+    }
+
+    kerr = krb5_cc_switch(ctx, cc);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        goto done;
+    }
+
+    kerr = krb5_cc_close(ctx, cc);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        goto done;
+    }
+
+done:
+    return kerr;
+}
+
 static krb5_error_code create_ccache_file(krb5_context ctx,
                                           krb5_principal princ,
                                           char *ccname, krb5_creds *creds)
@@ -232,7 +277,6 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
     size_t ccname_len;
     char *dummy;
     char *tmp_ccname;
-    krb5_creds *l_cred;
     TALLOC_CTX *tmp_ctx = NULL;
     mode_t old_umask;
 
@@ -285,38 +329,16 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
         goto done;
     }
 
-    kerr = krb5_cc_initialize(ctx, tmp_cc, princ);
-    if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
-        goto done;
-    }
+    kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds);
     if (fd != -1) {
         close(fd);
         fd = -1;
     }
-
-    if (creds == NULL) {
-        kerr = create_empty_cred(ctx, princ, &l_cred);
-        if (kerr != 0) {
-            KRB5_DEBUG(1, kerr);
-            goto done;
-        }
-    } else {
-        l_cred = creds;
-    }
-
-    kerr = krb5_cc_store_cred(ctx, tmp_cc, l_cred);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
     }
 
-    kerr = krb5_cc_close(ctx, tmp_cc);
-    if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
-        goto done;
-    }
-    tmp_cc = NULL;
 
     ccname_len = strlen(cc_file_name);
     if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len-6), "XXXXXX")==0 ) {
@@ -337,18 +359,98 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
     DEBUG(SSSDBG_TRACE_LIBS, ("Created ccache file: [%s]\n", ccname));
 
 done:
+    if (kerr != 0 && tmp_cc != NULL) {
+        krb5_cc_destroy(ctx, tmp_cc);
+    }
+
     if (fd != -1) {
         close(fd);
     }
-    if (kerr != 0 && tmp_cc != NULL) {
-        krb5_cc_destroy(ctx, tmp_cc);
-    }
 
     talloc_free(tmp_ctx);
+    return kerr;
+}
+
+static krb5_error_code
+create_ccache_in_dir(krb5_context ctx,
+                     krb5_principal princ,
+                     char *ccname, krb5_creds *creds)
+{
+    krb5_error_code kerr;
+    krb5_ccache tmp_cc = NULL;
+    const char *dirname;
+
+    DEBUG(SSSDBG_FUNC_DATA, ("Creating ccache at [%s]\n", ccname));
+
+    dirname = sss_krb5_residual_check_type(ccname, SSS_KRB5_TYPE_DIR);
+    if (dirname == NULL) {
+        return EIO;
+    }
+
+    if (dirname[0] == ':') {
+        /* Cache name in the form of DIR::filepath represents a single
+         * ccache in a collection that we are trying to reuse.
+         * See src/lib/krb5/ccache/cc_dir.c in the MIT Kerberos tree.
+         */
+        kerr = krb5_cc_resolve(ctx, ccname, &tmp_cc);
+        if (kerr != 0) {
+            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            goto done;
+        }
+    } else if (dirname[0] == '/') {
+        /* An absolute path denotes that krb5_child should create a new
+         * ccache
+         */
+        kerr = krb5_cc_set_default_name(ctx, ccname);
+        if (kerr != 0) {
+            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            goto done;
+        }
 
+        kerr = krb5_cc_new_unique(ctx, "DIR", NULL, &tmp_cc);
+        if (kerr != 0) {
+            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            goto done;
+        }
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Wrong residual format for DIR in ccache %s\n", ccname));
+        return EIO;
+    }
+
+    kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        goto done;
+    }
+
+done:
+    if (kerr != 0 && tmp_cc != NULL) {
+        krb5_cc_destroy(ctx, tmp_cc);
+    }
     return kerr;
 }
 
+static krb5_error_code
+create_ccache(krb5_context ctx, krb5_principal princ,
+              char *ccname, krb5_creds *creds)
+{
+    enum sss_krb5_cc_type cctype;
+
+    cctype = sss_krb5_get_type(ccname);
+    switch (cctype) {
+        case SSS_KRB5_TYPE_FILE:
+            return create_ccache_file(ctx, princ, ccname, creds);
+        case SSS_KRB5_TYPE_DIR:
+            return create_ccache_in_dir(ctx, princ, ccname, creds);
+        default:
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown cache type\n"));
+            return EINVAL;
+    }
+
+    return EINVAL;  /* Should never get here */
+}
+
 static errno_t pack_response_packet(struct response *resp, int status,
                                     struct pam_data *pd)
 {
@@ -728,9 +830,9 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
     }
 
     /* Use the updated principal in the creds in case canonicalized */
-    kerr = create_ccache_file(kr->ctx,
-                              kr->creds ? kr->creds->client : kr->princ,
-                              kr->ccname, kr->creds);
+    kerr = create_ccache(kr->ctx,
+                         kr->creds ? kr->creds->client : kr->princ,
+                         kr->ccname, kr->creds);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
@@ -1141,7 +1243,7 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
 
     DEBUG(SSSDBG_TRACE_LIBS, ("Creating empty ccache\n"));
 
-    ret = create_ccache_file(kr->ctx, kr->princ, kr->ccname, NULL);
+    ret = create_ccache(kr->ctx, kr->princ, kr->ccname, NULL);
     if (ret != 0) {
         KRB5_DEBUG(1, ret);
         pam_status = PAM_SYSTEM_ERR;
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 91816662b8a694fbf31933eae05db2793e3a9dbc..f4033d29597661d1a9b4ff66e0ee02b4bf649f21 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -182,13 +182,37 @@ errno_t check_and_export_options(struct dp_option *opts,
     }
 
     cc_be = sss_krb5_get_type(dummy);
-    if (cc_be != SSS_KRB5_TYPE_FILE || dummy[0] != '/') {
-        DEBUG(SSSDBG_CONF_SETTINGS,
-              ("Currently only file based credential caches are supported "
-               "and krb5ccname_template must start with '/' or 'FILE:'\n"));
+    switch (cc_be) {
+    case SSS_KRB5_TYPE_FILE:
+        DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type FILE\n"));
+        krb5_ctx->cc_be = &file_cc;
+        if (dummy[0] != '/') {
+            /* FILE:/path/to/cc */
+            break;
+        }
+
+        DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was "
+              "missing an explicit type, but is an absolute "
+              "path specifier. Assuming FILE:\n"));
+
+        dummy = talloc_asprintf(opts, "FILE:%s", dummy);
+        if (!dummy) return ENOMEM;
+
+        ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, dummy);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("dp_opt_set_string failed.\n"));
+            return ret;
+        }
+        break;
+    case SSS_KRB5_TYPE_DIR:
+        DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type DIR\n"));
+        krb5_ctx->cc_be = &dir_cc;
+        break;
+    default:
+        DEBUG(SSSDBG_OP_FAILURE, ("Unkown ccname database\n"));
         return EINVAL;
+        break;
     }
-    krb5_ctx->cc_be = &file_cc;
 
     return EOK;
 }
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 333d4b5f616d7d89094adf50259bb4667ae325f0..e32703d8ae37d07241397ae2ad22c5641f0d0017 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -23,6 +23,7 @@
 */
 #include <string.h>
 #include <stdlib.h>
+#include <libgen.h>
 
 #include "providers/krb5/krb5_utils.h"
 #include "providers/krb5/krb5_auth.h"
@@ -525,6 +526,9 @@ get_cc_be_ops(enum sss_krb5_cc_type type)
         case SSS_KRB5_TYPE_FILE:
             be = &file_cc;
             break;
+        case SSS_KRB5_TYPE_DIR:
+            be = &dir_cc;
+            break;
         case SSS_KRB5_TYPE_UNKNOWN:
             be = NULL;
             break;
@@ -616,6 +620,9 @@ cc_residual_is_used(uid_t uid, const char *ccname,
         }
 
         switch (type) {
+            case SSS_KRB5_TYPE_DIR:
+                ret = S_ISDIR(stat_buf.st_mode);
+                break;
             case SSS_KRB5_TYPE_FILE:
                 ret = S_ISREG(stat_buf.st_mode);
                 break;
@@ -658,6 +665,9 @@ cc_file_check_existing(const char *location, uid_t uid,
     bool active;
     bool valid;
     const char *filename;
+    krb5_ccache ccache = NULL;
+    krb5_context context = NULL;
+    krb5_error_code kerr;
 
     filename = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_FILE);
     if (!filename) {
@@ -676,11 +686,26 @@ cc_file_check_existing(const char *location, uid_t uid,
         return ret;
     }
 
-    ret = check_for_valid_tgt(location, realm, princ, &valid);
-    if (ret != EOK) {
+    kerr = krb5_init_context(&context);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to init kerberos context\n"));
+        return EIO;
+    }
+
+    kerr = krb5_cc_resolve(context, location, &ccache);
+    if (kerr != 0) {
+        krb5_free_context(context);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
+        return EIO;
+    }
+
+    kerr = check_for_valid_tgt(context, ccache, realm, princ, &valid);
+    krb5_free_context(context);
+    krb5_cc_close(context, ccache);
+    if (kerr != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               ("Could not check if ccache contains a valid principal\n"));
-        return ret;
+        return EIO;
     }
 
     *_active = active;
@@ -732,3 +757,211 @@ struct sss_krb5_cc_be file_cc = {
     .ccache_for_princ   = cc_file_cache_for_princ,
     .remove             = cc_file_remove,
 };
+
+/*======== Operations on the DIR: back end ========*/
+errno_t
+cc_dir_create(const char *location, pcre *illegal_re,
+              uid_t uid, gid_t gid, bool private_path)
+{
+    const char *dirname;
+
+    dirname = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_DIR);
+    if (dirname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Bad residual type\n"));
+        return EINVAL;
+    }
+
+    return create_ccache_dir(dirname, illegal_re, uid, gid, private_path);
+}
+
+static krb5_error_code
+get_ccache_for_princ(krb5_context context, const char *location,
+                     const char *princ, krb5_ccache *_ccache)
+{
+    krb5_error_code krberr;
+    krb5_principal client_principal = NULL;
+
+    krberr = krb5_cc_set_default_name(context, location);
+    if (krberr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_cc_resolve failed.\n"));
+        return krberr;
+    }
+
+    krberr = krb5_parse_name(context, princ, &client_principal);
+    if (krberr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_parse_name failed.\n"));
+        return krberr;
+    }
+
+    krberr = krb5_cc_cache_match(context, client_principal, _ccache);
+    krb5_free_principal(context, client_principal);
+    return krberr;
+}
+
+errno_t
+cc_dir_check_existing(const char *location, uid_t uid,
+                      const char *realm, const char *princ,
+                      bool *_active, bool *_valid)
+{
+    bool active = false;
+    bool valid = false;
+    krb5_ccache ccache = NULL;
+    krb5_context context = NULL;
+    krb5_error_code krberr;
+    enum sss_krb5_cc_type type;
+    const char *filename;
+    const char *dir;
+    char *tmp;
+    errno_t ret;
+
+    type = sss_krb5_get_type(location);
+    if (type != SSS_KRB5_TYPE_DIR) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type DIR:\n", location));
+        return EINVAL;
+    }
+
+    filename = sss_krb5_cc_file_path(location);
+    if (!filename) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Existing ccname does not contain path into the collection"));
+        return EINVAL;
+    }
+
+    if (filename[0] != '/') {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Only absolute path names are allowed.\n"));
+        return EINVAL;
+    }
+
+    tmp = talloc_strdup(NULL, filename);
+    if (!tmp) return ENOMEM;
+
+    dir = dirname(tmp);
+    if (!dir) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Cannot base get directory of %s\n", location));
+        return EINVAL;
+    }
+
+    ret = cc_residual_is_used(uid, dir, SSS_KRB5_TYPE_DIR, &active);
+    talloc_free(tmp);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not check if ccache is active\n"));
+        return ret;
+    }
+
+    krberr = krb5_init_context(&context);
+    if (krberr) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to init kerberos context\n"));
+        return EIO;
+    }
+
+    krberr = krb5_cc_resolve(context, location, &ccache);
+    if (krberr == KRB5_FCC_NOFILE || ccache == NULL) {
+        /* KRB5_FCC_NOFILE would be returned if the directory components
+         * of the DIR cache do not exist, which is the case in /run
+         * after a reboot
+         */
+        DEBUG(SSSDBG_TRACE_FUNC,
+              ("ccache %s is missing or empty\n", location));
+        valid = false;
+        ret = EOK;
+        goto done;
+    } else if (krberr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
+        ret = EIO;
+        goto done;
+    }
+
+    krberr = check_for_valid_tgt(context, ccache, realm, princ, &valid);
+    if (krberr != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not check if ccache contains a valid principal\n"));
+        ret = EIO;
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    if (ccache) krb5_cc_close(context, ccache);
+    krb5_free_context(context);
+    *_active = active;
+    *_valid = valid;
+    return ret;
+}
+
+const char *
+cc_dir_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location,
+                       const char *princ)
+{
+    krb5_context context = NULL;
+    krb5_error_code krberr;
+    krb5_ccache ccache = NULL;
+    char *name;
+    const char *ccname;
+
+    ccname = sss_krb5_residual_check_type(location, SSS_KRB5_TYPE_DIR);
+    if (!ccname) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get ccname file from %s\n",
+              location));
+        return NULL;
+    }
+
+    /* ccname already points to a subsidiary cache */
+    if (ccname[0] == ':' && ccname[1] && ccname[1] == '/') {
+        return talloc_strdup(mem_ctx, location);
+    }
+
+    krberr = krb5_init_context(&context);
+    if (krberr) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to init kerberos context\n"));
+        return NULL;
+    }
+
+    krberr = get_ccache_for_princ(context, location, princ, &ccache);
+    if (krberr) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("No principal for %s in %s\n",
+              princ, location));
+        krb5_free_context(context);
+        return NULL;
+    }
+
+    krberr = krb5_cc_get_full_name(context, ccache, &name);
+    if (ccache) krb5_cc_close(context, ccache);
+    krb5_free_context(context);
+    if (krberr) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Could not get full name of ccache\n"));
+        return NULL;
+    }
+
+    return talloc_strdup(mem_ctx, name);
+}
+
+errno_t
+cc_dir_remove(const char *location)
+{
+    const char *subsidiary;
+
+    if (sss_krb5_get_type(location) != SSS_KRB5_TYPE_DIR) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("%s is not of type DIR\n", location));
+        return EINVAL;
+    }
+
+    subsidiary = sss_krb5_cc_file_path(location);
+    if (!subsidiary) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot get subsidiary cache from %s\n",
+              location));
+        return EINVAL;
+    }
+
+    return cc_file_remove(subsidiary);
+}
+
+struct sss_krb5_cc_be dir_cc = {
+    .type               = SSS_KRB5_TYPE_DIR,
+    .create             = cc_dir_create,
+    .check_existing     = cc_dir_check_existing,
+    .ccache_for_princ   = cc_dir_cache_for_princ,
+    .remove             = cc_dir_remove
+};
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 87bba2bae403b1271f45a7cea685a9bcf02207f8..684c520151831eb7fbe619a1b14be38a6092e56a 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -53,10 +53,14 @@ struct sss_krb5_cc_be {
 };
 
 struct sss_krb5_cc_be file_cc;
+struct sss_krb5_cc_be dir_cc;
 
 errno_t cc_file_create(const char *filename, pcre *illegal_re,
                        uid_t uid, gid_t gid, bool private_path);
 
+errno_t cc_dir_create(const char *location, pcre *illegal_re,
+                      uid_t uid, gid_t gid, bool private_path);
+
 struct sss_krb5_cc_be *get_cc_be_ops(enum sss_krb5_cc_type type);
 struct sss_krb5_cc_be *get_cc_be_ops_ccache(const char *ccache);
 
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index 64a0289d58470b66be18d17ba04740a716e91a37..59172384a052152fc302161ee293fb4ad3fe4927 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -40,6 +40,7 @@
 
 extern struct dp_option default_krb5_opts[];
 extern struct sss_krb5_cc_be file_cc;
+extern struct sss_krb5_cc_be dir_cc;
 
 static krb5_context krb5_error_ctx;
 #define KRB5_DEBUG(level, krb5_error) do { \
@@ -119,8 +120,6 @@ create_dummy_krb5_ctx(TALLOC_CTX *mem_ctx, const char *realm)
     krb5_ctx = talloc_zero(mem_ctx, struct krb5_ctx);
     if (!krb5_ctx) return NULL;
 
-    krb5_ctx->cc_be = &file_cc;
-
     krb5_ctx->illegal_path_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
                                         &errval, &errstr, &errpos, NULL);
     if (krb5_ctx->illegal_path_re == NULL) {
@@ -202,10 +201,12 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
                  const char *ccname, const char *ccname_template,
                  int timeout)
 {
+    enum sss_krb5_cc_type cc_be;
     struct krb5child_req *kr;
     struct passwd *pwd;
     bool private;
     errno_t ret;
+    const char *tmpl;
 
     /* The top level child request */
     kr = talloc_zero(mem_ctx, struct krb5child_req);
@@ -237,6 +238,9 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
         ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL,
                                 ccname_template);
         if (ret != EOK) goto fail;
+        tmpl = ccname_template;
+    } else {
+        tmpl = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL);
     }
 
     if (timeout) {
@@ -252,18 +256,42 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
 
         DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
               kr->ccname, kr->uid, kr->gid));
-
-        ret = kr->krb5_ctx->cc_be->create(kr->ccname,
-                                          kr->krb5_ctx->illegal_path_re,
-                                          kr->uid, kr->gid, private);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
-        }
     } else {
         kr->ccname = talloc_strdup(kr, ccname);
     }
     if (!kr->ccname) goto fail;
 
+    cc_be = sss_krb5_get_type(kr->ccname);
+    switch (cc_be) {
+    case SSS_KRB5_TYPE_FILE:
+        kr->krb5_ctx->cc_be = &file_cc;
+        break;
+    case SSS_KRB5_TYPE_DIR:
+        kr->krb5_ctx->cc_be = &dir_cc;
+        break;
+    default:
+        if (tmpl[0] != '/') {
+            DEBUG(SSSDBG_OP_FAILURE, ("Unkown ccname database\n"));
+            ret = EINVAL;
+            goto fail;
+        }
+        DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was "
+              "missing an explicit type, but looks like an absolute "
+              "path specifier. Assuming FILE:\n"));
+        kr->krb5_ctx->cc_be = &file_cc;
+        break;
+    }
+    DEBUG(SSSDBG_FUNC_DATA, ("ccname [%s] uid [%llu] gid [%llu]\n",
+            kr->ccname, kr->uid, kr->gid));
+
+    ret = kr->krb5_ctx->cc_be->create(kr->ccname,
+                                      kr->krb5_ctx->illegal_path_re,
+                                      kr->uid, kr->gid, private);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("create_ccache_dir failed.\n"));
+        goto fail;
+    }
+
     return kr;
 
 fail:
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 4a572488f0dfc3e8bae59813d646ee0791b1f428..4a4b75df21e0b2a1b6ab3528d045a5cfbc792f18 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -357,6 +357,51 @@ START_TEST(test_illegal_patterns)
 }
 END_TEST
 
+START_TEST(test_cc_dir_create)
+{
+    char *residual;
+    char *dirname;
+    char *cwd;
+    uid_t uid = getuid();
+    gid_t gid = getgid();
+    pcre *illegal_re;
+    errno_t ret;
+    const char *errstr;
+    int errval;
+    int errpos;
+
+    illegal_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
+                               &errval, &errstr, &errpos, NULL);
+    fail_unless(illegal_re != NULL, "Invalid Regular Expression pattern at "
+                                    " position %d. (Error: %d [%s])\n",
+                                    errpos, errval, errstr);
+
+    cwd = getcwd(NULL, 0);
+    fail_unless(cwd != NULL, "getcwd failed.");
+
+    residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s/priv_ccdir",
+                               cwd, TESTS_PATH);
+    fail_unless(residual != NULL, "talloc_asprintf failed.");
+    ret = cc_dir_create(residual, illegal_re, uid, gid, true);
+    fail_unless(ret == EOK, "cc_dir_create failed\n");
+    dirname = strchr(residual, ':') + 1;
+    ret = rmdir(dirname);
+    fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname);
+    talloc_free(residual);
+
+    dirname = talloc_asprintf(tmp_ctx, "DIR:%s/%s/priv_ccdir2/",
+                              cwd, TESTS_PATH);
+    fail_unless(residual != NULL, "talloc_asprintf failed.");
+    ret = cc_dir_create(residual, illegal_re, uid, gid, true);
+    fail_unless(ret == EOK, "cc_dir_create failed\n");
+    dirname = strchr(residual, ':') + 1;
+    ret = rmdir(dirname);
+    fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname);
+    talloc_free(residual);
+    free(cwd);
+}
+END_TEST
+
 void setup_talloc_context(void)
 {
     int ret;
@@ -645,6 +690,7 @@ Suite *krb5_utils_suite (void)
     tcase_add_checked_fixture (tc_create_dir, setup_create_dir,
                                teardown_create_dir);
     tcase_add_test (tc_create_dir, test_illegal_patterns);
+    tcase_add_test (tc_create_dir, test_cc_dir_create);
     if (getuid() == 0) {
         tcase_add_test (tc_create_dir, test_priv_ccache_dir);
         tcase_add_test (tc_create_dir, test_private_ccache_dir_in_user_dir);
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index 5bc1140d0778882aa001a4a4504ddca42a63a4f8..8a6bfe2dc471e346b94069b2f7c04a671b8f4d37 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -604,11 +604,10 @@ void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name)
 }
 
 
-krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
+krb5_error_code check_for_valid_tgt(krb5_context context,
+                                    krb5_ccache ccache, const char *realm,
                                     const char *client_princ_str, bool *result)
 {
-    krb5_context context = NULL;
-    krb5_ccache ccache = NULL;
     krb5_error_code krberr;
     TALLOC_CTX *tmp_ctx = NULL;
     krb5_creds mcred;
@@ -625,18 +624,6 @@ krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
         return ENOMEM;
     }
 
-    krberr = krb5_init_context(&context);
-    if (krberr) {
-        DEBUG(1, ("Failed to init kerberos context\n"));
-        goto done;
-    }
-
-    krberr = krb5_cc_resolve(context, ccname, &ccache);
-    if (krberr != 0) {
-        DEBUG(1, ("krb5_cc_resolve failed.\n"));
-        goto done;
-    }
-
     server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
     if (server_name == NULL) {
         DEBUG(1, ("talloc_asprintf failed.\n"));
@@ -685,10 +672,6 @@ done:
     if (server_principal != NULL) {
         krb5_free_principal(context, server_principal);
     }
-    if (ccache != NULL) {
-        krb5_cc_close(context, ccache);
-    }
-    if (context != NULL) krb5_free_context(context);
     talloc_free(tmp_ctx);
     return krberr;
 }
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index bba2a7eda38b2da9fa3d875b327026de7cbe50cf..89ec00021f09333e94b6fbf1b0bcc1394f9523df 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -55,7 +55,8 @@ void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
 
 void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name);
 
-krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
+krb5_error_code check_for_valid_tgt(krb5_context context,
+                                    krb5_ccache ccache, const char *realm,
                                     const char *client_princ_str, bool *result);
 
 int sss_krb5_verify_keytab(const char *principal,
-- 
1.7.10.2

-------------- next part --------------
>From fa696ab9a195873796c384dfac982c8299908f06 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 11 Jun 2012 14:35:35 +0200
Subject: [PATCH 10/11] Use Kerberos context in KRB5_DEBUG

Passing Kerberos context to sss_krb5_get_error_message will allow us to
get better error messages.
---
 src/providers/krb5/krb5_child.c |   85 ++++++++++++++++++---------------------
 src/providers/krb5/krb5_utils.c |   31 +++++++++-----
 src/tests/krb5_child-test.c     |   14 ++-----
 src/util/sss_krb5.h             |    8 ++++
 4 files changed, 73 insertions(+), 65 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 93844fab9925ef67181105cb73c452f5796f0272..a45d03330e06a5a9f1676e4e97beef61aefc7930 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -99,13 +99,7 @@ struct krb5_req {
 };
 
 static krb5_context krb5_error_ctx;
-static const char *__krb5_error_msg;
-#define KRB5_DEBUG(level, krb5_error) do { \
-    __krb5_error_msg = sss_krb5_get_error_message(krb5_error_ctx, krb5_error); \
-    DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
-    sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
-    sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
-} while(0)
+#define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
 
 static void sss_krb5_expire_callback_func(krb5_context context, void *data,
                                           krb5_timestamp password_expiration,
@@ -230,14 +224,14 @@ store_creds_in_ccache(krb5_context ctx, krb5_principal princ,
 
     kerr = krb5_cc_initialize(ctx, cc, princ);
     if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
         goto done;
     }
 
     if (creds == NULL) {
         kerr = create_empty_cred(ctx, princ, &l_cred);
         if (kerr != 0) {
-            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
             goto done;
         }
     } else {
@@ -246,19 +240,19 @@ store_creds_in_ccache(krb5_context ctx, krb5_principal princ,
 
     kerr = krb5_cc_store_cred(ctx, cc, l_cred);
     if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
         goto done;
     }
 
     kerr = krb5_cc_switch(ctx, cc);
     if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
         goto done;
     }
 
     kerr = krb5_cc_close(ctx, cc);
     if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
         goto done;
     }
 
@@ -325,7 +319,7 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
 
     kerr = krb5_cc_resolve(ctx, tmp_ccname, &tmp_cc);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
 
@@ -335,7 +329,7 @@ static krb5_error_code create_ccache_file(krb5_context ctx,
         fd = -1;
     }
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
 
@@ -394,7 +388,7 @@ create_ccache_in_dir(krb5_context ctx,
          */
         kerr = krb5_cc_resolve(ctx, ccname, &tmp_cc);
         if (kerr != 0) {
-            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
             goto done;
         }
     } else if (dirname[0] == '/') {
@@ -403,13 +397,13 @@ create_ccache_in_dir(krb5_context ctx,
          */
         kerr = krb5_cc_set_default_name(ctx, ccname);
         if (kerr != 0) {
-            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
             goto done;
         }
 
         kerr = krb5_cc_new_unique(ctx, "DIR", NULL, &tmp_cc);
         if (kerr != 0) {
-            KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
             goto done;
         }
     } else {
@@ -420,7 +414,7 @@ create_ccache_in_dir(krb5_context ctx,
 
     kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds);
     if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
         goto done;
     }
 
@@ -766,14 +760,14 @@ static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx,
     kerr = krb5_get_init_creds_keytab(ctx, &creds, princ, keytab, 0, NULL,
                                       &options);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         return kerr;
     }
 
     /* Use the updated principal in the creds in case canonicalized */
     kerr = create_ccache_file(ctx, creds.client, ccname, &creds);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
     kerr = 0;
@@ -796,21 +790,21 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
                                                   sss_krb5_expire_callback_func,
                                                   kr);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         DEBUG(1, ("Failed to set expire callback, continue without.\n"));
     }
     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
                                         password, sss_krb5_prompter, kr, 0,
                                         NULL, kr->options);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         return kerr;
     }
 
     if (kr->validate) {
         kerr = validate_tgt(kr);
         if (kerr != 0) {
-            KRB5_DEBUG(1, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
             return kerr;
         }
 
@@ -834,7 +828,7 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
                          kr->creds ? kr->creds->client : kr->princ,
                          kr->ccname, kr->creds);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
 
@@ -904,7 +898,7 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
                                         changepw_princ,
                                         kr->options);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         if (kerr == KRB5_KDC_UNREACH) {
             pam_status = PAM_AUTHINFO_UNAVAIL;
         }
@@ -944,7 +938,7 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
 
     if (kerr != 0 || result_code != 0) {
         if (kerr != 0) {
-            KRB5_DEBUG(1, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         } else {
             kerr = KRB5KRB_ERR_GENERIC;
         }
@@ -996,7 +990,7 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     memset(kr->pd->newauthtok, 0, kr->pd->newauthtok_size);
 
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         if (kerr == KRB5_KDC_UNREACH) {
             pam_status = PAM_AUTHINFO_UNAVAIL;
         }
@@ -1058,7 +1052,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
                                                                kr->options,
                                                                NULL, NULL);
         if (kerr != 0) {
-            KRB5_DEBUG(1, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
             DEBUG(1, ("Failed to unset expire callback, continue ...\n"));
         }
         kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
@@ -1076,7 +1070,7 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
     memset(kr->pd->authtok, 0, kr->pd->authtok_size);
 
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         switch (kerr) {
             case KRB5_KDC_UNREACH:
                     pam_status = PAM_AUTHINFO_UNAVAIL;
@@ -1164,13 +1158,13 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
 
     kerr = krb5_cc_resolve(kr->ctx, ccname, &ccache);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
 
     kerr = krb5_get_renewed_creds(kr->ctx, kr->creds, kr->princ, ccache, NULL);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         if (kerr == KRB5_KDC_UNREACH) {
             status = PAM_AUTHINFO_UNAVAIL;
             DEBUG(SSSDBG_TRACE_ALL, ("kdc unreachable for renewed creds.\n"));
@@ -1181,7 +1175,7 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
     if (kr->validate) {
         kerr = validate_tgt(kr);
         if (kerr != 0) {
-            KRB5_DEBUG(1, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
             goto done;
         }
 
@@ -1203,13 +1197,13 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
 
     kerr = krb5_cc_initialize(kr->ctx, ccache, kr->princ);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
 
     kerr = krb5_cc_store_cred(kr->ctx, ccache, kr->creds);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto done;
     }
 
@@ -1245,7 +1239,7 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
 
     ret = create_ccache(kr->ctx, kr->princ, kr->ccname, NULL);
     if (ret != 0) {
-        KRB5_DEBUG(1, ret);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, ret);
         pam_status = PAM_SYSTEM_ERR;
     }
 
@@ -1582,19 +1576,20 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 
     kerr = krb5_init_context(&kr->ctx);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto failed;
     }
+    krb5_error_ctx = kr->ctx;
 
     kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto failed;
     }
 
     kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto failed;
     }
 
@@ -1607,7 +1602,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 
     kerr = sss_krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto failed;
     }
 
@@ -1617,7 +1612,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
      * but shall return KRB5KDC_ERR_KEY_EXP. */
     krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
     if (kerr != 0) {
-        KRB5_DEBUG(1, kerr);
+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
         goto failed;
     }
 #endif
@@ -1631,7 +1626,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         if (kerr != 0) {
             DEBUG(1, ("krb5_string_to_deltat failed for [%s].\n",
                       lifetime_str));
-            KRB5_DEBUG(1, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
             goto failed;
         }
         DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
@@ -1648,7 +1643,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         if (kerr != 0) {
             DEBUG(1, ("krb5_string_to_deltat failed for [%s].\n",
                       lifetime_str));
-            KRB5_DEBUG(1, kerr);
+            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
             goto failed;
         }
         DEBUG(SSSDBG_CONF_SETTINGS,
@@ -1705,7 +1700,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
                                      kr, &kr->fast_ccname);
             if (kerr != 0) {
                 DEBUG(1, ("check_fast_ccache failed.\n"));
-                KRB5_DEBUG(1, kerr);
+                KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
                 goto failed;
             }
 
@@ -1715,7 +1710,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
             if (kerr != 0) {
                 DEBUG(1, ("sss_krb5_get_init_creds_opt_set_fast_ccache_name "
                           "failed.\n"));
-                KRB5_DEBUG(1, kerr);
+                KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
                 goto failed;
             }
 
@@ -1726,7 +1721,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
                 if (kerr != 0) {
                     DEBUG(1, ("sss_krb5_get_init_creds_opt_set_fast_flags "
                               "failed.\n"));
-                    KRB5_DEBUG(1, kerr);
+                    KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
                     goto failed;
                 }
             }
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index e32703d8ae37d07241397ae2ad22c5641f0d0017..b50e76a0e3c48b2f2478da7d1e11a4c59617c898 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -436,7 +436,8 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
 
     kerr = krb5_parse_name(ctx, client_name, &client_princ);
     if (kerr != 0) {
-        DEBUG(1, ("krb5_parse_name failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_parse_name failed.\n"));
         goto done;
     }
 
@@ -454,13 +455,15 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
     kerr = krb5_parse_name(ctx, server_name, &server_princ);
     talloc_free(server_name);
     if (kerr != 0) {
-        DEBUG(1, ("krb5_parse_name failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_parse_name failed.\n"));
         goto done;
     }
 
     kerr = krb5_cc_resolve(ctx, ccache_file, &cc);
     if (kerr != 0) {
-        DEBUG(1, ("krb5_cc_resolve failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
         goto done;
     }
 
@@ -472,7 +475,8 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
 
     kerr = krb5_cc_retrieve_cred(ctx, cc, 0, &mcred, &cred);
     if (kerr != 0) {
-        DEBUG(1, ("krb5_cc_retrieve_cred failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_retrieve_cred failed.\n"));
         goto done;
     }
 
@@ -485,7 +489,8 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
 
     kerr = krb5_cc_close(ctx, cc);
     if (kerr != 0) {
-        DEBUG(1, ("krb5_cc_close failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_close failed.\n"));
         goto done;
     }
     cc = NULL;
@@ -694,6 +699,7 @@ cc_file_check_existing(const char *location, uid_t uid,
 
     kerr = krb5_cc_resolve(context, location, &ccache);
     if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, kerr);
         krb5_free_context(context);
         DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
         return EIO;
@@ -703,7 +709,8 @@ cc_file_check_existing(const char *location, uid_t uid,
     krb5_free_context(context);
     krb5_cc_close(context, ccache);
     if (kerr != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE,
               ("Could not check if ccache contains a valid principal\n"));
         return EIO;
     }
@@ -783,13 +790,15 @@ get_ccache_for_princ(krb5_context context, const char *location,
 
     krberr = krb5_cc_set_default_name(context, location);
     if (krberr != 0) {
-        DEBUG(SSSDBG_OP_FAILURE, ("krb5_cc_resolve failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_cc_resolve failed.\n"));
         return krberr;
     }
 
     krberr = krb5_parse_name(context, princ, &client_principal);
     if (krberr != 0) {
-        DEBUG(SSSDBG_OP_FAILURE, ("krb5_parse_name failed.\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("krb5_parse_name failed.\n"));
         return krberr;
     }
 
@@ -846,7 +855,7 @@ cc_dir_check_existing(const char *location, uid_t uid,
     ret = cc_residual_is_used(uid, dir, SSS_KRB5_TYPE_DIR, &active);
     talloc_free(tmp);
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, ("Could not check if ccache is active\n"));
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not check if ccache is active\n"));
         return ret;
     }
 
@@ -876,6 +885,7 @@ cc_dir_check_existing(const char *location, uid_t uid,
 
     krberr = check_for_valid_tgt(context, ccache, realm, princ, &valid);
     if (krberr != EOK) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr);
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Could not check if ccache contains a valid principal\n"));
         ret = EIO;
@@ -931,7 +941,8 @@ cc_dir_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location,
     if (ccache) krb5_cc_close(context, ccache);
     krb5_free_context(context);
     if (krberr) {
-        DEBUG(SSSDBG_TRACE_FUNC, ("Could not get full name of ccache\n"));
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, context, krberr);
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not get full name of ccache\n"));
         return NULL;
     }
 
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index 59172384a052152fc302161ee293fb4ad3fe4927..6bbf756df9d29a8c470112c593e6adbfc59ac0ef 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -43,24 +43,18 @@ extern struct sss_krb5_cc_be file_cc;
 extern struct sss_krb5_cc_be dir_cc;
 
 static krb5_context krb5_error_ctx;
-#define KRB5_DEBUG(level, krb5_error) do { \
-    const char * __krb5_error_msg; \
-    __krb5_error_msg = sss_krb5_get_error_message(krb5_error_ctx, krb5_error); \
-    DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
-    sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
-    sss_krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \
-} while(0)
+#define KRB5_CHILD_TEST_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
 
 #define CHECK_KRET(kret, err) do {              \
     if (kret) {                                 \
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);    \
+        KRB5_CHILD_TEST_DEBUG(SSSDBG_OP_FAILURE, kret);    \
         return err;                             \
     }                                           \
 } while(0)                                      \
 
 #define CHECK_KRET_L(kret, err, label) do {     \
     if (kret) {                                 \
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);    \
+        KRB5_CHILD_TEST_DEBUG(SSSDBG_OP_FAILURE, kret);    \
         goto label;                             \
     }                                           \
 } while(0)                                      \
@@ -321,7 +315,7 @@ printtime(krb5_timestamp ts)
 
     kret = krb5_timestamp_to_sfstring(ts, timestring, BUFSIZ, &fill);
     if (kret) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, kret);
+        KRB5_CHILD_TEST_DEBUG(SSSDBG_OP_FAILURE, kret);
     }
     printf("%s", timestring);
 }
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index 89ec00021f09333e94b6fbf1b0bcc1394f9523df..34fdc495056d7c43293fd57e2c618cfc65b1186e 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -46,6 +46,14 @@ const char * KRB5_CALLCONV sss_krb5_get_error_message (krb5_context,
 
 void KRB5_CALLCONV sss_krb5_free_error_message(krb5_context, const char *);
 
+#define KRB5_DEBUG(level, errctx, krb5_error) do { \
+    const char *__krb5_error_msg; \
+    __krb5_error_msg = sss_krb5_get_error_message(errctx, krb5_error); \
+    DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \
+    sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
+    sss_krb5_free_error_message(errctx, __krb5_error_msg); \
+} while(0)
+
 krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_alloc(
                                                  krb5_context context,
                                                  krb5_get_init_creds_opt **opt);
-- 
1.7.10.2

-------------- next part --------------
>From 99011b651ac0abbcee4cceb7537c1787eafb3c49 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 13 Jun 2012 10:44:45 +0200
Subject: [PATCH 11/11] Switch Kerberos cache default to DIR

---
 src/man/sssd-krb5.5.xml        |    4 ++--
 src/providers/ipa/ipa_opts.h   |    4 ++--
 src/providers/krb5/krb5_opts.h |    4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 1baee36e9b0ade76de2990fd4f06bc655b5ce06c..46a5b86274ea1e0c56d6f014bc4e4d94bd5dcade 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -148,7 +148,7 @@
                             </citerefentry> for details) is created.
                         </para>
                         <para>
-                            Default: /tmp
+                            Default: /run/user/%u
                         </para>
                     </listitem>
                 </varlistentry>
@@ -208,7 +208,7 @@
                             used to create a unique filename in a safe way.
                         </para>
                         <para>
-                            Default: FILE:%d/krb5cc_%U_XXXXXX
+                            Default: DIR:%d
                         </para>
                     </listitem>
                 </varlistentry>
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 770406cfebe67ba5104163986a29f4a1a9f34869..2786bb5e35a2128554448fac545f38e6f899cc05 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -232,8 +232,8 @@ struct sdap_attr_map ipa_selinux_user_map[] = {
 struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-    { "krb5_ccachedir", DP_OPT_STRING, { "/tmp" }, NULL_STRING },
-    { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING},
+    { "krb5_ccachedir", DP_OPT_STRING, { "/run/user/%u" }, NULL_STRING },
+    { "krb5_ccname_template", DP_OPT_STRING, { "DIR:%d" }, NULL_STRING},
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
     { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
     { "krb5_validate", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
diff --git a/src/providers/krb5/krb5_opts.h b/src/providers/krb5/krb5_opts.h
index 7f47290c961ce6455e0d123e9ec14c7add39d28d..45dfec544f6d5261df313649658a1432f763ce5b 100644
--- a/src/providers/krb5/krb5_opts.h
+++ b/src/providers/krb5/krb5_opts.h
@@ -28,8 +28,8 @@
 struct dp_option default_krb5_opts[] = {
     { "krb5_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-    { "krb5_ccachedir", DP_OPT_STRING, { "/tmp" }, NULL_STRING },
-    { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING},
+    { "krb5_ccachedir", DP_OPT_STRING, { "/run/user/%u" }, NULL_STRING },
+    { "krb5_ccname_template", DP_OPT_STRING, { "DIR:%d" }, NULL_STRING},
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
     { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
     { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
-- 
1.7.10.2



More information about the sssd-devel mailing list