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

Jakub Hrozek jhrozek at redhat.com
Thu Nov 13 18:30:41 UTC 2014


On Wed, Nov 12, 2014 at 05:08:09PM +0100, Lukas Slebodnik wrote:
> On (12/11/14 15:44), Jakub Hrozek wrote:
> >On Wed, Nov 12, 2014 at 01:45:26PM +0100, Lukas Slebodnik wrote:
> >> On (11/11/14 22:37), Jakub Hrozek wrote:
> >> >On Tue, Nov 11, 2014 at 09:11:45PM +0100, Jakub Hrozek wrote:
> >> >> On Tue, Nov 11, 2014 at 06:23:24PM +0100, Lukas Slebodnik wrote:
> >> >> > On (11/11/14 13:45), Jakub Hrozek wrote:
> >> >> > >On Tue, Nov 11, 2014 at 11:15:30AM +0100, Jakub Hrozek wrote:
> >> >> > >> Can you give me access to a host that reproduces this crash? ccname
> >> >> > >> should never be NULL with the new patches ...
> >> >> > >
> >> >> > >..except on access_provider=krb5...
> >> >> > >
> >> >> > >Thanks for catching that, new patches are attached.
> >> >> > 
> >> >> > There is problem with support of enterprise principals.
> >> >> > authentication for such users failed.
> >> >> 
> >> >> Thanks a lot for catching that, I had no trouble logging in as a user
> >> >> from a child domain, but I could reproduce the issue when I used a
> >> >> completely different suffix.
> >> >> 
> >> >> I'll work on a fix.
> >> >
> >> >Thanks again for the catch, can you test this additional fix on top of
> >> >your patches? (Sorry for sending a separate patch, I want to get a fresh
> >> >look at the set tomorrow, some other krb5_ccache.c functions might get
> >> >the same treatment)
> >> 
> >> >From 5d95f998643d875bcea149dde5e7a16aa42063b4 Mon Sep 17 00:00:00 2001
> >> >From: Jakub Hrozek <jhrozek at redhat.com>
> >> >Date: Tue, 11 Nov 2014 22:33:28 +0100
> >> >Subject: [PATCH] sss_krb5_check_ccache_princ fix
> >> >
> >> >---
> >> krb5 + ad tests passed with this patch.
> >> 
> >> LS
> >
> >Thanks, the whole patchset is attached again.
> 
> >From 7722435d86ab57c91a58028845fe781bc28605e0 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 wold be cumbersome. Check for illegal patterns when
> >expanding the template instead.
> >---
> > 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_utils-tests.c     | 78 ++++++++++++++++------------------------
> > 5 files changed, 69 insertions(+), 94 deletions(-)
> >
> Our CI does not like this patch.
> 
> src/providers/krb5/krb5_auth.c: In function 'krb5_auth_prepare_ccache_name':
> src/providers/krb5/krb5_auth.c:305:74: error: passing argument 4 of 'expand_ccname_template' makes pointer from integer without a cast [-Werror]
>              kr->ccname = expand_ccname_template(kr, kr, ccname_template, true,
>                                                                           ^
> In file included from src/providers/krb5/krb5_auth.c:41:0:
> src/providers/krb5/krb5_utils.h:45:7: note: expected 'struct pcre *' but argument is of type 'int'
>  char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
>        ^
> src/providers/krb5/krb5_auth.c:305:26: error: too few arguments to function 'expand_ccname_template'
>              kr->ccname = expand_ccname_template(kr, kr, ccname_template, true,
>                           ^
> In file included from src/providers/krb5/krb5_auth.c:41:0:
> src/providers/krb5/krb5_utils.h:45:7: note: declared here
>  char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
>        ^
> ../sssd/src/providers/krb5/krb5_auth.c:313:45: error: passing argument 2 of 'sss_krb5_precreate_ccache' makes integer from pointer without a cast [-Werror]
>                                              kr->krb5_ctx->illegal_path_re,
>                                              ^
> In file included from src/providers/krb5/krb5_auth.h:35:0,
>                  from src/providers/krb5/krb5_auth.c:40:
> src/providers/krb5/krb5_ccache.h:38:9: note: expected 'uid_t' but argument is of type 'struct pcre *'
>  errno_t sss_krb5_precreate_ccache(const char *ccname, uid_t uid, gid_t gid);
>          ^
> src/providers/krb5/krb5_auth.c:312:19: error: too many arguments to function 'sss_krb5_precreate_ccache'
>              ret = sss_krb5_precreate_ccache(kr->ccname,
>                    ^
> In file included from src/providers/krb5/krb5_auth.h:35:0,
>                  from src/providers/krb5/krb5_auth.c:40:
> src/providers/krb5/krb5_ccache.h:38:9: note: declared here
>  errno_t sss_krb5_precreate_ccache(const char *ccname, uid_t uid, gid_t gid);
>          ^
> cc1: all warnings being treated as errors
> Makefile:10727: recipe for target 'src/providers/krb5/libsss_krb5_common_la-krb5_auth.lo' failed
> 
> >From 6a854dab8a1affbc9af27b46591a0d8562829afb 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                             |  14 +-
> > src/providers/krb5/krb5_auth.c          | 222 ++++----------------------------
> > 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, 282 insertions(+), 247 deletions(-)
> >
> >diff --git a/Makefile.am b/Makefile.am
> >index 41b1843baee41ea8a67eb47e6786286190bbdcb9..c6601468e79244aad0a1bb8d7c87190aad9ef61a 100644
> >--- a/Makefile.am
> >+++ b/Makefile.am
> >@@ -1604,6 +1604,7 @@ krb5_child_test_SOURCES = \
> >     src/providers/krb5/krb5_child_handler.c \
> >     src/providers/krb5/krb5_common.c \
> >     src/util/sss_krb5.c \
> >+    src/util/find_uid.c \
> >     src/providers/data_provider_fo.c \
> >     src/providers/data_provider_opts.c \
> >     src/providers/data_provider_callbacks.c \
> >@@ -2491,27 +2492,36 @@ libsss_ad_la_LDFLAGS = \
> This change is not necessary and, because krb5_child test is already linked
> with libsss_util, which contains this file.
> 
> Moreover, this change caused compilation failures on platforms with disabled
> link_all_deplibs.
> 
>   CCLD     krb5-child-test
> /usr/bin/ld: src/util/krb5_child_test-find_uid.o: undefined reference to symbol
> +'sd_uid_get_sessions@@LIBSYSTEMD_209'
> /lib64/libsystemd.so.0: error adding symbols: DSO missing from command line
> collect2: error: ld returned 1 exit status
> 
> LS

Thank you, see the attached patches.
-------------- next part --------------
>From e3b461d35bec407f45c26e8a3947a87107a7df7d 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 7aa258cc1ac17f9bb08bd303cad96ff4c24cd1e3..4d54da22f2ad0d91d86c4355fabde85fbd1a866d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2873,6 +2873,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 908003f3aa7f930612a9cdcd11d730f66a5bcbf0 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..15dc316fab32934cd422368c1556c2f724fbd8ab 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 9076165af0dac859528552b48d1812c275153e0f 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 keytab 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                    | 672 +------------------
 src/providers/krb5/krb5_utils.h                    |  15 -
 src/tests/krb5_child-test.c                        |   1 +
 src/tests/krb5_utils-tests.c                       |   1 +
 12 files changed, 197 insertions(+), 1291 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 4d54da22f2ad0d91d86c4355fabde85fbd1a866d..41b1843baee41ea8a67eb47e6786286190bbdcb9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -563,6 +563,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 \
@@ -1317,6 +1318,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 \
@@ -1598,6 +1600,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 \
@@ -2301,6 +2304,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 15dc316fab32934cd422368c1556c2f724fbd8ab..d604fa833c7451266da2b1dc2e549174387a31f9 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..5f4078f2c2f30eddc004851e38b5fdafe93532e2 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"
@@ -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 ebe41bb1eb6b994b3501ead2e3b4f21eec1b9099 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 wold 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 5f4078f2c2f30eddc004851e38b5fdafe93532e2..1ca16100c434c9fd6ea6aff125fb4902ebe29e83 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 b33581c40a0c1ee6caade428e5d153a962321999 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                             |  14 +-
 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, 282 insertions(+), 248 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 41b1843baee41ea8a67eb47e6786286190bbdcb9..c6601468e79244aad0a1bb8d7c87190aad9ef61a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1604,6 +1604,7 @@ krb5_child_test_SOURCES = \
     src/providers/krb5/krb5_child_handler.c \
     src/providers/krb5/krb5_common.c \
     src/util/sss_krb5.c \
+    src/util/find_uid.c \
     src/providers/data_provider_fo.c \
     src/providers/data_provider_opts.c \
     src/providers/data_provider_callbacks.c \
@@ -2491,27 +2492,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 d604fa833c7451266da2b1dc2e549174387a31f9..b9830685122d30b9d9b0d2d4425dc05c78110c98 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 4e125ce8d3d672099c54f354169f024b6d1f99ab 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             | 27 ++++++++++++++++++++-------
 2 files changed, 27 insertions(+), 7 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..8fbc228ad93d16bd076d7920e22fb4b2f83417cb 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,
@@ -143,6 +148,11 @@ errno_t switch_creds(TALLOC_CTX *mem_ctx,
 
     /* TODO: use prctl 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
      * generally we only really care about uid and at mot primary gid */
@@ -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