[SSSD] [PATCH] Move ccache operations to krb5_child, allow the krb5_auth code to run unprivileged

Jakub Hrozek jhrozek at redhat.com
Tue Nov 18 13:56:35 UTC 2014


On Mon, Nov 17, 2014 at 06:35:57PM +0100, Sumit Bose wrote:
> On Fri, Nov 14, 2014 at 01:52:20PM +0100, Jakub Hrozek wrote:
> > On Thu, Nov 13, 2014 at 07:30:41PM +0100, Jakub Hrozek wrote:
> > > On Wed, Nov 12, 2014 at 05:08:09PM +0100, Lukas Slebodnik wrote:
> > > 
> 
> ...
> 
> > > Thank you, see the attached patches.
> > 
> > I forgot to remove the extra find_uid.c from Makefile.am
> 
> I only have minor comments, see below. I understand that most part of
> the patches are refactorings to make sure the related code is run with
> the right permissions. (See next mail for some additional suggestions).
> 
> The ccache handling always was delicate, e.g. directory creation with
> DIR type and determine if FILE with _XXXXXX should be recreated or
> reused. I run couple to tests without an issue but I think only
> regression testing might give clarity here. So ACK from my side.

Thank you for testing. Lukas was kind enough to run all available tests
our QE has at their disposal related to Kerberos. I hope that raises the
confidence level a bit.

The attached patches fix all the issues you raised previously.
-------------- next part --------------
>From 694491e3b9bf6162ced4999f58819c63de5e2ca6 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 24 Oct 2014 22:44:17 +0200
Subject: [PATCH 1/6] BUILD: Install krb5_child as suid if running under
 non-privileged user

If sssd_be is running unprivileged, then krb5_child must be setuid to be
able to access the keytab and become arbitrary user.
---
 Makefile.am          | 2 ++
 contrib/sssd.spec.in | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index b85341f5845c3cffab8a2c95b1be1d32517316e8..5f265dcefd16ce4efdde4d62f3cd5d02dbce255f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2872,6 +2872,8 @@ endif
 if SSSD_USER
 	chgrp $(SSSD_USER) $(sssdlibexecdir)/ldap_child
 	chmod 4750 $(sssdlibexecdir)/ldap_child
+	chgrp $(SSSD_USER) $(sssdlibexecdir)/krb5_child
+	chmod 4750 $(sssdlibexecdir)/krb5_child
 if BUILD_SEMANAGE
 	chgrp $(SSSD_USER) $(sssdlibexecdir)/selinux_child
 	chmod 4750 $(sssdlibexecdir)/selinux_child
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 08a007f3339a81812c2f5fc21cf58fcd79212088..371e5f24fa18687f99a784357f047cc8aa390504 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -646,7 +646,7 @@ rm -rf $RPM_BUILD_ROOT
 %doc COPYING
 %{_libdir}/%{name}/libsss_krb5_common.so
 %attr(4750,root,sssd) %{_libexecdir}/%{servicename}/ldap_child
-%{_libexecdir}/%{servicename}/krb5_child
+%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/krb5_child
 
 %files krb5 -f sssd_krb5.lang
 %defattr(-,root,root,-)
-- 
1.9.3

-------------- next part --------------
>From ae7a1957fc13f27c2f0a76fad951d2d3d578a8ed Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 13 Oct 2014 21:13:38 +0200
Subject: [PATCH 2/6] KRB5: Drop privileges in the child, not the back end

In future patches, sssd_be will be running as a non-privileged user, who
will execute the setuid krb5_child. In this case, the child will start
as root and drop the privileges as soon as possible.

However, we need to also remove the privilege drop in sssd_be, because
if we dropped to the user who is authenticating, we wouldn't be even
allowed to execute krb5_child. The krb5_child permissions should be
4750, owned by root.sssd, to make sure only root and sssd can execute
the child and if executed by sssd, the child will run as root.
---
 src/providers/krb5/krb5_child.c         | 71 ++++++++++++++++++++++++++-------
 src/providers/krb5/krb5_child_handler.c |  8 ----
 2 files changed, 57 insertions(+), 22 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 3234a4e6c740db5e05f7db8eb7f4ea0cc126e7ce..b0bf76fb3b8a77831b114c47acff8cd0d3fd780d 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -1840,11 +1840,60 @@ static int k5c_setup_fast(struct krb5_req *kr, bool demand)
     return EOK;
 }
 
-static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+enum k5c_fast_opt {
+    K5C_FAST_NEVER,
+    K5C_FAST_TRY,
+    K5C_FAST_DEMAND,
+};
+
+static errno_t check_use_fast(enum k5c_fast_opt *_fast_val)
 {
-    krb5_error_code kerr;
     char *use_fast_str;
+    enum k5c_fast_opt fast_val;
+
+    use_fast_str = getenv(SSSD_KRB5_USE_FAST);
+    if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
+        DEBUG(SSSDBG_CONF_SETTINGS, "Not using FAST.\n");
+        fast_val = K5C_FAST_NEVER;
+    } else if (strcasecmp(use_fast_str, "try") == 0) {
+        fast_val = K5C_FAST_TRY;
+    } else if (strcasecmp(use_fast_str, "demand") == 0) {
+        fast_val = K5C_FAST_DEMAND;
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                "Unsupported value [%s] for krb5_use_fast.\n",
+                use_fast_str);
+        return EINVAL;
+    }
+
+    *_fast_val = fast_val;
+    return EOK;
+}
+
+static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+{
+    krb5_error_code kerr;
     int parse_flags;
+    enum k5c_fast_opt fast_val;
+
+    kerr = check_use_fast(&fast_val);
+    if (kerr != EOK) {
+        return kerr;
+    }
+
+    if (offline || (fast_val == K5C_FAST_NEVER && kr->validate == false)) {
+        /* If krb5_child was started as setuid, but we don't need to
+         * perform either validation or FAST, just drop privileges to
+         * the user who is logging in. The same applies to the offline case
+         */
+        kerr = become_user(kr->uid, kr->gid);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
+            return kerr;
+        }
+    }
+    DEBUG(SSSDBG_TRACE_INTERNAL,
+          "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
 
     kr->realm = getenv(SSSD_KRB5_REALM);
     if (kr->realm == NULL) {
@@ -1931,18 +1980,12 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
     if (!offline) {
         set_canonicalize_option(kr->options);
 
-        use_fast_str = getenv(SSSD_KRB5_USE_FAST);
-        if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
-            DEBUG(SSSDBG_CONF_SETTINGS, "Not using FAST.\n");
-        } else if (strcasecmp(use_fast_str, "try") == 0) {
-            kerr = k5c_setup_fast(kr, false);
-        } else if (strcasecmp(use_fast_str, "demand") == 0) {
-            kerr = k5c_setup_fast(kr, true);
-        } else {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Unsupported value [%s] for krb5_use_fast.\n",
-                   use_fast_str);
-            return EINVAL;
+        if (fast_val != K5C_FAST_NEVER) {
+            kerr = k5c_setup_fast(kr, fast_val == K5C_FAST_DEMAND);
+            if (kerr != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, "Cannot set up FAST\n");
+                return kerr;
+            }
         }
     }
 
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 4ba939deb3e0e282b3ca9b2b21226ea7e64e311b..71c7f9c9f662e16b94afda0c8c0ae24666f0ba15 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -284,14 +284,6 @@ static errno_t fork_child(struct tevent_req *req)
     pid = fork();
 
     if (pid == 0) { /* child */
-        if (state->kr->run_as_user) {
-            ret = become_user(state->kr->uid, state->kr->gid);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
-                return ret;
-            }
-        }
-
         err = exec_child(state,
                          pipefd_to_child, pipefd_from_child,
                          KRB5_CHILD, state->kr->krb5_ctx->child_debug_fd);
-- 
1.9.3

-------------- next part --------------
>From 1e0dd98cbe2e1ad1a13984349623fb2304e3e6eb Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sat, 18 Oct 2014 20:52:43 +0200
Subject: [PATCH 3/6] KRB5: Move ccache-related functions to krb5_ccache.c

Add a new module krb5_ccache.c that contains all ccache-related
operations. The only user of this module shall be krb5_child.c as the
other modules will run unprivileged and accessing the ccache requires
either privileges of root or the ccache owner.
---
 Makefile.am                                        |   4 +
 src/providers/krb5/krb5_auth.c                     |  16 +-
 src/providers/krb5/krb5_auth.h                     |   1 +
 src/providers/krb5/{krb5_utils.c => krb5_ccache.c} | 720 +++++----------------
 src/providers/krb5/{krb5_utils.h => krb5_ccache.h} |  49 +-
 src/providers/krb5/krb5_child.c                    |   1 +
 src/providers/krb5/krb5_common.h                   |   7 -
 src/providers/krb5/krb5_renew_tgt.c                |   1 +
 src/providers/krb5/krb5_utils.c                    | 674 +------------------
 src/providers/krb5/krb5_utils.h                    |  15 -
 src/tests/krb5_child-test.c                        |   1 +
 src/tests/krb5_utils-tests.c                       |   1 +
 12 files changed, 198 insertions(+), 1292 deletions(-)
 copy src/providers/krb5/{krb5_utils.c => krb5_ccache.c} (57%)
 copy src/providers/krb5/{krb5_utils.h => krb5_ccache.h} (53%)

diff --git a/Makefile.am b/Makefile.am
index 5f265dcefd16ce4efdde4d62f3cd5d02dbce255f..4a69ecb0cfe48e20bde958c9351c2a5ece5ffffa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -568,6 +568,7 @@ dist_noinst_HEADERS = \
     src/providers/krb5/krb5_utils.h \
     src/providers/krb5/krb5_init_shared.h \
     src/providers/krb5/krb5_opts.h \
+    src/providers/krb5/krb5_ccache.h \
     src/providers/ldap/ldap_common.h \
     src/providers/ldap/sdap.h \
     src/providers/ldap/sdap_access.h \
@@ -1322,6 +1323,7 @@ strtonum_tests_LDADD = \
 krb5_utils_tests_SOURCES = \
     src/tests/krb5_utils-tests.c \
     src/providers/krb5/krb5_utils.c \
+    src/providers/krb5/krb5_ccache.c \
     src/providers/krb5/krb5_common.c \
     src/util/sss_krb5.c \
     src/providers/data_provider_fo.c \
@@ -1603,6 +1605,7 @@ stress_tests_LDADD = \
 krb5_child_test_SOURCES = \
     src/tests/krb5_child-test.c \
     src/providers/krb5/krb5_utils.c \
+    src/providers/krb5/krb5_ccache.c \
     src/providers/krb5/krb5_child_handler.c \
     src/providers/krb5/krb5_common.c \
     src/util/sss_krb5.c \
@@ -2306,6 +2309,7 @@ libsss_krb5_common_la_SOURCES = \
     src/providers/krb5/krb5_access.c \
     src/providers/krb5/krb5_child_handler.c \
     src/providers/krb5/krb5_init_shared.c \
+    src/providers/krb5/krb5_ccache.c \
     src/util/sss_krb5.c \
     src/util/become_user.c \
     $(NULL)
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index c96b7aee99da8c3d43a67a04bb1f67ee048d4705..bd8b51f47462f1eaef8da61b42caedda3475a4e7 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -39,21 +39,7 @@
 #include "util/child_common.h"
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
-
-static errno_t safe_remove_old_ccache_file(const char *old_ccache,
-                                           const char *new_ccache,
-                                           uid_t uid, gid_t gid)
-{
-    if ((old_ccache == new_ccache)
-        || (old_ccache && new_ccache
-            && (strcmp(old_ccache, new_ccache) == 0))) {
-        DEBUG(SSSDBG_TRACE_FUNC, "New and old ccache file are the same, "
-                                  "none will be deleted.\n");
-        return EOK;
-    }
-
-    return sss_krb5_cc_destroy(old_ccache, uid, gid);
-}
+#include "providers/krb5/krb5_ccache.h"
 
 static errno_t
 check_old_ccache(const char *old_ccache, struct krb5child_req *kr,
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 022dc9b7645f18d01a8a334371e178aa470d92a1..00cb658c418ade2e6a1d9b5362a40f97e0dc6c6b 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -32,6 +32,7 @@
 #include "providers/dp_backend.h"
 #include "util/child_common.h"
 #include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_ccache.h"
 
 #define CCACHE_ENV_NAME "KRB5CCNAME"
 
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_ccache.c
similarity index 57%
copy from src/providers/krb5/krb5_utils.c
copy to src/providers/krb5/krb5_ccache.c
index 0d2e281198390043068e21b412b57de04df6a12b..5586963338616519f36e5d75e796a597d3ac2f22 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_ccache.c
@@ -1,12 +1,13 @@
 /*
     SSSD
 
-    Kerberos 5 Backend Module -- Utilities
+    Kerberos 5 Backend Module -- ccache related utilities
 
     Authors:
         Sumit Bose <sbose at redhat.com>
+        Jakub Hrozek <jhrozek at redhat.com>
 
-    Copyright (C) 2009 Red Hat
+    Copyright (C) 2014 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
@@ -21,427 +22,37 @@
     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 <string.h>
-#include <stdlib.h>
-#include <libgen.h>
 
-#include "providers/krb5/krb5_utils.h"
-#include "providers/krb5/krb5_auth.h"
-#include "src/util/find_uid.h"
+#ifdef HAVE_KRB5_KRB5_H
+#include <krb5/krb5.h>
+#else
+#include <krb5.h>
+#endif
+
+#include "providers/krb5/krb5_ccache.h"
+#include "util/sss_krb5.h"
 #include "util/util.h"
 
-errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
-                          struct krb5_ctx *krb5_ctx,
-                          struct sss_domain_info *dom, const char *user,
-                          const char *user_dom, char **_upn)
+static errno_t
+check_ccache_re(const char *filename, pcre *illegal_re)
 {
-    const char *upn = NULL;
-    int ret;
+    errno_t ret;
 
-    if (krb5_ctx == NULL || dom == NULL || user == NULL || _upn == NULL) {
-        return EINVAL;
-    }
-
-    if (msg != NULL) {
-        upn = ldb_msg_find_attr_as_string(msg, SYSDB_CANONICAL_UPN, NULL);
-        if (upn != NULL) {
-            ret = EOK;
-            goto done;
-        }
-
-        upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
-        if (upn != NULL) {
-            ret = EOK;
-            goto done;
-        }
-    }
-
-    ret = krb5_get_simple_upn(mem_ctx, krb5_ctx, dom, user,
-                              user_dom, _upn);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "krb5_get_simple_upn failed.\n");
-        return ret;
-    }
-
-done:
-    if (ret == EOK && upn != NULL) {
-        *_upn = talloc_strdup(mem_ctx, upn);
-        if (*_upn == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-            return ENOMEM;
-        }
-    }
-
-    return ret;
-}
-
-errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
-                                         struct sss_domain_info *domain,
-                                         const char *user,
-                                         const char *upn)
-{
-    TALLOC_CTX *tmp_ctx;
-    int ret;
-    int sret;
-    const char *attrs[] = {SYSDB_UPN, SYSDB_CANONICAL_UPN, NULL};
-    struct sysdb_attrs *new_attrs;
-    struct ldb_result *res;
-    bool in_transaction = false;
-    const char *cached_upn;
-    const char *cached_canonical_upn;
-
-    if (sysdb == NULL || user == NULL || upn == NULL) {
-        return EINVAL;
-    }
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    ret = sysdb_get_user_attr(tmp_ctx, domain, user, attrs, &res);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_user_attr failed.\n");
-        goto done;
-    }
-
-    if (res->count != 1) {
-        DEBUG(SSSDBG_OP_FAILURE, "[%d] user objects for name [%s] found, " \
-                                  "expected 1.\n", res->count, user);
-        ret = EINVAL;
-        goto done;
-    }
-
-    cached_upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL);
-
-    if (cached_upn != NULL && strcmp(cached_upn, upn) == 0) {
-        DEBUG(SSSDBG_TRACE_ALL, "Cached UPN and new one match, "
-                                 "nothing to do.\n");
-        ret = EOK;
-        goto done;
-    }
-
-    cached_canonical_upn = ldb_msg_find_attr_as_string(res->msgs[0],
-                                                       SYSDB_CANONICAL_UPN,
-                                                       NULL);
-
-    if (cached_canonical_upn != NULL
-            && strcmp(cached_canonical_upn, upn) == 0) {
-        DEBUG(SSSDBG_TRACE_ALL, "Cached canonical UPN and new one match, "
-                                 "nothing to do.\n");
-        ret = EOK;
-        goto done;
-    }
-
-    DEBUG(SSSDBG_TRACE_LIBS, "Replacing canonical UPN [%s] with [%s] " \
-                              "for user [%s].\n",
-                              cached_canonical_upn == NULL ?
-                                                 "empty" : cached_canonical_upn,
-                              upn, user);
-
-    new_attrs = sysdb_new_attrs(tmp_ctx);
-    if (new_attrs == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
-        ret = ENOMEM;
-        goto done;
-    }
-
-    ret = sysdb_attrs_add_string(new_attrs, SYSDB_CANONICAL_UPN, upn);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
-        goto done;
-    }
-
-    ret = sysdb_transaction_start(sysdb);
-    if (ret != EOK) {
+    ret = pcre_exec(illegal_re, NULL, filename, strlen(filename),
+                    0, 0, NULL, 0);
+    if (ret == 0) {
         DEBUG(SSSDBG_OP_FAILURE,
-              "Error %d starting transaction (%s)\n", ret, strerror(ret));
-        goto done;
-    }
-    in_transaction = true;
-
-    ret = sysdb_set_entry_attr(sysdb, res->msgs[0]->dn, new_attrs,
-                               cached_canonical_upn == NULL ? SYSDB_MOD_ADD :
-                                                              SYSDB_MOD_REP);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed [%d][%s].\n",
-                                  ret, strerror(ret));
-        goto done;
-    }
-
-    ret = sysdb_transaction_commit(sysdb);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Failed to commit transaction!\n");
-        goto done;
-    }
-    in_transaction = false;
-
-    ret = EOK;
-
-done:
-    if (in_transaction) {
-        sret = sysdb_transaction_cancel(sysdb);
-        if (sret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
-        }
-    }
-
-    talloc_free(tmp_ctx);
-
-    return ret;
-}
-
-#define S_EXP_UID "{uid}"
-#define L_EXP_UID (sizeof(S_EXP_UID) - 1)
-#define S_EXP_USERID "{USERID}"
-#define L_EXP_USERID (sizeof(S_EXP_USERID) - 1)
-#define S_EXP_EUID "{euid}"
-#define L_EXP_EUID (sizeof(S_EXP_EUID) - 1)
-#define S_EXP_USERNAME "{username}"
-#define L_EXP_USERNAME (sizeof(S_EXP_USERNAME) - 1)
-
-char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template, bool file_mode,
-                             bool case_sensitive)
-{
-    char *copy;
-    char *p;
-    char *n;
-    char *result = NULL;
-    char *dummy;
-    char *name;
-    char *res = NULL;
-    const char *cache_dir_tmpl;
-    TALLOC_CTX *tmp_ctx = NULL;
-    char action;
-    bool rerun;
-
-    if (template == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Missing template.\n");
-        return NULL;
-    }
-
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) return NULL;
-
-    copy = talloc_strdup(tmp_ctx, template);
-    if (copy == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
-        goto done;
-    }
-
-    result = talloc_strdup(tmp_ctx, "");
-    if (result == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
-        goto done;
-    }
-
-    p = copy;
-    while ( (n = strchr(p, '%')) != NULL) {
-        *n = '\0';
-        n++;
-        if ( *n == '\0' ) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "format error, single %% at the end of the template.\n");
-            goto done;
-        }
-
-        rerun = true;
-        action = *n;
-        while (rerun) {
-            rerun = false;
-            switch (action) {
-            case 'u':
-                if (kr->pd->user == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "Cannot expand user name template "
-                              "because user name is empty.\n");
-                    goto done;
-                }
-                name = sss_get_cased_name(tmp_ctx, kr->pd->user,
-                                          case_sensitive);
-                if (!name) {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "sss_get_cased_name failed\n");
-                    goto done;
-                }
-
-                result = talloc_asprintf_append(result, "%s%s", p,
-                                                name);
-                break;
-            case 'U':
-                if (kr->uid <= 0) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand uid template "
-                              "because uid is invalid.\n");
-                    goto done;
-                }
-                result = talloc_asprintf_append(result, "%s%"SPRIuid, p,
-                                                kr->uid);
-                break;
-            case 'p':
-                if (kr->upn == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "Cannot expand user principal name template "
-                              "because upn is empty.\n");
-                    goto done;
-                }
-                result = talloc_asprintf_append(result, "%s%s", p, kr->upn);
-                break;
-            case '%':
-                result = talloc_asprintf_append(result, "%s%%", p);
-                break;
-            case 'r':
-                dummy = dp_opt_get_string(kr->krb5_ctx->opts, KRB5_REALM);
-                if (dummy == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, "Missing kerberos realm.\n");
-                    goto done;
-                }
-                result = talloc_asprintf_append(result, "%s%s", p, dummy);
-                break;
-            case 'h':
-                if (kr->homedir == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "Cannot expand home directory template "
-                              "because the path is not available.\n");
-                    goto done;
-                }
-                result = talloc_asprintf_append(result, "%s%s", p, kr->homedir);
-                break;
-            case 'd':
-                if (file_mode) {
-                    cache_dir_tmpl = dp_opt_get_string(kr->krb5_ctx->opts,
-                                                       KRB5_CCACHEDIR);
-                    if (cache_dir_tmpl == NULL) {
-                        DEBUG(SSSDBG_CRIT_FAILURE,
-                              "Missing credential cache directory.\n");
-                        goto done;
-                    }
-
-                    dummy = expand_ccname_template(tmp_ctx, kr, cache_dir_tmpl,
-                                                   false, case_sensitive);
-                    if (dummy == NULL) {
-                        DEBUG(SSSDBG_CRIT_FAILURE,
-                              "Expanding credential cache directory "
-                                  "template failed.\n");
-                        goto done;
-                    }
-                    result = talloc_asprintf_append(result, "%s%s", p, dummy);
-                    talloc_zfree(dummy);
-                } else {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "'%%d' is not allowed in this template.\n");
-                    goto done;
-                }
-                break;
-            case 'P':
-                if (!file_mode) {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "'%%P' is not allowed in this template.\n");
-                    goto done;
-                }
-                if (kr->pd->cli_pid == 0) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand PID template "
-                              "because PID is not available.\n");
-                    goto done;
-                }
-                result = talloc_asprintf_append(result, "%s%d", p,
-                                                kr->pd->cli_pid);
-                break;
-
-            /* Additional syntax from krb5.conf default_ccache_name */
-            case '{':
-                if (strncmp(n , S_EXP_UID, L_EXP_UID) == 0) {
-                    action = 'U';
-                    n += L_EXP_UID - 1;
-                    rerun = true;
-                    continue;
-                } else if (strncmp(n , S_EXP_USERID, L_EXP_USERID) == 0) {
-                    action = 'U';
-                    n += L_EXP_USERID - 1;
-                    rerun = true;
-                    continue;
-                } else if (strncmp(n , S_EXP_EUID, L_EXP_EUID) == 0) {
-                    /* SSSD does not distinguish betwen uid and euid,
-                     * so we treat both the same way */
-                    action = 'U';
-                    n += L_EXP_EUID - 1;
-                    rerun = true;
-                    continue;
-                } else if (strncmp(n , S_EXP_USERNAME, L_EXP_USERNAME) == 0) {
-                    action = 'u';
-                    n += L_EXP_USERNAME - 1;
-                    rerun = true;
-                    continue;
-                } else {
-                    /* ignore any expansion variable we do not understand and
-                     * let libkrb5 hndle it or fail */
-                    name = n;
-                    n = strchr(name, '}');
-                    if (!n) {
-                        DEBUG(SSSDBG_CRIT_FAILURE, 
-                              "Invalid substitution sequence in cache "
-                              "template. Missing closing '}' in [%s].\n",
-                              template);
-                        goto done;
-                    }
-                    result = talloc_asprintf_append(result, "%s%%%.*s", p,
-                                                    (int)(n - name + 1), name);
-                }
-                break;
-            default:
-                DEBUG(SSSDBG_CRIT_FAILURE,
-                      "format error, unknown template [%%%c].\n", *n);
-                goto done;
-            }
-        }
-
-        if (result == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
-            goto done;
-        }
-
-        p = n + 1;
-    }
-
-    result = talloc_asprintf_append(result, "%s", p);
-    if (result == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
-        goto done;
-    }
-
-    res = talloc_move(mem_ctx, &result);
-done:
-    talloc_zfree(tmp_ctx);
-    return res;
-}
-
-static errno_t check_parent_stat(struct stat *parent_stat, uid_t uid)
-{
-    if (parent_stat->st_uid != 0 && parent_stat->st_uid != uid) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Private directory can only be created below a directory "
-              "belonging to root or to [%"SPRIuid"].\n", uid);
+              "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;
     }
 
-    if (parent_stat->st_uid == uid) {
-        if (!(parent_stat->st_mode & S_IXUSR)) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Parent directory does not have the search bit set for "
-                   "the owner.\n");
-            return EINVAL;
-        }
-    } else {
-        if (!(parent_stat->st_mode & S_IXOTH)) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Parent directory does not have the search bit set for "
-                   "others.\n");
-            return EINVAL;
-        }
-    }
-
-    return EOK;
+    DEBUG(SSSDBG_CRIT_FAILURE, "pcre_exec failed [%d].\n", ret);
+    return EFAULT;
 }
 
 struct string_list {
@@ -523,31 +134,37 @@ done:
     return ret;
 }
 
-static errno_t
-check_ccache_re(const char *filename, pcre *illegal_re)
+static errno_t check_parent_stat(struct stat *parent_stat, uid_t uid)
 {
-    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);
+    if (parent_stat->st_uid != 0 && parent_stat->st_uid != uid) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Private directory can only be created below a directory "
+              "belonging to root or to [%"SPRIuid"].\n", uid);
         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;
+    if (parent_stat->st_uid == uid) {
+        if (!(parent_stat->st_mode & S_IXUSR)) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Parent directory does not have the search bit set for "
+                   "the owner.\n");
+            return EINVAL;
+        }
+    } else {
+        if (!(parent_stat->st_mode & S_IXOTH)) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Parent directory does not have the search bit set for "
+                   "others.\n");
+            return EINVAL;
+        }
+    }
+
+    return EOK;
 }
 
-errno_t
-create_ccache_dir(const char *ccdirname, pcre *illegal_re,
-                  uid_t uid, gid_t gid)
+errno_t create_ccache_dir(const char *ccdirname,
+                          pcre *illegal_re,
+                          uid_t uid, gid_t gid)
 {
     int ret = EFAULT;
     struct stat parent_stat;
@@ -625,113 +242,6 @@ done:
     return ret;
 }
 
-errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
-                             struct tgt_times *tgtt)
-{
-    krb5_error_code kerr;
-    krb5_context ctx = NULL;
-    krb5_ccache cc = NULL;
-    krb5_principal client_princ = NULL;
-    krb5_principal server_princ = NULL;
-    char *server_name;
-    krb5_creds mcred;
-    krb5_creds cred;
-    const char *realm_name;
-    int realm_length;
-
-    kerr = krb5_init_context(&ctx);
-    if (kerr != 0) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_init_context failed.\n");
-        goto done;
-    }
-
-    kerr = krb5_parse_name(ctx, client_name, &client_princ);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
-        goto done;
-    }
-
-    sss_krb5_princ_realm(ctx, client_princ, &realm_name, &realm_length);
-
-    server_name = talloc_asprintf(NULL, "krbtgt/%.*s@%.*s",
-                                  realm_length, realm_name,
-                                  realm_length, realm_name);
-    if (server_name == NULL) {
-        kerr = KRB5_CC_NOMEM;
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
-        goto done;
-    }
-
-    kerr = krb5_parse_name(ctx, server_name, &server_princ);
-    talloc_free(server_name);
-    if (kerr != 0) {
-        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) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_resolve failed.\n");
-        goto done;
-    }
-
-    memset(&mcred, 0, sizeof(mcred));
-    memset(&cred, 0, sizeof(mcred));
-
-    mcred.server = server_princ;
-    mcred.client = client_princ;
-
-    kerr = krb5_cc_retrieve_cred(ctx, cc, 0, &mcred, &cred);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_retrieve_cred failed.\n");
-        goto done;
-    }
-
-    tgtt->authtime = cred.times.authtime;
-    tgtt->starttime = cred.times.starttime;
-    tgtt->endtime = cred.times.endtime;
-    tgtt->renew_till = cred.times.renew_till;
-
-    krb5_free_cred_contents(ctx, &cred);
-
-    kerr = krb5_cc_close(ctx, cc);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_close failed.\n");
-        goto done;
-    }
-    cc = NULL;
-
-    kerr = 0;
-
-done:
-    if (cc != NULL) {
-        krb5_cc_close(ctx, cc);
-    }
-
-    if (client_princ != NULL) {
-        krb5_free_principal(ctx, client_princ);
-    }
-
-    if (server_princ != NULL) {
-        krb5_free_principal(ctx, server_princ);
-    }
-
-    if (ctx != NULL) {
-        krb5_free_context(ctx);
-    }
-
-    if (kerr != 0) {
-        return EIO;
-    }
-
-    return EOK;
-}
-
 errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
                                   uid_t uid, gid_t gid)
 {
@@ -783,7 +293,6 @@ done:
     return ret;
 }
 
-
 struct sss_krb5_ccache {
     struct sss_creds *creds;
     krb5_context context;
@@ -895,7 +404,6 @@ done:
     return ret;
 }
 
-
 /* This function is called only as a way to validate that we have the
  * right cache */
 errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
@@ -1086,22 +594,124 @@ done:
     return ret;
 }
 
-
-errno_t get_domain_or_subdomain(struct be_ctx *be_ctx,
-                                char *domain_name,
-                                struct sss_domain_info **dom)
+errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
+                             struct tgt_times *tgtt)
 {
+    krb5_error_code kerr;
+    krb5_context ctx = NULL;
+    krb5_ccache cc = NULL;
+    krb5_principal client_princ = NULL;
+    krb5_principal server_princ = NULL;
+    char *server_name;
+    krb5_creds mcred;
+    krb5_creds cred;
+    const char *realm_name;
+    int realm_length;
 
-    if (domain_name != NULL &&
-        strcasecmp(domain_name, be_ctx->domain->name) != 0) {
-        *dom = find_domain_by_name(be_ctx->domain, domain_name, true);
-        if (*dom == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n");
-            return ENOMEM;
-        }
-    } else {
-        *dom = be_ctx->domain;
+    kerr = krb5_init_context(&ctx);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_init_context failed.\n");
+        goto done;
+    }
+
+    kerr = krb5_parse_name(ctx, client_name, &client_princ);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
+        goto done;
+    }
+
+    sss_krb5_princ_realm(ctx, client_princ, &realm_name, &realm_length);
+
+    server_name = talloc_asprintf(NULL, "krbtgt/%.*s@%.*s",
+                                  realm_length, realm_name,
+                                  realm_length, realm_name);
+    if (server_name == NULL) {
+        kerr = KRB5_CC_NOMEM;
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
+        goto done;
+    }
+
+    kerr = krb5_parse_name(ctx, server_name, &server_princ);
+    talloc_free(server_name);
+    if (kerr != 0) {
+        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) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_resolve failed.\n");
+        goto done;
+    }
+
+    memset(&mcred, 0, sizeof(mcred));
+    memset(&cred, 0, sizeof(mcred));
+
+    mcred.server = server_princ;
+    mcred.client = client_princ;
+
+    kerr = krb5_cc_retrieve_cred(ctx, cc, 0, &mcred, &cred);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_retrieve_cred failed.\n");
+        goto done;
+    }
+
+    tgtt->authtime = cred.times.authtime;
+    tgtt->starttime = cred.times.starttime;
+    tgtt->endtime = cred.times.endtime;
+    tgtt->renew_till = cred.times.renew_till;
+
+    krb5_free_cred_contents(ctx, &cred);
+
+    kerr = krb5_cc_close(ctx, cc);
+    if (kerr != 0) {
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
+        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_close failed.\n");
+        goto done;
+    }
+    cc = NULL;
+
+    kerr = 0;
+
+done:
+    if (cc != NULL) {
+        krb5_cc_close(ctx, cc);
+    }
+
+    if (client_princ != NULL) {
+        krb5_free_principal(ctx, client_princ);
+    }
+
+    if (server_princ != NULL) {
+        krb5_free_principal(ctx, server_princ);
+    }
+
+    if (ctx != NULL) {
+        krb5_free_context(ctx);
+    }
+
+    if (kerr != 0) {
+        return EIO;
     }
 
     return EOK;
 }
+
+errno_t safe_remove_old_ccache_file(const char *old_ccache,
+                                    const char *new_ccache,
+                                    uid_t uid, gid_t gid)
+{
+    if ((old_ccache == new_ccache)
+        || (old_ccache && new_ccache
+            && (strcmp(old_ccache, new_ccache) == 0))) {
+        DEBUG(SSSDBG_TRACE_FUNC, "New and old ccache file are the same, "
+                                  "none will be deleted.\n");
+        return EOK;
+    }
+
+    return sss_krb5_cc_destroy(old_ccache, uid, gid);
+}
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_ccache.h
similarity index 53%
copy from src/providers/krb5/krb5_utils.h
copy to src/providers/krb5/krb5_ccache.h
index f54a07f7936a361c21ca933026ee753a89fe5808..9f0b3ac84b7af118c315ca00a7c52f200534d97e 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_ccache.h
@@ -1,13 +1,13 @@
 /*
     SSSD
 
-    Kerberos Backend, header file for utilities
+    Kerberos 5 Backend Module -- ccache related utilities
 
     Authors:
         Sumit Bose <sbose at redhat.com>
+        Jakub Hrozek <jhrozek at redhat.com>
 
-    Copyright (C) 2009 Red Hat
-
+    Copyright (C) 2014 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
@@ -23,45 +23,38 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef __KRB5_UTILS_H__
-#define __KRB5_UTILS_H__
+#ifndef __KRB5_CCACHE_H__
+#define __KRB5_CCACHE_H__
 
-#include <talloc.h>
-#include "config.h"
+#include "util/util.h"
 
-#include "providers/krb5/krb5_auth.h"
-#include "providers/data_provider.h"
+struct tgt_times {
+    time_t authtime;
+    time_t starttime;
+    time_t endtime;
+    time_t renew_till;
+};
 
-errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
-                          struct krb5_ctx *krb5_ctx,
-                          struct sss_domain_info *dom, const char *user,
-                          const char *user_dom, char **_upn);
-
-errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
-                                         struct sss_domain_info *domain,
-                                         const char *user,
-                                         const char *upn);
-
-errno_t create_ccache_dir(const char *dirname, pcre *illegal_re,
+errno_t create_ccache_dir(const char *ccdirname,
+                          pcre *illegal_re,
                           uid_t uid, gid_t gid);
 
-char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template, bool file_mode,
-                             bool case_sensitive);
-
 errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
                                   uid_t uid, gid_t gid);
+
 errno_t sss_krb5_cc_destroy(const char *ccname, uid_t uid, gid_t gid);
+
 errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
                                     const char *ccname, const char *principal);
+
 errno_t sss_krb5_cc_verify_ccache(const char *ccname, uid_t uid, gid_t gid,
                                   const char *realm, const char *principal);
 
 errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
                              struct tgt_times *tgtt);
 
+errno_t safe_remove_old_ccache_file(const char *old_ccache,
+                                    const char *new_ccache,
+                                    uid_t uid, gid_t gid);
 
-errno_t get_domain_or_subdomain(struct be_ctx *be_ctx,
-                                char *domain_name,
-                                struct sss_domain_info **dom);
-#endif /* __KRB5_UTILS_H__ */
+#endif /* __KRB5_CCACHE_H__ */
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index b0bf76fb3b8a77831b114c47acff8cd0d3fd780d..7fa5f0c344a4afe110afa08f479f283aefce8d23 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -1885,6 +1885,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
         /* If krb5_child was started as setuid, but we don't need to
          * perform either validation or FAST, just drop privileges to
          * the user who is logging in. The same applies to the offline case
+         * the user who is logging in. The same applies to the offline case.
          */
         kerr = become_user(kr->uid, kr->gid);
         if (kerr != 0) {
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index eac0d6b1f0c0fec4a107a7b830d8b0c927f4fe42..a5cee6497e4930b16b1102a525d9fa3452845a58 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -73,13 +73,6 @@ enum krb5_opts {
 
 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
 
-struct tgt_times {
-    time_t authtime;
-    time_t starttime;
-    time_t endtime;
-    time_t renew_till;
-};
-
 struct krb5_service {
     char *name;
     char *realm;
diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
index 12963549829c4bbb0f799c5d00b9b8986ccfd485..5277c0f7691dd272a69b3024cb2c0bd7545a8fbd 100644
--- a/src/providers/krb5/krb5_renew_tgt.c
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -27,6 +27,7 @@
 #include "providers/krb5/krb5_common.h"
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_ccache.h"
 
 #define INITIAL_TGT_TABLE_SIZE 10
 
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 0d2e281198390043068e21b412b57de04df6a12b..ae72b04be236cfce9b6f794c602887491ba487a9 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -26,6 +26,7 @@
 #include <libgen.h>
 
 #include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_ccache.h"
 #include "providers/krb5/krb5_auth.h"
 #include "src/util/find_uid.h"
 #include "util/util.h"
@@ -379,7 +380,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                     name = n;
                     n = strchr(name, '}');
                     if (!n) {
-                        DEBUG(SSSDBG_CRIT_FAILURE, 
+                        DEBUG(SSSDBG_CRIT_FAILURE,
                               "Invalid substitution sequence in cache "
                               "template. Missing closing '}' in [%s].\n",
                               template);
@@ -416,677 +417,6 @@ done:
     return res;
 }
 
-static errno_t check_parent_stat(struct stat *parent_stat, uid_t uid)
-{
-    if (parent_stat->st_uid != 0 && parent_stat->st_uid != uid) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Private directory can only be created below a directory "
-              "belonging to root or to [%"SPRIuid"].\n", uid);
-        return EINVAL;
-    }
-
-    if (parent_stat->st_uid == uid) {
-        if (!(parent_stat->st_mode & S_IXUSR)) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Parent directory does not have the search bit set for "
-                   "the owner.\n");
-            return EINVAL;
-        }
-    } else {
-        if (!(parent_stat->st_mode & S_IXOTH)) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Parent directory does not have the search bit set for "
-                   "others.\n");
-            return EINVAL;
-        }
-    }
-
-    return EOK;
-}
-
-struct string_list {
-    struct string_list *next;
-    struct string_list *prev;
-    char *s;
-};
-
-static errno_t find_ccdir_parent_data(TALLOC_CTX *mem_ctx,
-                                      const char *ccdirname,
-                                      struct stat *parent_stat,
-                                      struct string_list **missing_parents)
-{
-    int ret = EFAULT;
-    char *parent = NULL;
-    char *end;
-    struct string_list *li;
-
-    ret = stat(ccdirname, parent_stat);
-    if (ret == EOK) {
-        if ( !S_ISDIR(parent_stat->st_mode) ) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "[%s] is not a directory.\n", ccdirname);
-            return EINVAL;
-        }
-        return EOK;
-    } else {
-        if (errno != ENOENT) {
-            ret = errno;
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "stat for [%s] failed: [%d][%s].\n", ccdirname, ret,
-                   strerror(ret));
-            return ret;
-        }
-    }
-
-    li = talloc_zero(mem_ctx, struct string_list);
-    if (li == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "talloc_zero failed.\n");
-        return ENOMEM;
-    }
-
-    li->s = talloc_strdup(li, ccdirname);
-    if (li->s == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "talloc_strdup failed.\n");
-        return ENOMEM;
-    }
-
-    DLIST_ADD(*missing_parents, li);
-
-    parent = talloc_strdup(mem_ctx, ccdirname);
-    if (parent == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "talloc_strdup failed.\n");
-        return ENOMEM;
-    }
-
-    /* We'll remove all trailing slashes from the back so that
-     * we only pass /some/path to find_ccdir_parent_data, not
-     * /some/path */
-    do {
-        end = strrchr(parent, '/');
-        if (end == NULL || end == parent) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "Cannot find parent directory of [%s], / is not allowed.\n",
-                   ccdirname);
-            ret = EINVAL;
-            goto done;
-        }
-        *end = '\0';
-    } while (*(end+1) == '\0');
-
-    ret = find_ccdir_parent_data(mem_ctx, parent, parent_stat, missing_parents);
-
-done:
-    talloc_free(parent);
-    return ret;
-}
-
-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;
-}
-
-errno_t
-create_ccache_dir(const char *ccdirname, pcre *illegal_re,
-                  uid_t uid, gid_t gid)
-{
-    int ret = EFAULT;
-    struct stat parent_stat;
-    struct string_list *missing_parents = NULL;
-    struct string_list *li = NULL;
-    mode_t old_umask;
-    mode_t new_dir_mode;
-    TALLOC_CTX *tmp_ctx = NULL;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    if (*ccdirname != '/') {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              "Only absolute paths are allowed, not [%s] .\n", ccdirname);
-        ret = EINVAL;
-        goto done;
-    }
-
-    if (illegal_re != NULL) {
-        ret = check_ccache_re(ccdirname, illegal_re);
-        if (ret != EOK) {
-            goto done;
-        }
-    }
-
-    ret = find_ccdir_parent_data(tmp_ctx, ccdirname, &parent_stat,
-                                 &missing_parents);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              "find_ccdir_parent_data failed.\n");
-        goto done;
-    }
-
-    ret = check_parent_stat(&parent_stat, uid);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Check the ownership and permissions of krb5_ccachedir: [%s].\n",
-              ccdirname);
-        goto done;
-    }
-
-    DLIST_FOR_EACH(li, missing_parents) {
-        DEBUG(SSSDBG_TRACE_INTERNAL,
-              "Creating directory [%s].\n", li->s);
-        new_dir_mode = 0700;
-
-        old_umask = umask(0000);
-        ret = mkdir(li->s, new_dir_mode);
-        umask(old_umask);
-        if (ret != EOK) {
-            ret = errno;
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "mkdir [%s] failed: [%d][%s].\n", li->s, ret,
-                   strerror(ret));
-            goto done;
-        }
-        ret = chown(li->s, uid, gid);
-        if (ret != EOK) {
-            ret = errno;
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "chown failed [%d][%s].\n", ret, strerror(ret));
-            goto done;
-        }
-    }
-
-    ret = EOK;
-
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
-                             struct tgt_times *tgtt)
-{
-    krb5_error_code kerr;
-    krb5_context ctx = NULL;
-    krb5_ccache cc = NULL;
-    krb5_principal client_princ = NULL;
-    krb5_principal server_princ = NULL;
-    char *server_name;
-    krb5_creds mcred;
-    krb5_creds cred;
-    const char *realm_name;
-    int realm_length;
-
-    kerr = krb5_init_context(&ctx);
-    if (kerr != 0) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_init_context failed.\n");
-        goto done;
-    }
-
-    kerr = krb5_parse_name(ctx, client_name, &client_princ);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
-        goto done;
-    }
-
-    sss_krb5_princ_realm(ctx, client_princ, &realm_name, &realm_length);
-
-    server_name = talloc_asprintf(NULL, "krbtgt/%.*s@%.*s",
-                                  realm_length, realm_name,
-                                  realm_length, realm_name);
-    if (server_name == NULL) {
-        kerr = KRB5_CC_NOMEM;
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
-        goto done;
-    }
-
-    kerr = krb5_parse_name(ctx, server_name, &server_princ);
-    talloc_free(server_name);
-    if (kerr != 0) {
-        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) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_resolve failed.\n");
-        goto done;
-    }
-
-    memset(&mcred, 0, sizeof(mcred));
-    memset(&cred, 0, sizeof(mcred));
-
-    mcred.server = server_princ;
-    mcred.client = client_princ;
-
-    kerr = krb5_cc_retrieve_cred(ctx, cc, 0, &mcred, &cred);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_retrieve_cred failed.\n");
-        goto done;
-    }
-
-    tgtt->authtime = cred.times.authtime;
-    tgtt->starttime = cred.times.starttime;
-    tgtt->endtime = cred.times.endtime;
-    tgtt->renew_till = cred.times.renew_till;
-
-    krb5_free_cred_contents(ctx, &cred);
-
-    kerr = krb5_cc_close(ctx, cc);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, ctx, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_close failed.\n");
-        goto done;
-    }
-    cc = NULL;
-
-    kerr = 0;
-
-done:
-    if (cc != NULL) {
-        krb5_cc_close(ctx, cc);
-    }
-
-    if (client_princ != NULL) {
-        krb5_free_principal(ctx, client_princ);
-    }
-
-    if (server_princ != NULL) {
-        krb5_free_principal(ctx, server_princ);
-    }
-
-    if (ctx != NULL) {
-        krb5_free_context(ctx);
-    }
-
-    if (kerr != 0) {
-        return EIO;
-    }
-
-    return EOK;
-}
-
-errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
-                                  uid_t uid, gid_t gid)
-{
-    TALLOC_CTX *tmp_ctx = NULL;
-    const char *filename;
-    char *ccdirname;
-    char *end;
-    errno_t ret;
-
-    if (ccname[0] == '/') {
-        filename = ccname;
-    } else if (strncmp(ccname, "FILE:", 5) == 0) {
-        filename = ccname + 5;
-    } else if (strncmp(ccname, "DIR:", 4) == 0) {
-        filename = ccname + 4;
-    } else {
-        /* only FILE and DIR types need precreation so far, we ignore any
-         * other type */
-        return EOK;
-    }
-
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) return ENOMEM;
-
-    ccdirname = talloc_strdup(tmp_ctx, filename);
-    if (ccdirname == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
-        ret = ENOMEM;
-        goto done;
-    }
-
-    /* We'll remove all trailing slashes from the back so that
-     * we only pass /some/path to find_ccdir_parent_data, not
-     * /some/path/ */
-    do {
-        end = strrchr(ccdirname, '/');
-        if (end == NULL || end == ccdirname) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find parent directory of [%s], "
-                  "/ is not allowed.\n", ccdirname);
-            ret = EINVAL;
-            goto done;
-        }
-        *end = '\0';
-    } while (*(end+1) == '\0');
-
-    ret = create_ccache_dir(ccdirname, illegal_re, uid, gid);
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-
-struct sss_krb5_ccache {
-    struct sss_creds *creds;
-    krb5_context context;
-    krb5_ccache ccache;
-};
-
-static int sss_free_krb5_ccache(void *mem)
-{
-    struct sss_krb5_ccache *cc = talloc_get_type(mem, struct sss_krb5_ccache);
-
-    if (cc->ccache) {
-        krb5_cc_close(cc->context, cc->ccache);
-    }
-    krb5_free_context(cc->context);
-    restore_creds(cc->creds);
-    return 0;
-}
-
-static errno_t sss_open_ccache_as_user(TALLOC_CTX *mem_ctx,
-                                       const char *ccname,
-                                       uid_t uid, gid_t gid,
-                                       struct sss_krb5_ccache **ccache)
-{
-    struct sss_krb5_ccache *cc;
-    krb5_error_code kerr;
-    errno_t ret;
-
-    cc = talloc_zero(mem_ctx, struct sss_krb5_ccache);
-    if (!cc) {
-        return ENOMEM;
-    }
-    talloc_set_destructor((TALLOC_CTX *)cc, sss_free_krb5_ccache);
-
-    ret = switch_creds(cc, uid, gid, 0, NULL, &cc->creds);
-    if (ret) {
-        goto done;
-    }
-
-    kerr = krb5_init_context(&cc->context);
-    if (kerr) {
-        ret = EIO;
-        goto done;
-    }
-
-    kerr = krb5_cc_resolve(cc->context, ccname, &cc->ccache);
-    if (kerr == KRB5_FCC_NOFILE || cc->ccache == NULL) {
-        DEBUG(SSSDBG_TRACE_FUNC, "ccache %s is missing or empty\n", ccname);
-        ret = ERR_NOT_FOUND;
-        goto done;
-    } else if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_resolve failed.\n");
-        ret = ERR_INTERNAL;
-        goto done;
-    }
-
-    ret = EOK;
-
-done:
-    if (ret) {
-        talloc_free(cc);
-    } else {
-        *ccache = cc;
-    }
-    return ret;
-}
-
-static errno_t sss_destroy_ccache(struct sss_krb5_ccache *cc)
-{
-    krb5_error_code kerr;
-    errno_t ret;
-
-    kerr = krb5_cc_destroy(cc->context, cc->ccache);
-    if (kerr) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_destroy failed.\n");
-        ret = EIO;
-    } else {
-        ret = EOK;
-    }
-
-    /* krb5_cc_destroy frees cc->ccache in all events */
-    cc->ccache = NULL;
-
-    return ret;
-}
-
-errno_t sss_krb5_cc_destroy(const char *ccname, uid_t uid, gid_t gid)
-{
-    struct sss_krb5_ccache *cc = NULL;
-    TALLOC_CTX *tmp_ctx;
-    errno_t ret;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    ret = sss_open_ccache_as_user(tmp_ctx, ccname, uid, gid, &cc);
-    if (ret) {
-        goto done;
-    }
-
-    ret = sss_destroy_ccache(cc);
-
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-
-/* This function is called only as a way to validate that we have the
- * right cache */
-errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
-                                    const char *ccname, const char *principal)
-{
-    struct sss_krb5_ccache *cc = NULL;
-    krb5_principal ccprinc = NULL;
-    krb5_principal kprinc = NULL;
-    krb5_error_code kerr;
-    const char *cc_type;
-    TALLOC_CTX *tmp_ctx;
-    errno_t ret;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    ret = sss_open_ccache_as_user(tmp_ctx, ccname, uid, gid, &cc);
-    if (ret) {
-        goto done;
-    }
-
-    cc_type = krb5_cc_get_type(cc->context, cc->ccache);
-
-    DEBUG(SSSDBG_TRACE_INTERNAL,
-          "Searching for [%s] in cache of type [%s]\n", principal, cc_type);
-
-    kerr = krb5_parse_name(cc->context, principal, &kprinc);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
-        ret = ERR_INTERNAL;
-        goto done;
-    }
-
-    kerr = krb5_cc_get_principal(cc->context, cc->ccache, &ccprinc);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_get_principal failed.\n");
-    }
-
-    if (ccprinc) {
-        if (krb5_principal_compare(cc->context, kprinc, ccprinc) == TRUE) {
-            /* found in the primary ccache */
-            ret = EOK;
-            goto done;
-        }
-    }
-
-#ifdef HAVE_KRB5_CC_COLLECTION
-
-    if (krb5_cc_support_switch(cc->context, cc_type)) {
-
-        krb5_cc_close(cc->context, cc->ccache);
-        cc->ccache = NULL;
-
-        kerr = krb5_cc_set_default_name(cc->context, ccname);
-        if (kerr != 0) {
-            KRB5_DEBUG(SSSDBG_MINOR_FAILURE, cc->context, kerr);
-            /* try to continue despite failure */
-        }
-
-        kerr = krb5_cc_cache_match(cc->context, kprinc, &cc->ccache);
-        if (kerr == 0) {
-            ret = EOK;
-            goto done;
-        }
-        KRB5_DEBUG(SSSDBG_TRACE_INTERNAL, cc->context, kerr);
-    }
-
-#endif /* HAVE_KRB5_CC_COLLECTION */
-
-    ret = ERR_NOT_FOUND;
-
-done:
-    if (cc) {
-        krb5_free_principal(cc->context, ccprinc);
-        krb5_free_principal(cc->context, kprinc);
-    }
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-static errno_t sss_low_level_path_check(const char *ccname)
-{
-    const char *filename;
-    struct stat buf;
-    int ret;
-
-    if (ccname[0] == '/') {
-        filename = ccname;
-    } else if (strncmp(ccname, "FILE:", 5) == 0) {
-        filename = ccname + 5;
-    } else if (strncmp(ccname, "DIR:", 4) == 0) {
-        filename = ccname + 4;
-        if (filename[0] == ':') filename += 1;
-    } else {
-        /* only FILE and DIR types need file checks so far, we ignore any
-         * other type */
-        return EOK;
-    }
-
-    ret = stat(filename, &buf);
-    if (ret == -1) return errno;
-    return EOK;
-}
-
-errno_t sss_krb5_cc_verify_ccache(const char *ccname, uid_t uid, gid_t gid,
-                                  const char *realm, const char *principal)
-{
-    struct sss_krb5_ccache *cc = NULL;
-    TALLOC_CTX *tmp_ctx = NULL;
-    krb5_principal tgt_princ = NULL;
-    krb5_principal princ = NULL;
-    char *tgt_name;
-    krb5_creds mcred = { 0 };
-    krb5_creds cred = { 0 };
-    krb5_error_code kerr;
-    errno_t ret;
-
-    /* first of all verify if the old ccache file/dir exists as we may be
-     * trying to verify if an old ccache exists at all. If no file/dir
-     * exists bail out immediately otherwise a following krb5_cc_resolve()
-     * call may actually create paths and files we do not want to have
-     * around */
-    ret = sss_low_level_path_check(ccname);
-    if (ret) {
-        return ret;
-    }
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    ret = sss_open_ccache_as_user(tmp_ctx, ccname, uid, gid, &cc);
-    if (ret) {
-        goto done;
-    }
-
-    tgt_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
-    if (!tgt_name) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
-        ret = ENOMEM;
-        goto done;
-    }
-
-    kerr = krb5_parse_name(cc->context, tgt_name, &tgt_princ);
-    if (kerr) {
-        KRB5_DEBUG(SSSDBG_CRIT_FAILURE, cc->context, kerr);
-        if (kerr == KRB5_PARSE_MALFORMED) ret = EINVAL;
-        else ret = ERR_INTERNAL;
-        goto done;
-    }
-
-    kerr = krb5_parse_name(cc->context, principal, &princ);
-    if (kerr) {
-        KRB5_DEBUG(SSSDBG_CRIT_FAILURE, cc->context, kerr);
-        if (kerr == KRB5_PARSE_MALFORMED) ret = EINVAL;
-        else ret = ERR_INTERNAL;
-        goto done;
-    }
-
-    mcred.client = princ;
-    mcred.server = tgt_princ;
-    mcred.times.endtime = time(NULL);
-
-    kerr = krb5_cc_retrieve_cred(cc->context, cc->ccache,
-                                 KRB5_TC_MATCH_TIMES, &mcred, &cred);
-    if (kerr) {
-        if (kerr == KRB5_CC_NOTFOUND || kerr == KRB5_FCC_NOFILE) {
-            DEBUG(SSSDBG_TRACE_INTERNAL, "TGT not found or expired.\n");
-            ret = EINVAL;
-        } else {
-            KRB5_DEBUG(SSSDBG_CRIT_FAILURE, cc->context, kerr);
-            ret = ERR_INTERNAL;
-        }
-    }
-    krb5_free_cred_contents(cc->context, &cred);
-
-done:
-    if (tgt_princ) krb5_free_principal(cc->context, tgt_princ);
-    if (princ) krb5_free_principal(cc->context, princ);
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-
 errno_t get_domain_or_subdomain(struct be_ctx *be_ctx,
                                 char *domain_name,
                                 struct sss_domain_info **dom)
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index f54a07f7936a361c21ca933026ee753a89fe5808..ce5ce1ebcf6db14579191840600e684d41a2fdbe 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -42,25 +42,10 @@ errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
                                          const char *user,
                                          const char *upn);
 
