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 <jhro...@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 <jhro...@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 <jhro...@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.
>From e3b461d35bec407f45c26e8a3947a87107a7df7d Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@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
>From 908003f3aa7f930612a9cdcd11d730f66a5bcbf0 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@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
>From 9076165af0dac859528552b48d1812c275153e0f Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@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 <sb...@redhat.com> + Jakub Hrozek <jhro...@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 <sb...@redhat.com> + Jakub Hrozek <jhro...@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
>From ebe41bb1eb6b994b3501ead2e3b4f21eec1b9099 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@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
>From b33581c40a0c1ee6caade428e5d153a962321999 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@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
>From 4e125ce8d3d672099c54f354169f024b6d1f99ab Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@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
_______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel