[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