-errno_t create_ccache_dir(const char *dirname, pcre *illegal_re,
-                          uid_t uid, gid_t gid);
-
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                              const char *template, bool file_mode,
                              bool case_sensitive);
 
-errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
-                                  uid_t uid, gid_t gid);
-errno_t sss_krb5_cc_destroy(const char *ccname, uid_t uid, gid_t gid);
-errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
-                                    const char *ccname, const char *principal);
-errno_t sss_krb5_cc_verify_ccache(const char *ccname, uid_t uid, gid_t gid,
-                                  const char *realm, const char *principal);
-
-errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
-                             struct tgt_times *tgtt);
-
-
 errno_t get_domain_or_subdomain(struct be_ctx *be_ctx,
                                 char *domain_name,
                                 struct sss_domain_info **dom);
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index 63caa5f6c97f3e5c0df459b7549e681e51fcc108..09f23d5386e3c70efc5ce54fa199c1a6e8656eec 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -37,6 +37,7 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_common.h"
 #include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_ccache.h"
 
 extern struct dp_option default_krb5_opts[];
 
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 18d2bd230ab1170da2caf685cead46c1390c0851..52d8a18576b23c627c7ef3358bd34f4b2dbae6f7 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -27,6 +27,7 @@
 #include <check.h>
 
 #include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_ccache.h"
 #include "providers/krb5/krb5_auth.h"
 #include "tests/common.h"
 
-- 
1.9.3

-------------- next part --------------
>From 1df9876f3a7183c4fae484104bd005a94d60fa70 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sat, 18 Oct 2014 22:03:01 +0200
Subject: [PATCH 4/6] KRB5: Move checking for illegal RE to krb5_utils.c

Otherwise we would have to link krb5_child with pcre and transfer the
regex, which would be cumbersome. Check for illegal patterns when
expanding the template instead.
---
 src/providers/krb5/krb5_auth.c   |  5 +--
 src/providers/krb5/krb5_ccache.c | 38 ++------------------
 src/providers/krb5/krb5_ccache.h |  7 +---
 src/providers/krb5/krb5_utils.c  | 36 +++++++++++++++++--
 src/providers/krb5/krb5_utils.h  |  4 +--
 src/tests/krb5_child-test.c      |  2 +-
 src/tests/krb5_utils-tests.c     | 78 ++++++++++++++++------------------------
 7 files changed, 73 insertions(+), 97 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index bd8b51f47462f1eaef8da61b42caedda3475a4e7..5ed561601ac80e53ee795b458c5bf0ca410951bc 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -302,7 +302,9 @@ static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr,
             DEBUG(SSSDBG_TRACE_ALL, "Recreating  ccache file.\n");
             ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts,
                                                  KRB5_CCNAME_TMPL);
-            kr->ccname = expand_ccname_template(kr, kr, ccname_template, true,
+            kr->ccname = expand_ccname_template(kr, kr, ccname_template,
+                                                kr->krb5_ctx->illegal_path_re,
+                                                true,
                                                 be_ctx->domain->case_sensitive);
             if (kr->ccname == NULL) {
                 DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n");
@@ -310,7 +312,6 @@ static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr,
             }
 
             ret = sss_krb5_precreate_ccache(kr->ccname,
-                                            kr->krb5_ctx->illegal_path_re,
                                             kr->uid, kr->gid);
             if (ret != EOK) {
                 DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n");
diff --git a/src/providers/krb5/krb5_ccache.c b/src/providers/krb5/krb5_ccache.c
index 5586963338616519f36e5d75e796a597d3ac2f22..c0f5b7b8ced3fd2d6d8cbbf4e3339caba60888ff 100644
--- a/src/providers/krb5/krb5_ccache.c
+++ b/src/providers/krb5/krb5_ccache.c
@@ -33,28 +33,6 @@
 #include "util/sss_krb5.h"
 #include "util/util.h"
 
-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;
-}
-
 struct string_list {
     struct string_list *next;
     struct string_list *prev;
@@ -162,9 +140,7 @@ static errno_t check_parent_stat(struct stat *parent_stat, uid_t uid)
     return EOK;
 }
 
-errno_t create_ccache_dir(const char *ccdirname,
-                          pcre *illegal_re,
-                          uid_t uid, gid_t gid)
+static errno_t create_ccache_dir(const char *ccdirname, uid_t uid, gid_t gid)
 {
     int ret = EFAULT;
     struct stat parent_stat;
@@ -188,13 +164,6 @@ errno_t create_ccache_dir(const char *ccdirname,
         goto done;
     }
 
-    if (illegal_re != NULL) {
-        ret = check_ccache_re(ccdirname, illegal_re);
-        if (ret != EOK) {
-            goto done;
-        }
-    }
-
     ret = find_ccdir_parent_data(tmp_ctx, ccdirname, &parent_stat,
                                  &missing_parents);
     if (ret != EOK) {
@@ -242,8 +211,7 @@ done:
     return ret;
 }
 
-errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
-                                  uid_t uid, gid_t gid)
+errno_t sss_krb5_precreate_ccache(const char *ccname, uid_t uid, gid_t gid)
 {
     TALLOC_CTX *tmp_ctx = NULL;
     const char *filename;
@@ -287,7 +255,7 @@ errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
         *end = '\0';
     } while (*(end+1) == '\0');
 
-    ret = create_ccache_dir(ccdirname, illegal_re, uid, gid);
+    ret = create_ccache_dir(ccdirname, uid, gid);
 done:
     talloc_free(tmp_ctx);
     return ret;
diff --git a/src/providers/krb5/krb5_ccache.h b/src/providers/krb5/krb5_ccache.h
index 9f0b3ac84b7af118c315ca00a7c52f200534d97e..e39f96cad6f46c4003103dce4eadf007bc0f8920 100644
--- a/src/providers/krb5/krb5_ccache.h
+++ b/src/providers/krb5/krb5_ccache.h
@@ -35,12 +35,7 @@ struct tgt_times {
     time_t renew_till;
 };
 
-errno_t create_ccache_dir(const char *ccdirname,
-                          pcre *illegal_re,
-                          uid_t uid, gid_t gid);
-
-errno_t sss_krb5_precreate_ccache(const char *ccname, pcre *illegal_re,
-                                  uid_t uid, gid_t gid);
+errno_t sss_krb5_precreate_ccache(const char *ccname, uid_t uid, gid_t gid);
 
 errno_t sss_krb5_cc_destroy(const char *ccname, uid_t uid, gid_t gid);
 
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index ae72b04be236cfce9b6f794c602887491ba487a9..de2d94503744b80b0a3365efb227cd05434579ff 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -202,9 +202,31 @@ done:
 #define S_EXP_USERNAME "{username}"
 #define L_EXP_USERNAME (sizeof(S_EXP_USERNAME) - 1)
 
+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;
+}
+
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template, bool file_mode,
-                             bool case_sensitive)
+                             const char *template, pcre *illegal_re,
+                             bool file_mode, bool case_sensitive)
 {
     char *copy;
     char *p;
@@ -217,6 +239,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
     TALLOC_CTX *tmp_ctx = NULL;
     char action;
     bool rerun;
+    int ret;
 
     if (template == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Missing template.\n");
@@ -320,7 +343,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                     }
 
                     dummy = expand_ccname_template(tmp_ctx, kr, cache_dir_tmpl,
-                                                   false, case_sensitive);
+                                                   illegal_re, false, case_sensitive);
                     if (dummy == NULL) {
                         DEBUG(SSSDBG_CRIT_FAILURE,
                               "Expanding credential cache directory "
@@ -411,6 +434,13 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
         goto done;
     }
 
+    if (illegal_re != NULL) {
+        ret = check_ccache_re(result, illegal_re);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
     res = talloc_move(mem_ctx, &result);
 done:
     talloc_zfree(tmp_ctx);
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index ce5ce1ebcf6db14579191840600e684d41a2fdbe..0155905b5bc7469d09aecbd51cae0e8cc61b3952 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -43,8 +43,8 @@ errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
                                          const char *upn);
 
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
-                             const char *template, bool file_mode,
-                             bool case_sensitive);
+                             const char *template, pcre *illegal_re,
+                             bool file_mode, bool case_sensitive);
 
 errno_t get_domain_or_subdomain(struct be_ctx *be_ctx,
                                 char *domain_name,
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index 09f23d5386e3c70efc5ce54fa199c1a6e8656eec..8826a28ed5ea064317c62682003dc0e9a6df01b6 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -239,6 +239,7 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
         kr->ccname = expand_ccname_template(kr, kr,
                                         dp_opt_get_cstring(kr->krb5_ctx->opts,
                                                            KRB5_CCNAME_TMPL),
+                                            kr->krb5_ctx->illegal_path_re,
                                             true, true);
         if (!kr->ccname) goto fail;
 
@@ -254,7 +255,6 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
             kr->ccname, kr->uid, kr->gid);
 
     ret = sss_krb5_precreate_ccache(kr->ccname,
-                                    kr->krb5_ctx->illegal_path_re,
                                     kr->uid, kr->gid);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "create_ccache_dir failed.\n");
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 52d8a18576b23c627c7ef3358bd34f4b2dbae6f7..409c0f01d2cce9c24a648306007b9fa7f5bc8372 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -131,13 +131,13 @@ START_TEST(test_private_ccache_dir_in_user_dir)
 
     ret = chmod(user_dir, 0600);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = sss_krb5_precreate_ccache(filename, NULL, uid, gid);
+    ret = sss_krb5_precreate_ccache(filename, uid, gid);
     fail_unless(ret == EINVAL, "sss_krb5_precreate_ccache does not return EINVAL "
                                "while x-bit is missing.");
 
     ret = chmod(user_dir, 0700);
     fail_unless(ret == EOK, "chmod failed.");
-    ret = sss_krb5_precreate_ccache(filename, NULL, uid, gid);
+    ret = sss_krb5_precreate_ccache(filename, uid, gid);
     fail_unless(ret == EOK, "sss_krb5_precreate_ccache failed.");
 
     check_dir(dn3, uid, gid, 0700);
@@ -175,7 +175,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 = sss_krb5_precreate_ccache(filename, NULL, 12345, 12345);
+    ret = sss_krb5_precreate_ccache(filename, 12345, 12345);
     fail_unless(ret == EINVAL, "Creating private ccache dir in wrong user "
                                "dir does not failed with EINVAL.");
 
@@ -185,16 +185,14 @@ END_TEST
 
 START_TEST(test_illegal_patterns)
 {
-    int ret;
     char *cwd;
     char *dirname;
     char *filename;
-    uid_t uid = getuid();
-    gid_t gid = getgid();
     pcre *illegal_re;
     const char *errstr;
     int errval;
     int errpos;
+    char *result = NULL;
 
     illegal_re = pcre_compile2(ILLEGAL_PATH_PATTERN, 0,
                                &errval, &errstr, &errpos, NULL);
@@ -209,33 +207,28 @@ START_TEST(test_illegal_patterns)
     free(cwd);
     fail_unless(dirname != NULL, "talloc_asprintf failed.");
 
-
-    filename = talloc_asprintf(tmp_ctx, "abc/./ccfile");
-    fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(filename, illegal_re, uid, gid);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed relative path [%s].",
-                               filename);
+    result = expand_ccname_template(tmp_ctx, kr, "abc/./ccfile", illegal_re, true, true);
+    fail_unless(result == NULL, "expand_ccname_template allowed relative path\n");
 
     filename = talloc_asprintf(tmp_ctx, "%s/abc/./ccfile", dirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(filename, illegal_re, uid, gid);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
-                               "illegal pattern '/./' in filename [%s].",
-                               filename);
+    result = expand_ccname_template(tmp_ctx, kr, filename, illegal_re, true, true);
+    fail_unless(result == NULL, "expand_ccname_template allowed "
+                                "illegal pattern '/./'\n");
 
     filename = talloc_asprintf(tmp_ctx, "%s/abc/../ccfile", dirname);
     fail_unless(filename != NULL, "talloc_asprintf failed.");
-    ret = create_ccache_dir(filename, illegal_re, uid, gid);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
-                               "illegal pattern '/../' in filename [%s].",
-                               filename);
+    result = expand_ccname_template(tmp_ctx, kr, filename, illegal_re, true, true);
+    fail_unless(result == NULL, "expand_ccname_template 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(filename, illegal_re, uid, gid);
-    fail_unless(ret == EINVAL, "create_ccache_dir allowed "
-                               "illegal pattern '//' in filename [%s].",
-                               filename);
+    result = expand_ccname_template(tmp_ctx, kr, filename, illegal_re, true, true);
+    fail_unless(result == NULL, "expand_ccname_template allowed "
+                                "illegal pattern '//' in filename [%s].",
+                                filename);
 
     pcre_free(illegal_re);
 }
@@ -248,17 +241,7 @@ START_TEST(test_cc_dir_create)
     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.");
@@ -269,7 +252,7 @@ START_TEST(test_cc_dir_create)
     residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s", dirname, "ccdir");
     fail_unless(residual != NULL, "talloc_asprintf failed.");
 
-    ret = sss_krb5_precreate_ccache(residual, illegal_re, uid, gid);
+    ret = sss_krb5_precreate_ccache(residual, uid, gid);
     fail_unless(ret == EOK, "sss_krb5_precreate_ccache failed\n");
     ret = rmdir(dirname);
     if (ret < 0) ret = errno;
@@ -282,14 +265,13 @@ START_TEST(test_cc_dir_create)
     residual = talloc_asprintf(tmp_ctx, "DIR:%s/%s", dirname, "ccdir/");
     fail_unless(residual != NULL, "talloc_asprintf failed.");
 
-    ret = sss_krb5_precreate_ccache(residual, illegal_re, uid, gid);
+    ret = sss_krb5_precreate_ccache(residual, uid, gid);
     fail_unless(ret == EOK, "sss_krb5_precreate_ccache failed\n");
     ret = rmdir(dirname);
     if (ret < 0) ret = errno;
     fail_unless(ret == 0, "Cannot remove %s: %s\n", dirname, strerror(ret));
     talloc_free(residual);
     free(cwd);
-    pcre_free(illegal_re);
 }
 END_TEST
 
@@ -356,7 +338,7 @@ static void do_test(const char *file_template, const char *dir_template,
     ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, dir_template);
     fail_unless(ret == EOK, "Failed to set Ccache dir");
 
-    result = expand_ccname_template(tmp_ctx, kr, file_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, NULL, true, true);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", file_template);
     fail_unless(strcmp(result, expected) == 0,
@@ -391,14 +373,14 @@ START_TEST(test_case_sensitive)
     ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, CCACHE_DIR);
     fail_unless(ret == EOK, "Failed to set Ccache dir");
 
-    result = expand_ccname_template(tmp_ctx, kr, file_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, NULL, true, true);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", file_template);
     fail_unless(strcmp(result, expected_cs) == 0,
                 "Expansion failed, result [%s], expected [%s].",
                 result, expected_cs);
 
-    result = expand_ccname_template(tmp_ctx, kr, file_template, true, false);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, NULL, true, false);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", file_template);
     fail_unless(strcmp(result, expected_ci) == 0,
@@ -445,7 +427,7 @@ START_TEST(test_ccache_dir)
     ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, BASE"_%d");
     fail_unless(ret == EOK, "Failed to set Ccache dir");
 
-    result = expand_ccname_template(tmp_ctx, kr, "%d/"FILENAME, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, "%d/"FILENAME, NULL, true, true);
 
     fail_unless(result == NULL, "Using %%d in ccache dir should fail.");
 }
@@ -461,7 +443,7 @@ START_TEST(test_pid)
     ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, BASE"_%P");
     fail_unless(ret == EOK, "Failed to set Ccache dir");
 
-    result = expand_ccname_template(tmp_ctx, kr, "%d/"FILENAME, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, "%d/"FILENAME, NULL, true, true);
 
     fail_unless(result == NULL, "Using %%P in ccache dir should fail.");
 }
@@ -480,7 +462,7 @@ START_TEST(test_unknown_template)
     char *result;
     int ret;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, NULL, true, true);
 
     fail_unless(result == NULL, "Unknown template [%s] should fail.",
                 test_template);
@@ -488,7 +470,7 @@ START_TEST(test_unknown_template)
     ret = dp_opt_set_string(kr->krb5_ctx->opts, KRB5_CCACHEDIR, BASE"_%X");
     fail_unless(ret == EOK, "Failed to set Ccache dir");
     test_template = "%d/"FILENAME;
-    result = expand_ccname_template(tmp_ctx, kr, test_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, NULL, true, true);
 
     fail_unless(result == NULL, "Unknown template [%s] should fail.",
                 test_template);
@@ -500,7 +482,7 @@ START_TEST(test_NULL)
     char *test_template = NULL;
     char *result;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, NULL, true, true);
 
     fail_unless(result == NULL, "Expected NULL as a result for an empty input.",
                 test_template);
@@ -512,7 +494,7 @@ START_TEST(test_no_substitution)
     const char *test_template = BASE;
     char *result;
 
-    result = expand_ccname_template(tmp_ctx, kr, test_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, test_template, NULL, true, true);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", test_template);
     fail_unless(strcmp(result, test_template) == 0,
@@ -529,7 +511,7 @@ START_TEST(test_krb5_style_expansion)
 
     file_template = BASE"/%{uid}/%{USERID}/%{euid}/%{username}";
     expected = BASE"/"UID"/"UID"/"UID"/"USERNAME;
-    result = expand_ccname_template(tmp_ctx, kr, file_template, true, true);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, NULL, true, true);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", file_template);
     fail_unless(strcmp(result, expected) == 0,
@@ -538,7 +520,7 @@ START_TEST(test_krb5_style_expansion)
 
     file_template = BASE"/%{unknown}";
     expected = BASE"/%{unknown}";
-    result = expand_ccname_template(tmp_ctx, kr, file_template, true, false);
+    result = expand_ccname_template(tmp_ctx, kr, file_template, NULL, true, true);
 
     fail_unless(result != NULL, "Cannot expand template [%s].", file_template);
     fail_unless(strcmp(result, expected) == 0,
-- 
1.9.3

-------------- next part --------------
>From 643aa5ebe89ab65b80dfebc2c0114161a99ea4f1 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sat, 18 Oct 2014 22:03:13 +0200
Subject: [PATCH 5/6] KRB5: Move all ccache operations to krb5_child.c

The credential cache operations must be now performed by the krb5_child
completely, because the sssd_be process might be running as the sssd
user who doesn't have access to the ccaches.

src/providers/krb5/krb5_ccache.c is still linked against libsss_krb5
until we fix Kerberos ticket renewal as non-root.

Also includes a new error code that indicates that the back end should
remove the old ccache attribute -- the child can't do that if it's
running as the user.
---
 Makefile.am                             |  13 +-
 src/providers/krb5/krb5_auth.c          | 223 ++++----------------------------
 src/providers/krb5/krb5_ccache.c        |  62 ++++-----
 src/providers/krb5/krb5_ccache.h        |   5 +-
 src/providers/krb5/krb5_child.c         | 208 +++++++++++++++++++++++++++--
 src/providers/krb5/krb5_child_handler.c |  13 ++
 src/tests/krb5_child-test.c             |   3 +-
 src/util/util_errors.c                  |   1 +
 src/util/util_errors.h                  |   1 +
 9 files changed, 281 insertions(+), 248 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4a69ecb0cfe48e20bde958c9351c2a5ece5ffffa..5325d51e7240ae39a546e68b2a2aea202b3dfdfa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2496,27 +2496,36 @@ libsss_ad_la_LDFLAGS = \
 
 krb5_child_SOURCES = \
     src/providers/krb5/krb5_child.c \
+    src/providers/krb5/krb5_ccache.c \
     src/providers/dp_pam_data_util.c \
     src/util/user_info_msg.c \
     src/util/sss_krb5.c \
+    src/util/find_uid.c \
     src/util/atomic_io.c \
     src/util/authtok.c \
     src/util/util.c \
     src/util/signal.c \
+    src/util/strtonum.c \
     src/util/become_user.c \
     src/sss_client/common.c \
     $(NULL)
 krb5_child_CFLAGS = \
     $(AM_CFLAGS) \
     $(POPT_CFLAGS) \
-    $(KRB5_CFLAGS)
+    $(KRB5_CFLAGS) \
+    $(PCRE_CFLAGS) \
+    $(SYSTEMD_LOGIN_CFLAGS) \
+    $(NULL)
 krb5_child_LDADD = \
     libsss_debug.la \
     $(TALLOC_LIBS) \
     $(POPT_LIBS) \
     $(DHASH_LIBS) \
     $(KRB5_LIBS) \
-    $(CLIENT_LIBS)
+    $(CLIENT_LIBS) \
+    $(PCRE_LIBS) \
+    $(SYSTEMD_LOGIN_LIBS) \
+    $(NULL)
 
 ldap_child_SOURCES = \
     src/providers/ldap/ldap_child.c \
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 5ed561601ac80e53ee795b458c5bf0ca410951bc..e791aee1c2d83f84ba617db1d5d93948c0e4e2a1 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -41,45 +41,6 @@
 #include "providers/krb5/krb5_utils.h"
 #include "providers/krb5/krb5_ccache.h"
 
-static errno_t
-check_old_ccache(const char *old_ccache, struct krb5child_req *kr,
-                 const char *realm, bool *active, bool *valid)
-{
-    errno_t ret;
-
-    *active = false;
-    *valid = false;
-
-    ret = sss_krb5_cc_verify_ccache(old_ccache,
-                                    kr->uid, kr->gid,
-                                    realm, kr->upn);
-    switch (ret) {
-    case ERR_NOT_FOUND:
-    case ENOENT:
-        DEBUG(SSSDBG_TRACE_FUNC,
-              "Saved ccache %s doesn't exist.\n", old_ccache);
-        return ENOENT;
-    case EINVAL:
-        /* cache found but no tgt or expired */
-    case EOK:
-        *valid = true;
-        break;
-    default:
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Cannot check if saved ccache %s is valid\n",
-               old_ccache);
-        return ret;
-    }
-
-    ret = check_if_uid_is_active(kr->uid, active);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "check_if_uid_is_active failed.\n");
-        return ret;
-    }
-
-    return EOK;
-}
-
 static int krb5_mod_ccname(TALLOC_CTX *mem_ctx,
                            struct sysdb_ctx *sysdb,
                            struct sss_domain_info *domain,
@@ -225,7 +186,6 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
         return ENOMEM;
     }
     kr->is_offline = false;
-    kr->active_ccache = true;
     kr->run_as_user = true;
     talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
 
@@ -276,47 +236,26 @@ static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx,
 }
 
 static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr,
+                                             struct ldb_message *user_msg,
                                              struct be_ctx *be_ctx)
 {
     const char *ccname_template;
-    errno_t ret;
 
-    if (!kr->is_offline) {
-        kr->is_offline = be_is_offline(be_ctx);
+    ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL);
+
+    kr->ccname = expand_ccname_template(kr, kr, ccname_template,
+                                        kr->krb5_ctx->illegal_path_re, true,
+                                        be_ctx->domain->case_sensitive);
+    if (kr->ccname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n");
+        return ENOMEM;
     }
 
-    /* The ccache file should be (re)created if one of the following conditions
-     * is true:
-     * - it doesn't exist (kr->ccname == NULL)
-     * - the backend is online and the current ccache file is not used, i.e
-     *   the related user is currently not logged in and it is not a renewal
-     *   request
-     *   (!kr->is_offline && !kr->active_ccache && kr->pd->cmd != SSS_CMD_RENEW)
-     * - the backend is offline and the current cache file not used and
-     *   it does not contain a valid tgt
-     *   (kr->is_offline && !kr->active_ccache && !kr->valid_tgt)
-     */
-    if (kr->ccname == NULL ||
-        (kr->is_offline && !kr->active_ccache && !kr->valid_tgt) ||
-        (!kr->is_offline && !kr->active_ccache && kr->pd->cmd != SSS_CMD_RENEW)) {
-            DEBUG(SSSDBG_TRACE_ALL, "Recreating  ccache file.\n");
-            ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts,
-                                                 KRB5_CCNAME_TMPL);
-            kr->ccname = expand_ccname_template(kr, kr, ccname_template,
-                                                kr->krb5_ctx->illegal_path_re,
-                                                true,
-                                                be_ctx->domain->case_sensitive);
-            if (kr->ccname == NULL) {
-                DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n");
-                return ENOMEM;
-            }
-
-            ret = sss_krb5_precreate_ccache(kr->ccname,
-                                            kr->uid, kr->gid);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n");
-                return ret;
-            }
+    kr->old_ccname = ldb_msg_find_attr_as_string(user_msg,
+                                                 SYSDB_CCACHE_FILE, NULL);
+    if (kr->old_ccname == NULL) {
+        DEBUG(SSSDBG_TRACE_LIBS,
+                "No ccache file for user [%s] found.\n", kr->pd->user);
     }
 
     return EOK;
@@ -402,7 +341,6 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
     struct krb5_auth_state *state;
     struct ldb_result *res;
     struct krb5child_req *kr = NULL;
-    const char *ccache_file = NULL;
     const char *realm;
     struct tevent_req *req;
     struct tevent_req *subreq;
@@ -588,45 +526,10 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        ccache_file = ldb_msg_find_attr_as_string(res->msgs[0],
-                                                  SYSDB_CCACHE_FILE,
-                                                  NULL);
-        if (ccache_file != NULL) {
-            ret = check_old_ccache(ccache_file, kr, realm,
-                                   &kr->active_ccache,
-                                   &kr->valid_tgt);
-            if (ret == ENOENT) {
-                DEBUG(SSSDBG_FUNC_DATA,
-                      "Ignoring ccache attribute [%s], because it doesn't"
-                       "exist.\n", ccache_file);
-                ccache_file = NULL;
-            } else if (ret != EOK) {
-                DEBUG(SSSDBG_CRIT_FAILURE,
-                      "check_if_ccache_file_is_used failed.\n");
-                ccache_file = NULL;
-            }
-        } else {
-            kr->active_ccache = false;
-            kr->valid_tgt = false;
-            DEBUG(SSSDBG_CONF_SETTINGS,
-                  "No ccache file for user [%s] found.\n", pd->user);
-        }
-        DEBUG(SSSDBG_TRACE_ALL,
-              "Ccache_file is [%s] and is %s active and TGT is %s valid.\n",
-                  ccache_file ? ccache_file : "not set",
-                  kr->active_ccache ? "" : "not",
-                  kr->valid_tgt ? "" : "not");
-        if (ccache_file != NULL) {
-            kr->ccname = ccache_file;
-            kr->old_ccname = talloc_strdup(kr, ccache_file);
-            if (kr->old_ccname == NULL) {
-                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
-                ret = ENOMEM;
-                goto done;
-            }
-        } else {
-            kr->ccname = NULL;
-            kr->old_ccname = NULL;
+        ret = krb5_auth_prepare_ccache_name(kr, res->msgs[0], state->be_ctx);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot prepare ccache names!\n");
+            goto done;
         }
         break;
 
@@ -669,7 +572,6 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq)
     struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
     struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
     struct krb5child_req *kr = state->kr;
-    char *msg;
     int ret;
 
     if (!state->search_kpasswd) {
@@ -728,45 +630,8 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq)
         }
     }
 
-    ret = krb5_auth_prepare_ccache_name(kr, state->be_ctx);
-    if (ret) {
-        goto done;
-    }
-
-    if (kr->is_offline) {
-        DEBUG(SSSDBG_TRACE_ALL, "Preparing for offline operation.\n");
-
-        if (kr->valid_tgt || kr->active_ccache) {
-            DEBUG(SSSDBG_TRACE_ALL, "Valid TGT available or "
-                      "ccache file is already in use.\n");
-            kr->ccname = kr->old_ccname;
-            msg = talloc_asprintf(kr->pd,
-                                  "%s=%s", CCACHE_ENV_NAME, kr->ccname);
-            if (msg == NULL) {
-                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
-            } else {
-                ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM,
-                                       strlen(msg) + 1, (uint8_t *) msg);
-                if (ret != EOK) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
-                }
-            }
-
-            if (dp_opt_get_bool(kr->krb5_ctx->opts,
-                                KRB5_STORE_PASSWORD_IF_OFFLINE)) {
-                krb5_auth_cache_creds(state->kr->krb5_ctx,
-                                      state->domain,
-                                      state->be_ctx->cdb,
-                                      kr->pd, kr->uid,
-                                      &state->pam_status, &state->dp_err);
-            } else {
-                state->pam_status = PAM_AUTHINFO_UNAVAIL;
-                state->dp_err = DP_ERR_OFFLINE;
-            }
-            ret = EOK;
-            goto done;
-
-        }
+    if (!kr->is_offline) {
+        kr->is_offline = be_is_offline(state->be_ctx);
     }
 
     /* We need to keep the root privileges to read the keytab file if
@@ -814,7 +679,6 @@ static void krb5_auth_done(struct tevent_req *subreq)
     char *renew_interval_str;
     time_t renew_interval_time = 0;
     bool use_enterprise_principal;
-    uint32_t user_info_type;
 
     ret = handle_child_recv(subreq, pd, &buf, &len);
     talloc_zfree(subreq);
@@ -974,6 +838,14 @@ static void krb5_auth_done(struct tevent_req *subreq)
         }
         break;
 
+    case ERR_CREDS_EXPIRED_CCACHE:
+        ret = krb5_delete_ccname(state, state->sysdb, state->domain,
+                pd->user, kr->old_ccname);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "krb5_delete_ccname failed.\n");
+        }
+        /* FALLTHROUGH */
+
     case ERR_CREDS_EXPIRED:
         /* 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
@@ -981,14 +853,6 @@ static void krb5_auth_done(struct tevent_req *subreq)
          * used. */
         if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache) {
             if (kr->old_ccname != NULL) {
-                ret = safe_remove_old_ccache_file(kr->old_ccname, NULL,
-                                                  kr->uid, kr->gid);
-                if (ret != EOK) {
-                    DEBUG(SSSDBG_CRIT_FAILURE,
-                          "Failed to remove old ccache file [%s], "
-                              "please remove it manually.\n", kr->old_ccname);
-                }
-
                 ret = krb5_delete_ccname(state, state->sysdb, state->domain,
                                          pd->user, kr->old_ccname);
                 if (ret != EOK) {
@@ -1062,37 +926,6 @@ static void krb5_auth_done(struct tevent_req *subreq)
         goto done;
     }
 
-    ret = sss_krb5_check_ccache_princ(kr->uid, kr->gid, kr->ccname, kr->upn);
-    if (ret) {
-        if (res->otp == true && pd->cmd == SSS_PAM_CHAUTHTOK) {
-            DEBUG(SSSDBG_IMPORTANT_INFO,
-                  "Password change succeeded but currently "
-                  "post-chpass kinit is not implemented\n");
-
-            user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS;
-            ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
-                                   (const uint8_t *) &user_info_type);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
-                /* Not fatal */
-            }
-        } else {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "No ccache for %s in %s?\n", kr->upn, kr->ccname);
-            goto done;
-        }
-    }
-
-    if (kr->old_ccname) {
-        ret = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname,
-                                          kr->uid, kr->gid);
-        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->sysdb, state->domain,
                            pd->user, kr->ccname);
     if (ret) {
diff --git a/src/providers/krb5/krb5_ccache.c b/src/providers/krb5/krb5_ccache.c
index c0f5b7b8ced3fd2d6d8cbbf4e3339caba60888ff..7aa36b744ddcf7e46edcc26405a5101645b8b546 100644
--- a/src/providers/krb5/krb5_ccache.c
+++ b/src/providers/krb5/krb5_ccache.c
@@ -374,49 +374,32 @@ done:
 
 /* This function is called only as a way to validate that we have the
  * right cache */
-errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
-                                    const char *ccname, const char *principal)
+errno_t sss_krb5_check_ccache_princ(krb5_context kctx,
+                                    const char *ccname,
+                                    krb5_principal user_princ)
 {
-    struct sss_krb5_ccache *cc = NULL;
+    krb5_ccache kcc = NULL;
     krb5_principal ccprinc = NULL;
-    krb5_principal kprinc = NULL;
     krb5_error_code kerr;
     const char *cc_type;
-    TALLOC_CTX *tmp_ctx;
     errno_t ret;
 
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    ret = sss_open_ccache_as_user(tmp_ctx, ccname, uid, gid, &cc);
-    if (ret) {
-        goto done;
-    }
-
-    cc_type = krb5_cc_get_type(cc->context, cc->ccache);
-
-    DEBUG(SSSDBG_TRACE_INTERNAL,
-          "Searching for [%s] in cache of type [%s]\n", principal, cc_type);
-
-    kerr = krb5_parse_name(cc->context, principal, &kprinc);
-    if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr);
-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
+    kerr = krb5_cc_resolve(kctx, ccname, &kcc);
+    if (kerr) {
         ret = ERR_INTERNAL;
         goto done;
     }
 
-    kerr = krb5_cc_get_principal(cc->context, cc->ccache, &ccprinc);
+    cc_type = krb5_cc_get_type(kctx, kcc);
+
+    kerr = krb5_cc_get_principal(kctx, kcc, &ccprinc);
     if (kerr != 0) {
-        KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr);
+        KRB5_DEBUG(SSSDBG_OP_FAILURE, kctx, kerr);
         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_get_principal failed.\n");
     }
 
     if (ccprinc) {
-        if (krb5_principal_compare(cc->context, kprinc, ccprinc) == TRUE) {
+        if (krb5_principal_compare(kctx, user_princ, ccprinc) == TRUE) {
             /* found in the primary ccache */
             ret = EOK;
             goto done;
@@ -425,23 +408,23 @@ errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
 
 #ifdef HAVE_KRB5_CC_COLLECTION
 
-    if (krb5_cc_support_switch(cc->context, cc_type)) {
+    if (krb5_cc_support_switch(kctx, cc_type)) {
 
-        krb5_cc_close(cc->context, cc->ccache);
-        cc->ccache = NULL;
+        krb5_cc_close(kctx, kcc);
+        kcc = NULL;
 
-        kerr = krb5_cc_set_default_name(cc->context, ccname);
+        kerr = krb5_cc_set_default_name(kctx, ccname);
         if (kerr != 0) {
-            KRB5_DEBUG(SSSDBG_MINOR_FAILURE, cc->context, kerr);
+            KRB5_DEBUG(SSSDBG_MINOR_FAILURE, kctx, kerr);
             /* try to continue despite failure */
         }
 
-        kerr = krb5_cc_cache_match(cc->context, kprinc, &cc->ccache);
+        kerr = krb5_cc_cache_match(kctx, user_princ, &kcc);
         if (kerr == 0) {
             ret = EOK;
             goto done;
         }
-        KRB5_DEBUG(SSSDBG_TRACE_INTERNAL, cc->context, kerr);
+        KRB5_DEBUG(SSSDBG_TRACE_INTERNAL, kctx, kerr);
     }
 
 #endif /* HAVE_KRB5_CC_COLLECTION */
@@ -449,11 +432,12 @@ errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
     ret = ERR_NOT_FOUND;
 
 done:
-    if (cc) {
-        krb5_free_principal(cc->context, ccprinc);
-        krb5_free_principal(cc->context, kprinc);
+    if (ccprinc) {
+        krb5_free_principal(kctx, ccprinc);
+    }
+    if (kcc) {
+        krb5_cc_close(kctx, kcc);
     }
-    talloc_free(tmp_ctx);
     return ret;
 }
 
diff --git a/src/providers/krb5/krb5_ccache.h b/src/providers/krb5/krb5_ccache.h
index e39f96cad6f46c4003103dce4eadf007bc0f8920..e47df3665e3f325cc56d34767b416662577cc048 100644
--- a/src/providers/krb5/krb5_ccache.h
+++ b/src/providers/krb5/krb5_ccache.h
@@ -39,8 +39,9 @@ errno_t sss_krb5_precreate_ccache(const char *ccname, uid_t uid, gid_t gid);
 
 errno_t sss_krb5_cc_destroy(const char *ccname, uid_t uid, gid_t gid);
 
-errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid,
-                                    const char *ccname, const char *principal);
+errno_t sss_krb5_check_ccache_princ(krb5_context kctx,
+                                    const char *ccname,
+                                    krb5_principal user_princ);
 
 errno_t sss_krb5_cc_verify_ccache(const char *ccname, uid_t uid, gid_t gid,
                                   const char *realm, const char *principal);
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 7fa5f0c344a4afe110afa08f479f283aefce8d23..94cd34e433cf6a197860d233fbf9ca30cd3eb535 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -33,6 +33,7 @@
 #include "util/sss_krb5.h"
 #include "util/user_info_msg.h"
 #include "util/child_common.h"
+#include "util/find_uid.h"
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
@@ -61,6 +62,10 @@ struct krb5_req {
     const char *upn;
     uid_t uid;
     gid_t gid;
+
+    char *old_ccname;
+    bool old_cc_valid;
+    bool old_cc_active;
 };
 
 static krb5_context krb5_error_ctx;
@@ -1021,6 +1026,24 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         goto done;
     }
 
+    /* Successfull authentication! Check if ccache contains the
+     * right principal...
+     */
+    kerr = sss_krb5_check_ccache_princ(kr->ctx, kr->ccname, kr->creds->client);
+    if (kerr) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "No ccache for %s in %s?\n", kr->upn, kr->ccname);
+        goto done;
+    }
+
+    kerr = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname,
+                                       kr->uid, kr->gid);
+    if (kerr != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Failed to remove old ccache file [%s], "
+              "please remove it manually.\n", kr->old_ccname);
+    }
+
     kerr = add_ticket_times_and_upn_to_response(kr);
     if (kerr != 0) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -1094,6 +1117,7 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
     int realm_length;
     size_t msg_len;
     uint8_t *msg;
+    uint32_t user_info_type;
 
     DEBUG(SSSDBG_TRACE_LIBS, "Password change operation\n");
 
@@ -1222,6 +1246,14 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
     krb5_free_cred_contents(kr->ctx, kr->creds);
 
     if (kr->otp == true) {
+        user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS;
+        ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
+                               (const uint8_t *) &user_info_type);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+            /* Not fatal */
+        }
+
         sss_authtok_set_empty(kr->pd->newauthtok);
         return map_krb5_error(kerr);
     }
@@ -1298,6 +1330,21 @@ static errno_t tgt_req_child(struct krb5_req *kr)
     krb5_free_cred_contents(kr->ctx, kr->creds);
     if (kerr == 0) {
         ret = ERR_CREDS_EXPIRED;
+
+        /* 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 (kr->old_cc_active == false && kr->old_ccname) {
+            ret = safe_remove_old_ccache_file(kr->old_ccname, NULL,
+                    kr->uid, kr->gid);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                        "Failed to remove old ccache file [%s], "
+                        "please remove it manually.\n", kr->old_ccname);
+            }
+            ret = ERR_CREDS_EXPIRED_CCACHE;
+        }
     } else {
         ret = map_krb5_error(kerr);
     }
@@ -1423,12 +1470,17 @@ static errno_t create_empty_ccache(struct krb5_req *kr)
     krb5_creds *creds = NULL;
     krb5_error_code kerr;
 
-    DEBUG(SSSDBG_TRACE_LIBS, "Creating empty ccache\n");
-
-    kerr = create_empty_cred(kr->ctx, kr->princ, &creds);
-    if (kerr == 0) {
-        kerr = create_ccache(kr->ccname, creds);
+    if (kr->old_cc_valid == false) {
+        DEBUG(SSSDBG_TRACE_LIBS, "Creating empty ccache\n");
+        kerr = create_empty_cred(kr->ctx, kr->princ, &creds);
+        if (kerr == 0) {
+            kerr = create_ccache(kr->ccname, creds);
+        }
+    } else {
+        DEBUG(SSSDBG_TRACE_LIBS, "Existing ccache still valid, reusing\n");
+        kerr = 0;
     }
+
     if (kerr != 0) {
         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
     } else {
@@ -1529,6 +1581,17 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
 
         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
         if ((p + len ) > size) return EINVAL;
+
+        if (len > 0) {
+            kr->old_ccname = talloc_strndup(pd, (char *)(buf + p), len);
+            if (kr->old_ccname == NULL) return ENOMEM;
+            p += len;
+        } else {
+            DEBUG(SSSDBG_TRACE_INTERNAL, "No old ccache\n");
+        }
+
+        SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
+        if ((p + len ) > size) return EINVAL;
         kr->keytab = talloc_strndup(pd, (char *)(buf + p), len);
         if (kr->keytab == NULL) return ENOMEM;
         p += len;
@@ -1538,10 +1601,14 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
             return ret;
         }
 
-        DEBUG(SSSDBG_CONF_SETTINGS, "ccname: [%s] keytab: [%s]\n",
-              kr->ccname, kr->keytab);
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              "ccname: [%s] old_ccname: [%s] keytab: [%s]\n",
+              kr->ccname,
+              kr->old_ccname ? kr->old_ccname : "not set",
+              kr->keytab);
     } else {
         kr->ccname = NULL;
+        kr->old_ccname = NULL;
         kr->keytab = NULL;
         sss_authtok_set_empty(pd->authtok);
     }
@@ -1870,6 +1937,126 @@ static errno_t check_use_fast(enum k5c_fast_opt *_fast_val)
     return EOK;
 }
 
+static errno_t old_ccache_valid(struct krb5_req *kr, bool *_valid)
+{
+    errno_t ret;
+    bool valid;
+
+    valid = false;
+
+    ret = sss_krb5_cc_verify_ccache(kr->old_ccname,
+                                    kr->uid, kr->gid,
+                                    kr->realm, kr->upn);
+    switch (ret) {
+        case ERR_NOT_FOUND:
+        case ENOENT:
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Saved ccache %s doesn't exist, ignoring\n", kr->old_ccname);
+            break;
+        case EINVAL:
+            /* cache found but no tgt or expired */
+        case EOK:
+            valid = true;
+            break;
+        default:
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Cannot check if saved ccache %s is valid\n",
+                   kr->old_ccname);
+            return ret;
+    }
+
+    *_valid = valid;
+    return EOK;
+}
+
+static int k5c_check_old_ccache(struct krb5_req *kr)
+{
+    errno_t ret;
+
+    if (kr->old_ccname) {
+        ret = old_ccache_valid(kr, &kr->old_cc_valid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "old_ccache_valid failed.\n");
+            return ret;
+        }
+
+        ret = check_if_uid_is_active(kr->uid, &kr->old_cc_active);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "check_if_uid_is_active failed.\n");
+            return ret;
+        }
+
+        DEBUG(SSSDBG_TRACE_ALL,
+                "Ccache_file is [%s] and is %s active and TGT is %s valid.\n",
+                kr->old_ccname ? kr->old_ccname : "not set",
+                kr->old_cc_active ? "" : "not",
+                kr->old_cc_valid ? "" : "not");
+    }
+
+    return EOK;
+}
+
+static int k5c_precreate_ccache(struct krb5_req *kr, uint32_t offline)
+{
+    errno_t ret;
+
+    /* The ccache file should be (re)created if one of the following conditions
+     * is true:
+     * - it doesn't exist (kr->old_ccname == NULL)
+     * - the backend is online and the current ccache file is not used, i.e
+     * the related user is currently not logged in and it is not a renewal
+     * request
+     * (offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)
+     * - the backend is offline and the current cache file not used and
+     * it does not contain a valid tgt
+     * (offline && !kr->old_cc_active && !kr->valid_tgt)
+     */
+    if (kr->old_ccname == NULL ||
+            (offline && !kr->old_cc_active && !kr->old_cc_valid) ||
+            (!offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)) {
+        DEBUG(SSSDBG_TRACE_ALL, "Recreating ccache\n");
+
+        ret = sss_krb5_precreate_ccache(kr->ccname, kr->uid, kr->gid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n");
+            return ret;
+        }
+    } else {
+        /* We can reuse the old ccache */
+        kr->ccname = kr->old_ccname;
+    }
+
+    return EOK;
+}
+
+static int k5c_ccache_setup(struct krb5_req *kr, uint32_t offline)
+{
+    errno_t ret;
+
+    if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
+        return EOK;
+    }
+
+    ret = k5c_check_old_ccache(kr);
+    if (ret != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot check old ccache\n");
+        return ret;
+    }
+
+    /* Pre-creating the ccache must be done as root, otherwise we can't mkdir
+     * some of the DIR: cache components. One example is /run/user/$UID because
+     * logind doesn't create the directory until the session phase, whereas
+     * we need the directory during the auth phase already
+     */
+    ret = k5c_precreate_ccache(kr, offline);
+    if (ret != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot precreate ccache\n");
+        return ret;
+    }
+
+    return EOK;
+}
+
 static int k5c_setup(struct krb5_req *kr, uint32_t offline)
 {
     krb5_error_code kerr;
@@ -1881,6 +2068,11 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
         return kerr;
     }
 
+    kerr = k5c_ccache_setup(kr, offline);
+    if (kerr != EOK) {
+        return kerr;
+    }
+
     if (offline || (fast_val == K5C_FAST_NEVER && kr->validate == false)) {
         /* If krb5_child was started as setuid, but we don't need to
          * perform either validation or FAST, just drop privileges to
@@ -1974,7 +2166,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
 
     kerr = set_lifetime_options(kr->options);
     if (kerr != 0) {
-        DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n"));
+        DEBUG(SSSDBG_OP_FAILURE, "set_lifetime_options failed.\n");
         return kerr;
     }
 
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 71c7f9c9f662e16b94afda0c8c0ae24666f0ba15..93961172c7a3a5d8f2a4fb320370037f188b5909 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -144,6 +144,11 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
         buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
                      sss_authtok_get_size(kr->pd->authtok);
+
+        buf->size += sizeof(uint32_t);
+        if (kr->old_ccname) {
+            buf->size += strlen(kr->old_ccname);
+        }
     }
 
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
@@ -182,6 +187,14 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
         SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->ccname), &rp);
         safealign_memcpy(&buf->data[rp], kr->ccname, strlen(kr->ccname), &rp);
 
+        if (kr->old_ccname) {
+            SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->old_ccname), &rp);
+            safealign_memcpy(&buf->data[rp], kr->old_ccname,
+                             strlen(kr->old_ccname), &rp);
+        } else {
+            SAFEALIGN_SET_UINT32(&buf->data[rp], 0, &rp);
+        }
+
         SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(keytab), &rp);
         safealign_memcpy(&buf->data[rp], keytab, strlen(keytab), &rp);
 
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index 8826a28ed5ea064317c62682003dc0e9a6df01b6..a59863b4d8aa8bdcc241c87befd672a5e5c876a3 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -239,8 +239,7 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
         kr->ccname = expand_ccname_template(kr, kr,
                                         dp_opt_get_cstring(kr->krb5_ctx->opts,
                                                            KRB5_CCNAME_TMPL),
-                                            kr->krb5_ctx->illegal_path_re,
-                                            true, true);
+                                            kr->krb5_ctx->illegal_path_re, true, true);
         if (!kr->ccname) goto fail;
 
         DEBUG(SSSDBG_FUNC_DATA, "ccname [%s] uid [%llu] gid [%llu]\n",
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index d5da64622eebe7f779816c7f2090da5b9a9b13f0..c1ed0fb634c447904b63335d1cd161b7e7914a08 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -31,6 +31,7 @@ struct err_string error_to_str[] = {
     { "Invalid credential type" },  /* ERR_INVALID_CRED_TYPE */
     { "No credentials available" }, /* ERR_NO_CREDS */
     { "Credentials are expired" }, /* ERR_CREDS_EXPIRED */
+    { "Credentials are expired, old ccache was removed" }, /* ERR_CREDS_EXPIRED_CCACHE */
     { "Failure setting user credentials"}, /* ERR_CREDS_INVALID */
     { "No cached credentials available" }, /* ERR_NO_CACHED_CREDS */
     { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 2bc576605e613d674d38b54aae1604c0b044635f..f71ede8d0fa000627a1bd994ec8bd94a632b35b2 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -56,6 +56,7 @@ enum sssd_errors {
     ERR_CREDS_INVALID,
     ERR_NO_CACHED_CREDS,
     ERR_CACHED_CREDS_EXPIRED,
+    ERR_CREDS_EXPIRED_CCACHE,
     ERR_AUTH_DENIED,
     ERR_AUTH_FAILED,
     ERR_CHPASS_DENIED,
-- 
1.9.3

-------------- next part --------------
>From 329a54c4476f5d9600c58c0ef6ba1051380136f2 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 19 Oct 2014 12:28:13 +0200
Subject: [PATCH 6/6] KRB5: Do not switch_creds() if already the specified user

The code didn't have to handle this case previously as sssd_be was always
running as root and switching to the ccache as the user logging in.

Also handle NULL creds on restore_creds() in case there was no switch.
One less if-condition and fewer indentation levels.
---
 src/tests/cwrap/test_become_user.c |  7 +++++++
 src/util/become_user.c             | 29 +++++++++++++++++++++--------
 2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/src/tests/cwrap/test_become_user.c b/src/tests/cwrap/test_become_user.c
index 06d3ad425c4928e9e9bff661fbb8f7b4536b8896..7ecea5aac34bb73ca81d94ad481f05b338e65ed0 100644
--- a/src/tests/cwrap/test_become_user.c
+++ b/src/tests/cwrap/test_become_user.c
@@ -76,6 +76,7 @@ void test_switch_user(void **state)
     struct passwd *sssd;
     TALLOC_CTX *tmp_ctx;
     struct sss_creds *saved_creds;
+    struct sss_creds *saved_creds2 = NULL;
 
     check_leaks_push(global_talloc_context);
     tmp_ctx = talloc_new(global_talloc_context);
@@ -102,6 +103,12 @@ void test_switch_user(void **state)
     assert_int_equal(saved_creds->uid, 0);
     assert_int_equal(saved_creds->gid, 0);
 
+    /* Attempt to restore creds again */
+    ret = switch_creds(tmp_ctx, sssd->pw_uid, sssd->pw_gid,
+                       0, NULL, &saved_creds2);
+    assert_int_equal(ret, EOK);
+    assert_null(saved_creds2);
+
     /* restore root */
     ret = restore_creds(saved_creds);
     assert_int_equal(ret, EOK);
diff --git a/src/util/become_user.c b/src/util/become_user.c
index b5f94f993cd2c23bd3340fc502d36a530aa729fa..7dd2c752b1d0f289e7d82feee6d93e5974823f95 100644
--- a/src/util/become_user.c
+++ b/src/util/become_user.c
@@ -90,9 +90,14 @@ errno_t switch_creds(TALLOC_CTX *mem_ctx,
     struct sss_creds *ssc = NULL;
     int size;
     int ret;
+    uid_t myuid;
+    uid_t mygid;
 
     DEBUG(SSSDBG_FUNC_DATA, "Switch user to [%d][%d].\n", uid, gid);
 
+    myuid = geteuid();
+    mygid = getegid();
+
     if (saved_creds) {
         /* save current user credentials */
         size = getgroups(0, NULL);
@@ -124,8 +129,8 @@ errno_t switch_creds(TALLOC_CTX *mem_ctx,
         }
 
         /* we care only about effective ids */
-        ssc->uid = geteuid();
-        ssc->gid = getegid();
+        ssc->uid = myuid;
+        ssc->gid = mygid;
     }
 
     /* if we are regaining root set euid first so that we have CAP_SETUID back,
@@ -141,7 +146,12 @@ errno_t switch_creds(TALLOC_CTX *mem_ctx,
         }
     }
 
-    /* TODO: use prctl to get/set capabilities too ? */
+    /* TODO: use libcap-ng if we need to get/set capabilities too ? */
+
+    if (myuid == uid && mygid == gid) {
+        DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
+        return EOK;
+    }
 
     /* try to setgroups first should always work if CAP_SETUID is set,
      * otherwise it will always fail, failure is not critical though as
@@ -177,11 +187,9 @@ errno_t switch_creds(TALLOC_CTX *mem_ctx,
 
 done:
     if (ret) {
-        if (ssc) {
-            /* attempt to restore creds first */
-            restore_creds(ssc);
-            talloc_free(ssc);
-        }
+        /* attempt to restore creds first */
+        restore_creds(ssc);
+        talloc_free(ssc);
     } else if (saved_creds) {
         *saved_creds = ssc;
     }
@@ -190,6 +198,11 @@ done:
 
 errno_t restore_creds(struct sss_creds *saved_creds)
 {
+    if (saved_creds == NULL) {
+        /* In case save_creds was saved with the UID already dropped */
+        return EOK;
+    }
+
     return switch_creds(saved_creds,
                         saved_creds->uid,
                         saved_creds->gid,
-- 
1.9.3



More information about the sssd-devel mailing list