From ecf7095b458fd37aa15bb4b2037d5dc9a7979e1c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Dec 04 2017 23:01:24 +0000 Subject: import sssd-1.15.2-50.el7_4.8 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bae0ba3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/sssd-1.15.2.tar.gz diff --git a/.sssd.metadata b/.sssd.metadata new file mode 100644 index 0000000..bc1073e --- /dev/null +++ b/.sssd.metadata @@ -0,0 +1 @@ +f94298ac05169cdf5a9082c3aba9f6a18513720a SOURCES/sssd-1.15.2.tar.gz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch b/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch new file mode 100644 index 0000000..e405016 --- /dev/null +++ b/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch @@ -0,0 +1,36 @@ +From 1bf225166db64e81fe94cbee0fd3b1dc124717f4 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 21 Mar 2017 12:27:16 +0100 +Subject: [PATCH 01/15] MAN: Mention sssd-secrets in "SEE ALSO" section +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Resolves: +https://pagure.io/SSSD/sssd/issue/3344 + +Reviewed-by: Fabiano Fidêncio +--- + src/man/include/seealso.xml | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/man/include/seealso.xml b/src/man/include/seealso.xml +index 25b421748fb19881552de8b6384af2c794063e11..2e9c646c475887bce3612472975ade375edbd819 100644 +--- a/src/man/include/seealso.xml ++++ b/src/man/include/seealso.xml +@@ -28,6 +28,12 @@ + 5 + , + ++ ++ ++ sssd-secrets ++ 5 ++ , ++ + + sss_cache8 + , +-- +2.9.3 + diff --git a/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch b/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch new file mode 100644 index 0000000..24961c7 --- /dev/null +++ b/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch @@ -0,0 +1,369 @@ +From 4f98b36562fb02f95c9af7af6fde548334ce9c34 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 8 Feb 2017 14:28:28 +0100 +Subject: [PATCH 02/15] split_on_separator: move to a separate file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To be able to include split_on_separator() without additional +dependencies (only talloc), it is moved into a separate file. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 30 ++++++++++--- + src/util/util.c | 93 ---------------------------------------- + src/util/util_ext.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 144 insertions(+), 100 deletions(-) + create mode 100644 src/util/util_ext.c + +diff --git a/Makefile.am b/Makefile.am +index 45b04de2638a745a189c0b4e5794ccd29913b10d..6dae4f2dd7f2dee501add82c7ab4f15fcbcc59ac 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -987,6 +987,7 @@ libsss_util_la_SOURCES = \ + src/sbus/sssd_dbus_common_signals.c \ + src/sbus/sssd_dbus_utils.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + src/util/memory.c \ + src/util/safe-format-string.c \ + src/util/server.c \ +@@ -2355,19 +2356,23 @@ test_authtok_SOURCES = \ + src/tests/cmocka/test_authtok.c \ + src/util/authtok.c \ + src/util/authtok-utils.c \ +- src/util/util.c ++ src/util/util.c \ ++ src/util/util_ext.c \ ++ $(NULL) + test_authtok_CFLAGS = \ + $(AM_CFLAGS) \ + $(TALLOC_CFLAGS) \ + $(POPT_CFLAGS) \ +- $(DHASH_CFLAGS) ++ $(DHASH_CFLAGS) \ ++ $(NULL) + test_authtok_LDADD = \ + $(TALLOC_LIBS) \ + $(CMOCKA_LIBS) \ + $(DHASH_LIBS) \ + $(POPT_LIBS) \ + libsss_test_common.la \ +- libsss_debug.la ++ libsss_debug.la \ ++ $(NULL) + + sss_nss_idmap_tests_SOURCES = \ + src/tests/cmocka/sss_nss_idmap-tests.c +@@ -2839,6 +2844,7 @@ test_child_common_SOURCES = \ + src/util/atomic_io.c \ + src/util/util_errors.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + $(NULL) + test_child_common_CFLAGS = \ + $(AM_CFLAGS) \ +@@ -3774,6 +3780,7 @@ krb5_child_SOURCES = \ + src/util/authtok.c \ + src/util/authtok-utils.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + src/util/signal.c \ + src/util/strtonum.c \ + src/util/become_user.c \ +@@ -3807,6 +3814,7 @@ ldap_child_SOURCES = \ + src/util/authtok.c \ + src/util/authtok-utils.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + src/util/signal.c \ + src/util/become_user.c \ + $(NULL) +@@ -3827,6 +3835,7 @@ selinux_child_SOURCES = \ + src/util/sss_semanage.c \ + src/util/atomic_io.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + $(NULL) + selinux_child_CFLAGS = \ + $(AM_CFLAGS) \ +@@ -3845,6 +3854,7 @@ gpo_child_SOURCES = \ + src/providers/ad/ad_gpo_child.c \ + src/util/atomic_io.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + src/util/signal.c + gpo_child_CFLAGS = \ + $(AM_CFLAGS) \ +@@ -3876,6 +3886,7 @@ p11_child_SOURCES = \ + src/p11_child/p11_child_nss.c \ + src/util/atomic_io.c \ + src/util/util.c \ ++ src/util/util_ext.c \ + $(NULL) + p11_child_CFLAGS = \ + $(AM_CFLAGS) \ +@@ -3893,16 +3904,21 @@ p11_child_LDADD = \ + + memberof_la_SOURCES = \ + src/ldb_modules/memberof.c \ +- src/util/util.c ++ src/util/util.c \ ++ src/util/util_ext.c \ ++ $(NULL) + memberof_la_CFLAGS = \ +- $(AM_CFLAGS) ++ $(AM_CFLAGS) \ ++ $(NULL) + memberof_la_LIBADD = \ + libsss_debug.la \ + $(LDB_LIBS) \ +- $(DHASH_LIBS) ++ $(DHASH_LIBS) \ ++ $(NULL) + memberof_la_LDFLAGS = \ + -avoid-version \ +- -module ++ -module \ ++ $(NULL) + + if BUILD_KRB5_LOCATOR_PLUGIN + sssd_krb5_locator_plugin_la_SOURCES = \ +diff --git a/src/util/util.c b/src/util/util.c +index a528f0c0249c33bfc3d3275250e74d5edcef2e6f..9d6202f695d516f20d648621da81a2d5e746daa5 100644 +--- a/src/util/util.c ++++ b/src/util/util.c +@@ -35,99 +35,6 @@ + int socket_activated = 0; + int dbus_activated = 0; + +-int split_on_separator(TALLOC_CTX *mem_ctx, const char *str, +- const char sep, bool trim, bool skip_empty, +- char ***_list, int *size) +-{ +- int ret; +- const char *substr_end = str; +- const char *substr_begin = str; +- const char *sep_pos = NULL; +- size_t substr_len; +- char **list = NULL; +- int num_strings = 0; +- TALLOC_CTX *tmp_ctx = NULL; +- +- if (str == NULL || *str == '\0' || _list == NULL) { +- return EINVAL; +- } +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- return ENOMEM; +- } +- +- do { +- substr_len = 0; +- +- /* If this is not the first substring, then move from the separator. */ +- if (sep_pos != NULL) { +- substr_end = sep_pos + 1; +- substr_begin = sep_pos + 1; +- } +- +- /* Find end of the first substring */ +- while (*substr_end != sep && *substr_end != '\0') { +- substr_end++; +- substr_len++; +- } +- +- sep_pos = substr_end; +- +- if (trim) { +- /* Trim leading whitespace */ +- while (isspace(*substr_begin) && substr_begin < substr_end) { +- substr_begin++; +- substr_len--; +- } +- +- /* Trim trailing whitespace */ +- while (substr_end - 1 > substr_begin && isspace(*(substr_end-1))) { +- substr_end--; +- substr_len--; +- } +- } +- +- /* Copy the substring to the output list of strings */ +- if (skip_empty == false || substr_len > 0) { +- list = talloc_realloc(tmp_ctx, list, char*, num_strings + 2); +- if (list == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- /* empty string is stored for substr_len == 0 */ +- list[num_strings] = talloc_strndup(list, substr_begin, substr_len); +- if (list[num_strings] == NULL) { +- ret = ENOMEM; +- goto done; +- } +- num_strings++; +- } +- +- } while (*sep_pos != '\0'); +- +- if (list == NULL) { +- /* No allocations were done, make space for the NULL */ +- list = talloc(tmp_ctx, char *); +- if (list == NULL) { +- ret = ENOMEM; +- goto done; +- } +- } +- list[num_strings] = NULL; +- +- if (size) { +- *size = num_strings; +- } +- +- *_list = talloc_steal(mem_ctx, list); +- ret = EOK; +-done: +- talloc_free(tmp_ctx); +- return ret; +-} +- + static void free_args(char **args) + { + int i; +diff --git a/src/util/util_ext.c b/src/util/util_ext.c +new file mode 100644 +index 0000000000000000000000000000000000000000..fceb8c873a26471d476b39d5d4e567c445ed8d0b +--- /dev/null ++++ b/src/util/util_ext.c +@@ -0,0 +1,121 @@ ++/* ++ SSSD helper calls - can be used by libraries for external use as well ++ ++ Authors: ++ Simo Sorce ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#define EOK 0 ++ ++int split_on_separator(TALLOC_CTX *mem_ctx, const char *str, ++ const char sep, bool trim, bool skip_empty, ++ char ***_list, int *size) ++{ ++ int ret; ++ const char *substr_end = str; ++ const char *substr_begin = str; ++ const char *sep_pos = NULL; ++ size_t substr_len; ++ char **list = NULL; ++ int num_strings = 0; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ if (str == NULL || *str == '\0' || _list == NULL) { ++ return EINVAL; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ do { ++ substr_len = 0; ++ ++ /* If this is not the first substring, then move from the separator. */ ++ if (sep_pos != NULL) { ++ substr_end = sep_pos + 1; ++ substr_begin = sep_pos + 1; ++ } ++ ++ /* Find end of the first substring */ ++ while (*substr_end != sep && *substr_end != '\0') { ++ substr_end++; ++ substr_len++; ++ } ++ ++ sep_pos = substr_end; ++ ++ if (trim) { ++ /* Trim leading whitespace */ ++ while (isspace(*substr_begin) && substr_begin < substr_end) { ++ substr_begin++; ++ substr_len--; ++ } ++ ++ /* Trim trailing whitespace */ ++ while (substr_end - 1 > substr_begin && isspace(*(substr_end-1))) { ++ substr_end--; ++ substr_len--; ++ } ++ } ++ ++ /* Copy the substring to the output list of strings */ ++ if (skip_empty == false || substr_len > 0) { ++ list = talloc_realloc(tmp_ctx, list, char*, num_strings + 2); ++ if (list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ /* empty string is stored for substr_len == 0 */ ++ list[num_strings] = talloc_strndup(list, substr_begin, substr_len); ++ if (list[num_strings] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ num_strings++; ++ } ++ ++ } while (*sep_pos != '\0'); ++ ++ if (list == NULL) { ++ /* No allocations were done, make space for the NULL */ ++ list = talloc(tmp_ctx, char *); ++ if (list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ list[num_strings] = NULL; ++ ++ if (size) { ++ *size = num_strings; ++ } ++ ++ *_list = talloc_steal(mem_ctx, list); ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} +-- +2.9.3 + diff --git a/SOURCES/0003-util-move-string_in_list-to-util_ext.patch b/SOURCES/0003-util-move-string_in_list-to-util_ext.patch new file mode 100644 index 0000000..f21cb03 --- /dev/null +++ b/SOURCES/0003-util-move-string_in_list-to-util_ext.patch @@ -0,0 +1,91 @@ +From 7bf6cf5632fbdf83a37c52c40b7b982094b5c668 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 20 Feb 2017 17:28:51 +0100 +Subject: [PATCH 03/15] util: move string_in_list to util_ext +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To be able to include string_in_list() without additional +dependencies it is moved into a separate file. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + src/util/util.c | 20 -------------------- + src/util/util_ext.c | 22 ++++++++++++++++++++++ + 2 files changed, 22 insertions(+), 20 deletions(-) + +diff --git a/src/util/util.c b/src/util/util.c +index 9d6202f695d516f20d648621da81a2d5e746daa5..f0e8f9dd6a4bceed6befb74c57aa066b19a72bb7 100644 +--- a/src/util/util.c ++++ b/src/util/util.c +@@ -617,26 +617,6 @@ errno_t add_string_to_list(TALLOC_CTX *mem_ctx, const char *string, + return EOK; + } + +-bool string_in_list(const char *string, char **list, bool case_sensitive) +-{ +- size_t c; +- int(*compare)(const char *s1, const char *s2); +- +- if (string == NULL || list == NULL || *list == NULL) { +- return false; +- } +- +- compare = case_sensitive ? strcmp : strcasecmp; +- +- for (c = 0; list[c] != NULL; c++) { +- if (compare(string, list[c]) == 0) { +- return true; +- } +- } +- +- return false; +-} +- + void safezero(void *data, size_t size) + { + volatile uint8_t *p = data; +diff --git a/src/util/util_ext.c b/src/util/util_ext.c +index fceb8c873a26471d476b39d5d4e567c445ed8d0b..04dc02a8adf32bd0590fe6eba230658e67d0a362 100644 +--- a/src/util/util_ext.c ++++ b/src/util/util_ext.c +@@ -24,6 +24,8 @@ + #include + #include + #include ++#include ++#include + + #define EOK 0 + +@@ -119,3 +121,23 @@ done: + talloc_free(tmp_ctx); + return ret; + } ++ ++bool string_in_list(const char *string, char **list, bool case_sensitive) ++{ ++ size_t c; ++ int(*compare)(const char *s1, const char *s2); ++ ++ if (string == NULL || list == NULL || *list == NULL) { ++ return false; ++ } ++ ++ compare = case_sensitive ? strcmp : strcasecmp; ++ ++ for (c = 0; list[c] != NULL; c++) { ++ if (compare(string, list[c]) == 0) { ++ return true; ++ } ++ } ++ ++ return false; ++} +-- +2.9.3 + diff --git a/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch b/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch new file mode 100644 index 0000000..1a04fe7 --- /dev/null +++ b/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch @@ -0,0 +1,5787 @@ +From 64d74f65ca66fafd67c99ac17c5e45e584f59d83 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 2 Feb 2017 11:24:02 +0100 +Subject: [PATCH 04/15] certmap: add new library libsss_certmap +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this library it would be possible to map certificates and users not +only by adding the full certificate to the user's LDAP object but by +adding e.g. only parts like the issuer and subject name. Additionally +the library is also able to flexible select/match certificates based on +values in the certificate. + +Details about mapping and matching rules can be found in the included +man page. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 55 ++ + configure.ac | 1 + + contrib/sssd.spec.in | 32 + + src/lib/certmap/sss_cert_content_nss.c | 1014 +++++++++++++++++++ + src/lib/certmap/sss_certmap.c | 993 +++++++++++++++++++ + src/lib/certmap/sss_certmap.doxy.in | 3 + + src/lib/certmap/sss_certmap.exports | 13 + + src/lib/certmap/sss_certmap.h | 155 +++ + src/lib/certmap/sss_certmap.pc.in | 11 + + src/lib/certmap/sss_certmap_attr_names.c | 107 +++ + src/lib/certmap/sss_certmap_int.h | 187 ++++ + src/lib/certmap/sss_certmap_krb5_match.c | 558 +++++++++++ + src/lib/certmap/sss_certmap_ldap_mapping.c | 367 +++++++ + src/man/Makefile.am | 2 +- + src/man/po/po4a.cfg | 1 + + src/man/sss-certmap.5.xml | 600 ++++++++++++ + src/tests/cmocka/test_certmap.c | 1443 ++++++++++++++++++++++++++++ + src/tests/dlopen-tests.c | 1 + + 18 files changed, 5542 insertions(+), 1 deletion(-) + create mode 100644 src/lib/certmap/sss_cert_content_nss.c + create mode 100644 src/lib/certmap/sss_certmap.c + create mode 100644 src/lib/certmap/sss_certmap.doxy.in + create mode 100644 src/lib/certmap/sss_certmap.exports + create mode 100644 src/lib/certmap/sss_certmap.h + create mode 100644 src/lib/certmap/sss_certmap.pc.in + create mode 100644 src/lib/certmap/sss_certmap_attr_names.c + create mode 100644 src/lib/certmap/sss_certmap_int.h + create mode 100644 src/lib/certmap/sss_certmap_krb5_match.c + create mode 100644 src/lib/certmap/sss_certmap_ldap_mapping.c + create mode 100644 src/man/sss-certmap.5.xml + create mode 100644 src/tests/cmocka/test_certmap.c + +diff --git a/Makefile.am b/Makefile.am +index 6dae4f2dd7f2dee501add82c7ab4f15fcbcc59ac..8ca12c10d2713b6a72361d84b25486500c79f407 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -278,6 +278,7 @@ if HAVE_CMOCKA + simple-access-tests \ + krb5_common_test \ + test_iobuf \ ++ sss_certmap_test \ + $(NULL) + + if HAVE_LIBRESOLV +@@ -1074,6 +1075,7 @@ SSSD_INTERNAL_LTLIBS = \ + lib_LTLIBRARIES = libipa_hbac.la \ + libsss_idmap.la \ + libsss_nss_idmap.la \ ++ libsss_certmap.la \ + $(NULL) + + pkgconfig_DATA += src/lib/ipa_hbac/ipa_hbac.pc +@@ -1128,6 +1130,7 @@ include_HEADERS = \ + src/lib/ipa_hbac/ipa_hbac.h \ + src/lib/idmap/sss_idmap.h \ + src/sss_client/idmap/sss_nss_idmap.h \ ++ src/lib/certmap/sss_certmap.h \ + $(NULL) + + if BUILD_LIBWBCLIENT +@@ -1712,6 +1715,38 @@ sssd_check_socket_activated_responders_LDADD = \ + $(NULL) + endif + ++if HAVE_NSS ++pkgconfig_DATA += src/lib/certmap/sss_certmap.pc ++libsss_certmap_la_DEPENDENCIES = src/lib/certmap/sss_certmap.exports ++libsss_certmap_la_SOURCES = \ ++ src/lib/certmap/sss_certmap.c \ ++ src/lib/certmap/sss_certmap_attr_names.c \ ++ src/lib/certmap/sss_cert_content_nss.c \ ++ src/lib/certmap/sss_certmap_krb5_match.c \ ++ src/lib/certmap/sss_certmap_ldap_mapping.c \ ++ src/util/util_ext.c \ ++ src/util/cert/cert_common.c \ ++ src/util/crypto/nss/nss_base64.c \ ++ src/util/cert/nss/cert.c \ ++ src/util/crypto/nss/nss_util.c \ ++ $(NULL) ++libsss_certmap_la_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(TALLOC_CFLAGS) \ ++ $(NSS_CFLAGS) \ ++ $(NULL) ++libsss_certmap_la_LIBADD = \ ++ $(TALLOC_LIBS) \ ++ $(NSS_LIBS) \ ++ $(NULL) ++libsss_certmap_la_LDFLAGS = \ ++ -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \ ++ -version-info 0:0:0 ++ ++dist_noinst_DATA += src/lib/certmap/sss_certmap.exports ++dist_noinst_HEADERS += src/lib/certmap/sss_certmap_int.h ++endif ++ + ################# + # Feature Tests # + ################# +@@ -3245,6 +3280,25 @@ test_inotify_LDADD = \ + libsss_test_common.la \ + $(NULL) + ++if HAVE_NSS ++sss_certmap_test_SOURCES = \ ++ src/tests/cmocka/test_certmap.c \ ++ src/lib/certmap/sss_certmap_attr_names.c \ ++ $(NULL) ++sss_certmap_test_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NSS_CFLAGS) \ ++ $(NULL) ++sss_certmap_test_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(POPT_LIBS) \ ++ $(TALLOC_LIBS) \ ++ $(NSS_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ libsss_certmap.la \ ++ $(NULL) ++endif + endif # HAVE_CMOCKA + + noinst_PROGRAMS = pam_test_client +@@ -4404,6 +4458,7 @@ docs: + $(DOXYGEN) src/lib/ipa_hbac/ipa_hbac.doxy + $(DOXYGEN) src/lib/idmap/sss_idmap.doxy + $(DOXYGEN) src/sss_client/idmap/sss_nss_idmap.doxy ++ $(DOXYGEN) src/lib/certmap/sss_certmap.doxy + if BUILD_IFP + $(DOXYGEN) src/lib/sifp/sss_simpleifp.doxy + endif +diff --git a/configure.ac b/configure.ac +index e6a3b4e857bcbec16873f54008e6b42aeb9b7cd7..dd1012015a5fea9f25e5b5199b4868fbc0bc14c4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -483,6 +483,7 @@ AC_CONFIG_FILES([Makefile contrib/sssd.spec src/examples/rwtab src/doxy.config + src/tests/intg/Makefile + src/lib/ipa_hbac/ipa_hbac.pc src/lib/ipa_hbac/ipa_hbac.doxy + src/lib/idmap/sss_idmap.pc src/lib/idmap/sss_idmap.doxy ++ src/lib/certmap/sss_certmap.pc src/lib/certmap/sss_certmap.doxy + src/sss_client/idmap/sss_nss_idmap.pc + src/sss_client/idmap/sss_nss_idmap.doxy + src/sss_client/libwbclient/wbclient_sssd.pc +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 5bd2beb89014ba7c86b4231f0357a7a6e10a18a8..28ebe07a26a3112210b092b7831e7f6aae061c8d 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -658,6 +658,25 @@ The libnfsidmap sssd module provides a way for rpc.idmapd to call SSSD to map + UIDs/GIDs to names and vice versa. It can be also used for mapping principal + (user) name to IDs(UID or GID) or to obtain groups which user are member of. + ++%package -n libsss_certmap ++Summary: SSSD Certficate Mapping Library ++Group: Development/Libraries ++License: LGPLv3+ ++Requires(post): /sbin/ldconfig ++Requires(postun): /sbin/ldconfig ++ ++%description -n libsss_certmap ++Library to map certificates to users based on rules ++ ++%package -n libsss_certmap-devel ++Summary: SSSD Certficate Mapping Library ++Group: Development/Libraries ++License: LGPLv3+ ++Requires: libsss_certmap = %{version}-%{release} ++ ++%description -n libsss_certmap-devel ++Library to map certificates to users based on rules ++ + %prep + %setup -q -n %{name}-%{version} + +@@ -888,6 +907,7 @@ done + %{_datadir}/sssd/sssd.api.d + %{_mandir}/man1/sss_ssh_authorizedkeys.1* + %{_mandir}/man1/sss_ssh_knownhostsproxy.1* ++%{_mandir}/man5/sss-certmap.5* + %{_mandir}/man5/sssd.conf.5* + %{_mandir}/man5/sssd-simple.5* + %{_mandir}/man5/sssd-sudo.5* +@@ -1146,6 +1166,18 @@ done + %files nfs-idmap + %{_libdir}/libnfsidmap/sss.so + ++%files -n libsss_certmap ++%defattr(-,root,root,-) ++%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER ++%{_libdir}/libsss_certmap.so.* ++ ++%files -n libsss_certmap-devel ++%defattr(-,root,root,-) ++%doc certmap_doc/html ++%{_includedir}/sss_certmap.h ++%{_libdir}/libsss_certmap.so ++%{_libdir}/pkgconfig/sss_certmap.pc ++ + %pre common + getent group sssd >/dev/null || groupadd -r sssd + getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd +diff --git a/src/lib/certmap/sss_cert_content_nss.c b/src/lib/certmap/sss_cert_content_nss.c +new file mode 100644 +index 0000000000000000000000000000000000000000..d3182895465706c87503b8abb0cea49b7677625d +--- /dev/null ++++ b/src/lib/certmap/sss_cert_content_nss.c +@@ -0,0 +1,1014 @@ ++/* ++ SSSD - certificate handling utils - NSS version ++ The calls defined here should be useable outside of SSSD as well, e.g. in ++ libsss_certmap. ++ ++ Copyright (C) Sumit Bose 2017 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "util/crypto/sss_crypto.h" ++#include "util/crypto/nss/nss_util.h" ++#include "util/cert.h" ++#include "lib/certmap/sss_certmap.h" ++#include "lib/certmap/sss_certmap_int.h" ++ ++ ++/* The following two functions are copied from NSS's lib/certdb/secname.c ++ * becasue CERT_AddAVA is not exported. I just renamed it and made it static ++ * to avoid issues if the call gets exported some time in future. */ ++ ++static void ** ++AddToArray(PLArenaPool *arena, void **array, void *element) ++{ ++ unsigned count; ++ void **ap; ++ ++ /* Count up number of slots already in use in the array */ ++ count = 0; ++ ap = array; ++ if (ap) { ++ while (*ap++) { ++ count++; ++ } ++ } ++ ++ if (array) { ++ array = (void**) PORT_ArenaGrow(arena, array, ++ (count + 1) * sizeof(void *), ++ (count + 2) * sizeof(void *)); ++ } else { ++ array = (void**) PORT_ArenaAlloc(arena, (count + 2) * sizeof(void *)); ++ } ++ if (array) { ++ array[count] = element; ++ array[count+1] = 0; ++ } ++ return array; ++} ++ ++ ++static SECStatus ++sss_CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava) ++{ ++ rdn->avas = (CERTAVA**) AddToArray(arena, (void**) rdn->avas, ava); ++ return rdn->avas ? SECSuccess : SECFailure; ++} ++ ++static SECItem * ++cert_get_ext_by_tag(CERTCertificate *cert, SECOidTag tag) ++{ ++ SECOidData *oid; ++ int i; ++ ++ oid = SECOID_FindOIDByTag(tag); ++ for (i = 0; ++ (cert->extensions != NULL) && (cert->extensions[i] != NULL); ++ i++) ++ if (SECITEM_ItemsAreEqual(&cert->extensions[i]->id, &oid->oid)) ++ return &cert->extensions[i]->value; ++ return NULL; ++} ++ ++static int get_extended_key_usage_oids(TALLOC_CTX *mem_ctx, ++ CERTCertificate *cert, ++ const char ***_oids) ++{ ++ PLArenaPool *pool; ++ SECItem *ext; ++ SECItem **oids = NULL; ++ const char **oids_list = NULL; ++ size_t c; ++ SECStatus rv; ++ char *tmp_str; ++ int ret; ++ ++ pool = PORT_NewArena(sizeof(double)); ++ ext = cert_get_ext_by_tag(cert, SEC_OID_X509_EXT_KEY_USAGE); ++ if (ext != NULL) { ++ rv = SEC_ASN1DecodeItem(pool, &oids, ++ SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate), ++ ext); ++ if (rv != SECSuccess) { ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ for (c = 0; (oids != NULL && oids[c] != NULL); c++); ++ oids_list = talloc_zero_array(mem_ctx, const char *, c + 1); ++ if (oids_list == NULL) { ++ return ENOMEM; ++ } ++ ++ for (c = 0; (oids != NULL && oids[c] != NULL); c++) { ++ tmp_str = CERT_GetOidString(oids[c]); ++ /* is it expexted that NSS OID strings start with "OID." but we ++ * prefer the plain dotted-decimal version so the prefix is skipped */ ++ if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) { ++ PR_smprintf_free(tmp_str); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ oids_list[c] = talloc_strdup(oids_list, tmp_str + 4); ++ PR_smprintf_free(tmp_str); ++ if(oids_list[c] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ PORT_FreeArena(pool, PR_TRUE); ++ if (ret == 0) { ++ *_oids = oids_list; ++ } else { ++ talloc_free(oids_list); ++ } ++ ++ return ret; ++ ++} ++ ++static int get_rdn_str(TALLOC_CTX *mem_ctx, CERTAVA **avas, ++ const char **rdn_str) ++{ ++ size_t c; ++ char *tmp_name = NULL; ++ const char *tmp_str = NULL; ++ int ret; ++ SECStatus rv; ++ CERTRDN rdn = { 0 }; ++ CERTName *name = NULL; ++ PLArenaPool *arena = NULL; ++ ++ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); ++ if (arena == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ++ /* Multiple AVAs should be avoided becasue there is no general ordering ++ * rule and the RDN strings are not reproducible */ ++ for (c = 0; avas[c] != NULL; c++) { ++ rv = sss_CERT_AddAVA(arena, &rdn, avas[c]); ++ if (rv != SECSuccess) { ++ ret = EIO; ++ goto done; ++ } ++ } ++ ++ name = CERT_CreateName(&rdn, NULL); ++ if (name == NULL) { ++ ret = EIO; ++ goto done; ++ } ++ ++ tmp_name = CERT_NameToAscii(name); ++ CERT_DestroyName(name); ++ if (tmp_name == NULL) { ++ ret = EIO; ++ goto done; ++ } ++ ++ tmp_str = talloc_strdup(mem_ctx, tmp_name); ++ PORT_Free(tmp_name); ++ if (tmp_str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *rdn_str = tmp_str; ++ } else { ++ talloc_free(discard_const(tmp_str)); ++ } ++ PORT_FreeArena(arena, PR_FALSE); ++ ++ return ret; ++} ++ ++static int get_rdn_list(TALLOC_CTX *mem_ctx, CERTRDN **rdns, ++ const char ***rdn_list) ++{ ++ int ret; ++ size_t c; ++ const char **list = NULL; ++ ++ for (c = 0; rdns[c] != NULL; c++); ++ list = talloc_zero_array(mem_ctx, const char *, c + 1); ++ if (list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ for (c = 0; rdns[c] != NULL; c++) { ++ ret = get_rdn_str(list, rdns[c]->avas, ++ &(list[c])); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *rdn_list = list; ++ } else { ++ talloc_free(list); ++ } ++ ++ return ret; ++} ++ ++enum san_opt nss_name_type_to_san_opt(CERTGeneralNameType type) ++{ ++ switch (type) { ++ case certOtherName: ++ return SAN_OTHER_NAME; ++ case certRFC822Name: ++ return SAN_RFC822_NAME; ++ case certDNSName: ++ return SAN_DNS_NAME; ++ case certX400Address: ++ return SAN_X400_ADDRESS; ++ case certDirectoryName: ++ return SAN_DIRECTORY_NAME; ++ case certEDIPartyName: ++ return SAN_EDIPART_NAME; ++ case certURI: ++ return SAN_URI; ++ case certIPAddress: ++ return SAN_IP_ADDRESS; ++ case certRegisterID: ++ return SAN_REGISTERED_ID; ++ default: ++ return SAN_INVALID; ++ } ++} ++ ++static int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin, ++ enum san_opt san_opt, uint8_t *data, size_t len, ++ struct san_list **item) ++{ ++ struct san_list *i; ++ ++ if (data == NULL || len == 0 || san_opt == SAN_INVALID) { ++ return EINVAL; ++ } ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ return ENOMEM; ++ } ++ ++ i->san_opt = san_opt; ++ if (is_bin) { ++ i->bin_val = talloc_memdup(i, data, len); ++ i->bin_val_len = len; ++ } else { ++ i->val = talloc_strndup(i, (char *) data, len); ++ } ++ if (i->val == NULL) { ++ talloc_free(i); ++ return ENOMEM; ++ } ++ ++ *item = i; ++ ++ return 0; ++} ++ ++/* taken from pkinit_crypto_nss.c of MIT Kerberos */ ++/* KerberosString: RFC 4120, 5.2.1. */ ++static const SEC_ASN1Template kerberos_string_template[] = { ++ { ++ SEC_ASN1_GENERAL_STRING, ++ 0, ++ NULL, ++ sizeof(SECItem), ++ } ++}; ++ ++/* Realm: RFC 4120, 5.2.2. */ ++struct realm { ++ SECItem name; ++}; ++static const SEC_ASN1Template realm_template[] = { ++ { ++ SEC_ASN1_GENERAL_STRING, ++ 0, ++ NULL, ++ sizeof(SECItem), ++ } ++}; ++ ++/* PrincipalName: RFC 4120, 5.2.2. */ ++static const SEC_ASN1Template sequence_of_kerberos_string_template[] = { ++ { ++ SEC_ASN1_SEQUENCE_OF, ++ 0, ++ &kerberos_string_template, ++ 0, ++ } ++}; ++ ++struct principal_name { ++ SECItem name_type; ++ SECItem **name_string; ++}; ++static const SEC_ASN1Template principal_name_template[] = { ++ { ++ SEC_ASN1_SEQUENCE, ++ 0, ++ NULL, ++ sizeof(struct principal_name), ++ }, ++ { ++ SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT, ++ offsetof(struct principal_name, name_type), ++ &SEC_IntegerTemplate, ++ sizeof(SECItem), ++ }, ++ { ++ SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT, ++ offsetof(struct principal_name, name_string), ++ sequence_of_kerberos_string_template, ++ sizeof(struct SECItem **), ++ }, ++ {0, 0, NULL, 0}, ++}; ++ ++/* KRB5PrincipalName: RFC 4556, 3.2.2. */ ++struct kerberos_principal_name { ++ SECItem realm; ++ struct principal_name principal_name; ++}; ++static const SEC_ASN1Template kerberos_principal_name_template[] = { ++ { ++ SEC_ASN1_SEQUENCE, ++ 0, ++ NULL, ++ sizeof(struct kerberos_principal_name), ++ }, ++ { ++ SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT, ++ offsetof(struct kerberos_principal_name, realm), ++ &realm_template, ++ sizeof(struct realm), ++ }, ++ { ++ SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT, ++ offsetof(struct kerberos_principal_name, principal_name), ++ &principal_name_template, ++ sizeof(struct principal_name), ++ }, ++ {0, 0, NULL, 0} ++}; ++ ++#define PKINIT_OID "1.3.6.1.5.2.2" ++#define NT_PRINCIPAL_OID "1.3.6.1.4.1.311.20.2.3" ++ ++static int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx, ++ enum san_opt san_opt, ++ CERTGeneralName *current, ++ struct san_list **item) ++{ ++ struct san_list *i = NULL; ++ int ret; ++ char *tmp_str; ++ ++ tmp_str = CERT_GetOidString(&(current->name.OthName.oid)); ++ /* is it expexted that NSS OID strings start with "OID." but we ++ * prefer the plain dotted-decimal version so the prefix is skipped */ ++ if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) { ++ PR_smprintf_free(tmp_str); ++ return EINVAL; ++ } ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ PR_smprintf_free(tmp_str); ++ return ENOMEM; ++ } ++ i->san_opt = san_opt; ++ ++ i->other_name_oid = talloc_strdup(i, tmp_str + 4); ++ PR_smprintf_free(tmp_str); ++ if (i->other_name_oid == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ i->bin_val = talloc_memdup(i, current->name.OthName.name.data, ++ current->name.OthName.name.len); ++ if (i->bin_val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ i->bin_val_len = current->name.OthName.name.len; ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *item = i; ++ } else { ++ talloc_free(i); ++ } ++ ++ return ret; ++} ++ ++static int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name, ++ char delim, char **short_name) ++{ ++ char *at; ++ char *s; ++ ++ if (full_name == NULL || delim == '\0' || short_name == NULL) { ++ return EINVAL; ++ } ++ ++ at = strchr(full_name, delim); ++ if (at != NULL) { ++ s = talloc_strndup(mem_ctx, full_name, (at - full_name)); ++ } else { ++ s = talloc_strdup(mem_ctx, full_name); ++ } ++ if (s == NULL) { ++ return ENOMEM; ++ } ++ ++ *short_name = s; ++ ++ return 0; ++} ++ ++static int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx, ++ PLArenaPool *pool, ++ enum san_opt san_opt, ++ CERTGeneralName *current, ++ struct san_list **item) ++{ ++ struct san_list *i = NULL; ++ SECStatus rv; ++ SECItem tmp_secitem = { 0 }; ++ int ret; ++ ++ rv = SEC_ASN1DecodeItem(pool, &tmp_secitem, ++ SEC_ASN1_GET(SEC_UTF8StringTemplate), ++ &(current->name.OthName.name)); ++ if (rv != SECSuccess) { ++ return EINVAL; ++ } ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ return ENOMEM; ++ } ++ i->san_opt = san_opt; ++ ++ i->val = talloc_strndup(i, (char *) tmp_secitem.data, ++ tmp_secitem.len); ++ if (i->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = get_short_name(i, i->val, '@', &(i->short_name)); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *item = i; ++ } else { ++ talloc_free(i); ++ } ++ ++ return ret; ++} ++ ++static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx, ++ PLArenaPool *pool, ++ enum san_opt san_opt, ++ CERTGeneralName *current, ++ struct san_list **item) ++{ ++ struct san_list *i = NULL; ++ SECStatus rv; ++ struct kerberos_principal_name kname; ++ int ret; ++ size_t c; ++ ++ rv = SEC_ASN1DecodeItem(pool, &kname, ++ kerberos_principal_name_template, ++ &(current->name.OthName.name)); ++ if (rv != SECSuccess) { ++ return EINVAL; ++ } ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ return ENOMEM; ++ } ++ i->san_opt = san_opt; ++ ++ if (kname.principal_name.name_string != NULL) { ++ i->val = talloc_strdup(i, ""); ++ if (i->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ for (c = 0; kname.principal_name.name_string[c] != NULL; c++) { ++ if (c > 0) { ++ i->val = talloc_strdup_append(i->val, "/"); ++ if (i->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ i->val = talloc_strndup_append(i->val, ++ (char *) kname.principal_name.name_string[c]->data, ++ kname.principal_name.name_string[c]->len); ++ if (i->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ i->val = talloc_strndup_append(i->val, ++ (char *) kname.realm.data, ++ kname.realm.len); ++ if (i->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = get_short_name(i, i->val, '@', &(i->short_name)); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *item = i; ++ } else { ++ talloc_free(i); ++ } ++ ++ return ret; ++} ++ ++static int add_oid_to_san_list(TALLOC_CTX *mem_ctx, ++ enum san_opt san_opt, ++ SECItem oid, ++ struct san_list **item) ++{ ++ struct san_list *i = NULL; ++ char *tmp_str; ++ ++ tmp_str = CERT_GetOidString(&oid); ++ /* is it expexted that NSS OID strings start with "OID." but we ++ * prefer the plain dotted-decimal version so the prefix is skipped */ ++ if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) { ++ PR_smprintf_free(tmp_str); ++ return EINVAL; ++ } ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ PR_smprintf_free(tmp_str); ++ return ENOMEM; ++ } ++ i->san_opt = san_opt; ++ ++ i->val = talloc_strdup(i, tmp_str + 4); ++ PR_smprintf_free(tmp_str); ++ if (i->val == NULL) { ++ talloc_free(i); ++ return ENOMEM; ++ } ++ ++ *item = i; ++ return 0; ++} ++ ++static int add_rdn_list_to_san_list(TALLOC_CTX *mem_ctx, ++ enum san_opt san_opt, ++ CERTName name, ++ struct san_list **item) ++{ ++ struct san_list *i = NULL; ++ int ret; ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ return ENOMEM; ++ } ++ i->san_opt = san_opt; ++ ++ ret = get_rdn_list(i, name.rdns, &(i->rdn_list)); ++ if (ret != 0) { ++ talloc_free(i); ++ return ret; ++ } ++ ++ *item = i; ++ return 0; ++} ++ ++static int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt, ++ uint8_t *data, size_t len, ++ struct san_list **item) ++{ ++ struct san_list *i; ++ PRStatus st; ++ PRNetAddr addr; ++ char addrBuf[80]; ++ ++ if (data == NULL || len == 0 || san_opt == SAN_INVALID) { ++ return EINVAL; ++ } ++ ++ /* taken from secu_PrintIPAddress() */ ++ memset(&addr, 0, sizeof addr); ++ if (len == 4) { ++ addr.inet.family = PR_AF_INET; ++ memcpy(&addr.inet.ip, data, len); ++ } else if (len == 16) { ++ addr.ipv6.family = PR_AF_INET6; ++ memcpy(addr.ipv6.ip.pr_s6_addr, data, len); ++ if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) { ++ /* convert to IPv4. */ ++ addr.inet.family = PR_AF_INET; ++ memcpy(&addr.inet.ip, &addr.ipv6.ip.pr_s6_addr[12], 4); ++ memset(&addr.inet.pad[0], 0, sizeof addr.inet.pad); ++ } ++ } else { ++ return EINVAL; ++ } ++ ++ st = PR_NetAddrToString(&addr, addrBuf, sizeof addrBuf); ++ if (st != PR_SUCCESS) { ++ return EIO; ++ } ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ return ENOMEM; ++ } ++ ++ i->san_opt = san_opt; ++ i->val = talloc_strdup(i, addrBuf); ++ if (i->val == NULL) { ++ talloc_free(i); ++ return ENOMEM; ++ } ++ ++ *item = i; ++ return 0; ++} ++static int add_principal_to_san_list(TALLOC_CTX *mem_ctx, ++ enum san_opt san_opt, ++ const char *princ, ++ struct san_list **item) ++{ ++ struct san_list *i = NULL; ++ int ret; ++ ++ i = talloc_zero(mem_ctx, struct san_list); ++ if (i == NULL) { ++ return ENOMEM; ++ } ++ i->san_opt = san_opt; ++ ++ i->val = talloc_strdup(i, princ); ++ if (i->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = get_short_name(i, i->val, '@', &(i->short_name)); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *item = i; ++ } else { ++ talloc_free(i); ++ } ++ ++ return ret; ++} ++ ++static int get_san(TALLOC_CTX *mem_ctx, CERTCertificate *cert, ++ struct san_list **san_list) ++{ ++ ++ SECItem subAltName = { 0 }; ++ SECStatus rv; ++ CERTGeneralName *name_list = NULL; ++ CERTGeneralName *current; ++ PLArenaPool *pool = NULL; ++ int ret; ++ struct san_list *list = NULL; ++ struct san_list *item = NULL; ++ struct san_list *item_s = NULL; ++ struct san_list *item_p = NULL; ++ struct san_list *item_pb = NULL; ++ ++ rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, ++ &subAltName); ++ if (rv != SECSuccess) { ++ if (rv == SECFailure ++ && PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) { ++ ret = EOK; ++ } else { ++ ret = EIO; ++ } ++ goto done; ++ } ++ ++ pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); ++ if (pool == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ name_list = CERT_DecodeAltNameExtension(pool, &subAltName); ++ if (name_list == NULL ) { ++ ret = EIO; ++ goto done; ++ } ++ ++ current = name_list; ++ do { ++ switch (current->type) { ++ case certOtherName: ++ ret = add_string_other_name_to_san_list(mem_ctx, ++ SAN_STRING_OTHER_NAME, ++ current, &item_s); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item_s); ++ ++ item_p = NULL; ++ if (strcmp(item_s->other_name_oid, NT_PRINCIPAL_OID) == 0) { ++ ret = add_nt_princ_to_san_list(mem_ctx, pool, SAN_NT, current, ++ &item_p); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item_p); ++ } else if (strcmp(item_s->other_name_oid, PKINIT_OID) == 0) { ++ ret = add_pkinit_princ_to_san_list(mem_ctx, pool, SAN_PKINIT, ++ current, &item_p); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item_p); ++ } ++ ++ if (item_p != NULL) { ++ ret = add_principal_to_san_list(mem_ctx, SAN_PRINCIPAL, ++ item_p->val, &item_pb); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item_pb); ++ } ++ ++ break; ++ case certRFC822Name: ++ case certDNSName: ++ case certURI: ++ ret = add_to_san_list(mem_ctx, false, ++ nss_name_type_to_san_opt(current->type), ++ current->name.other.data, ++ current->name.other.len, &item); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ if (current->type == certRFC822Name ++ || current->type == certDNSName) { ++ ret = get_short_name(item, item->val, ++ (current->type == certRFC822Name ++ ? '@' : '.'), ++ &(item->short_name)); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ ++ DLIST_ADD(list, item); ++ break; ++ case certIPAddress: ++ ret = add_ip_to_san_list(mem_ctx, ++ nss_name_type_to_san_opt(current->type), ++ current->name.other.data, ++ current->name.other.len, &item); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item); ++ break; ++ case certDirectoryName: ++ ret = add_rdn_list_to_san_list(mem_ctx, ++ nss_name_type_to_san_opt(current->type), ++ current->name.directoryName, &item); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item); ++ break; ++ case certRegisterID: ++ ret = add_oid_to_san_list(mem_ctx, ++ nss_name_type_to_san_opt(current->type), ++ current->name.other, &item); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item); ++ break; ++ case certX400Address: ++ case certEDIPartyName: ++ ret = add_to_san_list(mem_ctx, true, ++ nss_name_type_to_san_opt(current->type), ++ current->name.other.data, ++ current->name.other.len, &item); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(list, item); ++ break; ++ default: ++ ret = EINVAL; ++ } ++ ++ current = CERT_GetNextGeneralName(current); ++ if (current == NULL) { ++ ret = EIO; ++ goto done; ++ } ++ } while (current != name_list); ++ ++done: ++ ++ /* Don't free nameList, it's part of the arena. */ ++ ++ if (pool != NULL) { ++ PORT_FreeArena(pool, PR_FALSE); ++ } ++ ++ if (subAltName.data != NULL) { ++ SECITEM_FreeItem(&subAltName, PR_FALSE); ++ } ++ ++ if (ret == EOK) { ++ *san_list = list; ++ } ++ return ret; ++} ++ ++int sss_cert_get_content(TALLOC_CTX *mem_ctx, ++ const uint8_t *der_blob, size_t der_size, ++ struct sss_cert_content **content) ++{ ++ int ret; ++ struct sss_cert_content *cont = NULL; ++ CERTCertDBHandle *handle; ++ CERTCertificate *cert = NULL; ++ SECItem der_item; ++ NSSInitContext *nss_ctx; ++ ++ if (der_blob == NULL || der_size == 0) { ++ return EINVAL; ++ } ++ ++ nss_ctx = NSS_InitContext("", "", "", "", NULL, NSS_INIT_READONLY ++ | NSS_INIT_NOCERTDB ++ | NSS_INIT_NOMODDB ++ | NSS_INIT_FORCEOPEN ++ | NSS_INIT_NOROOTINIT ++ | NSS_INIT_OPTIMIZESPACE); ++ if (nss_ctx == NULL) { ++ return EIO; ++ } ++ ++ cont = talloc_zero(mem_ctx, struct sss_cert_content); ++ if (cont == NULL) { ++ return ENOMEM; ++ } ++ ++ handle = CERT_GetDefaultCertDB(); ++ der_item.len = der_size; ++ der_item.data = discard_const(der_blob); ++ ++ cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE); ++ if (cert == NULL) { ++ ret = EINVAL; ++ goto done; ++ } ++ ++ cont->issuer_str = talloc_strdup(cont, cert->issuerName); ++ if (cont->issuer_str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = get_rdn_list(cont, cert->issuer.rdns, &cont->issuer_rdn_list); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ cont->subject_str = talloc_strdup(cont, cert->subjectName); ++ if (cont->subject_str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = get_rdn_list(cont, cert->subject.rdns, &cont->subject_rdn_list); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ ++ cont->key_usage = cert->keyUsage; ++ ++ ret = get_extended_key_usage_oids(cont, cert, ++ &(cont->extended_key_usage_oids)); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ ret = get_san(cont, cert, &(cont->san_list)); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ cont->cert_der = talloc_memdup(cont, der_blob, der_size); ++ if (cont->cert_der == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cont->cert_der_size = der_size; ++ ret = EOK; ++ ++done: ++ ++ CERT_DestroyCertificate(cert); ++ NSS_ShutdownContext(nss_ctx); ++ ++ if (ret == EOK) { ++ *content = cont; ++ } else { ++ talloc_free(cont); ++ } ++ ++ return ret; ++} +diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..080cab8ab585ce64a88c352a23a8062887fa720a +--- /dev/null ++++ b/src/lib/certmap/sss_certmap.c +@@ -0,0 +1,993 @@ ++/* ++ SSSD ++ ++ Library for rule based certificate to user mapping ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "util/util.h" ++#include "util/cert.h" ++#include "util/crypto/sss_crypto.h" ++#include "lib/certmap/sss_certmap.h" ++#include "lib/certmap/sss_certmap_int.h" ++ ++int debug_level; ++void sss_debug_fn(const char *file, ++ long line, ++ const char *function, ++ int level, ++ const char *format, ...) ++{ ++ return; ++} ++ ++static int get_type_prefix(TALLOC_CTX *mem_ctx, const char *match_rule, ++ char **type, const char **rule_start) ++{ ++ const char *c; ++ char *delim; ++ ++ *type = NULL; ++ *rule_start = match_rule; ++ ++ delim = strchr(match_rule, ':'); ++ if (delim == NULL) { ++ /* no type prefix found */ ++ return 0; ++ } ++ ++ /* rule starts with ':', empty type */ ++ if (delim == match_rule) { ++ *rule_start = delim + 1; ++ return EOK; ++ } ++ ++ for (c = match_rule; c < delim; c++) { ++ /* type prefix may only contain digits and upper-case ASCII characters */ ++ if (!(isascii(*c) && (isdigit(*c) || isupper(*c)))) { ++ /* no type prefix found */ ++ return 0; ++ } ++ } ++ ++ *rule_start = delim + 1; ++ *type = talloc_strndup(mem_ctx, match_rule, (delim - match_rule)); ++ if (*type == NULL) { ++ return ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int parse_match_rule(struct sss_certmap_ctx *ctx, const char *match_rule, ++ struct krb5_match_rule **parsed_match_rule) ++{ ++ int ret; ++ char *type; ++ const char *rule_start; ++ ++ ret = get_type_prefix(ctx, match_rule, &type, &rule_start); ++ if (ret != EOK) { ++ CM_DEBUG(ctx, "Failed to read rule type."); ++ goto done; ++ } ++ ++ if (type == NULL || strcmp(type, "KRB5") == 0) { ++ ret = parse_krb5_match_rule(ctx, rule_start, parsed_match_rule); ++ if (ret != EOK) { ++ CM_DEBUG(ctx, "Failed to parse KRB5 matching rule."); ++ goto done; ++ } ++ } else { ++ CM_DEBUG(ctx, "Unsupported matching rule type."); ++ ret = ESRCH; ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(type); ++ ++ return ret; ++} ++ ++static int parse_mapping_rule(struct sss_certmap_ctx *ctx, ++ const char *mapping_rule, ++ struct ldap_mapping_rule **parsed_mapping_rule) ++{ ++ int ret; ++ char *type; ++ const char *rule_start; ++ ++ ret = get_type_prefix(ctx, mapping_rule, &type, &rule_start); ++ if (ret != EOK) { ++ CM_DEBUG(ctx, "Failed to read rule type."); ++ goto done; ++ } ++ ++ if (type == NULL || strcmp(type, "LDAP") == 0) { ++ ret = parse_ldap_mapping_rule(ctx, rule_start, parsed_mapping_rule); ++ if (ret != EOK) { ++ CM_DEBUG(ctx, "Failed to parse LDAP mapping rule."); ++ goto done; ++ } ++ } else { ++ CM_DEBUG(ctx, "Unsupported mapping rule type."); ++ ret = ESRCH; ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(type); ++ ++ return ret; ++} ++ ++int sss_certmap_add_rule(struct sss_certmap_ctx *ctx, ++ uint32_t priority, const char *match_rule, ++ const char *map_rule, const char **domains) ++{ ++ size_t c; ++ int ret; ++ struct match_map_rule *rule; ++ struct TALLOC_CTX *tmp_ctx; ++ struct priority_list *p; ++ struct priority_list *p_new; ++ struct krb5_match_rule *parsed_match_rule; ++ struct ldap_mapping_rule *parsed_mapping_rule; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ rule = talloc_zero(tmp_ctx, struct match_map_rule); ++ if (rule == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ rule->priority = priority; ++ ++ if (match_rule == NULL) { ++ match_rule = DEFAULT_MATCH_RULE; ++ } ++ ret = parse_match_rule(ctx, match_rule, &parsed_match_rule); ++ if (ret == 0) { ++ rule->parsed_match_rule = talloc_steal(rule, parsed_match_rule); ++ rule->match_rule = talloc_strdup(rule, match_rule); ++ if (rule->match_rule == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } else if (ret == ESRCH) { ++ /* report unsupported rules */ ++ goto done; ++ } else { ++ goto done; ++ } ++ ++ if (map_rule == NULL) { ++ map_rule = DEFAULT_MAP_RULE; ++ } ++ ret = parse_mapping_rule(ctx, map_rule, &parsed_mapping_rule); ++ if (ret == 0) { ++ rule->parsed_mapping_rule = talloc_steal(rule, parsed_mapping_rule); ++ rule->map_rule = talloc_strdup(rule, map_rule); ++ if (rule->map_rule == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } else if (ret == ESRCH) { ++ /* report unsupported rules */ ++ goto done; ++ } else { ++ goto done; ++ } ++ ++ if (domains != NULL && *domains != NULL) { ++ for (c = 0; domains[c] != NULL; c++); ++ rule->domains = talloc_zero_array(rule, char *, c + 1); ++ if (rule->domains == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ for (c = 0; domains[c] != NULL; c++) { ++ rule->domains[c] = talloc_strdup(rule->domains, domains[c]); ++ if (rule->domains[c] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ ++ if (ctx->prio_list == NULL) { ++ ctx->prio_list = talloc_zero(ctx, struct priority_list); ++ if (ctx->prio_list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ctx->prio_list->priority = rule->priority; ++ ctx->prio_list->rule_list = rule; ++ } else { ++ for (p = ctx->prio_list; p != NULL && p->priority < rule->priority; ++ p = p->next); ++ if (p != NULL && p->priority == priority) { ++ DLIST_ADD(p->rule_list, rule); ++ } else { ++ p_new = talloc_zero(ctx, struct priority_list); ++ if (p_new == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ p_new->priority = rule->priority; ++ p_new->rule_list = rule; ++ ++ if (p == NULL) { ++ DLIST_ADD_END(ctx->prio_list, p_new, struct priority_list *); ++ } else if (p->prev == NULL) { ++ DLIST_ADD(ctx->prio_list, p_new); ++ } else { ++ DLIST_ADD_AFTER(ctx->prio_list, p_new, p->prev); ++ } ++ } ++ } ++ ++ talloc_steal(ctx, rule); ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ ++static int expand_cert(struct sss_certmap_ctx *ctx, ++ struct parsed_template *parsed_template, ++ struct sss_cert_content *cert_content, ++ char **expanded) ++{ ++ int ret; ++ char *tmp_str = NULL; ++ ++ if (parsed_template->conversion == NULL ++ || strcmp(parsed_template->conversion, "bin") == 0) { ++ ret = bin_to_ldap_filter_value(ctx, cert_content->cert_der, ++ cert_content->cert_der_size, &tmp_str); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "bin conversion failed."); ++ goto done; ++ } ++ } else if (strcmp(parsed_template->conversion, "base64") == 0) { ++ tmp_str = sss_base64_encode(ctx, cert_content->cert_der, ++ cert_content->cert_der_size); ++ if (tmp_str == NULL) { ++ CM_DEBUG(ctx, "base64 conversion failed."); ++ ret = ENOMEM; ++ goto done; ++ } ++ } else { ++ CM_DEBUG(ctx, "Unsupported conversion."); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *expanded = tmp_str; ++ } else { ++ talloc_free(tmp_str); ++ } ++ ++ return ret; ++} ++ ++static int get_dn_str(struct sss_certmap_ctx *ctx, const char *conversion, ++ const char **rdn_list, char **result) ++{ ++ char *str = NULL; ++ size_t c; ++ int ret; ++ char *conv = NULL; ++ ++ str = talloc_strdup(ctx, ""); ++ if (str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ if (conversion == NULL || strcmp(conversion, "nss_ldap") == 0 ++ || strcmp(conversion, "nss") == 0) { ++ for (c = 0; rdn_list[c] != NULL; c++); ++ while (c != 0) { ++ c--; ++ str = talloc_asprintf_append(str, "%s%s", ++ (rdn_list[c + 1] == NULL) ? "" : ",", ++ rdn_list[c]); ++ if (str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ }; ++ } else if (strcmp(conversion, "ad_ldap") == 0) { ++ for (c = 0; rdn_list[c] != NULL; c++); ++ while (c != 0) { ++ c--; ++ conv = check_ad_attr_name(str, rdn_list[c]); ++ str = talloc_asprintf_append(str, "%s%s", ++ (rdn_list[c + 1] == NULL) ? "" : ",", ++ conv == NULL ? rdn_list[c] : conv); ++ talloc_free(conv); ++ conv = NULL; ++ if (str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ }; ++ } else if (strcmp(conversion, "nss_x500") == 0) { ++ for (c = 0; rdn_list[c] != NULL; c++) { ++ str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",", ++ rdn_list[c]); ++ if (str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } else if (strcmp(conversion, "ad_x500") == 0 ++ || strcmp(conversion, "ad") == 0) { ++ for (c = 0; rdn_list[c] != NULL; c++) { ++ conv = check_ad_attr_name(str, rdn_list[c]); ++ str = talloc_asprintf_append(str, "%s%s", ++ (c == 0) ? "" : ",", ++ conv == NULL ? rdn_list[c] : conv); ++ talloc_free(conv); ++ conv = NULL; ++ if (str == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } else { ++ ret = EINVAL; ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *result = str; ++ } else { ++ talloc_free(str); ++ } ++ ++ return ret; ++} ++ ++static int expand_san_blob(struct sss_certmap_ctx *ctx, enum san_opt san_opt, ++ struct san_list *san_list, char **expanded) ++{ ++ struct san_list *item; ++ char *exp; ++ int ret; ++ ++ DLIST_FOR_EACH(item, san_list) { ++ if (item->san_opt == san_opt) { ++ ret = bin_to_ldap_filter_value(ctx, item->bin_val, ++ item->bin_val_len, &exp); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "bin conversion failed."); ++ return ret; ++ } ++ ++ *expanded = exp; ++ return 0; ++ } ++ } ++ ++ return ENOENT; ++} ++ ++static int expand_san_string(struct sss_certmap_ctx *ctx, enum san_opt san_opt, ++ struct san_list *san_list, const char *attr_name, ++ char **expanded) ++{ ++ struct san_list *item; ++ char *exp; ++ ++ DLIST_FOR_EACH(item, san_list) { ++ if (item->san_opt == san_opt) { ++ if (attr_name == NULL) { ++ exp = talloc_strdup(ctx, item->val); ++ } else if (strcasecmp(attr_name, "short_name") == 0) { ++ exp = talloc_strdup(ctx, item->short_name); ++ } else { ++ CM_DEBUG(ctx, "Unsupported attribute name [%s].", attr_name); ++ return EINVAL; ++ } ++ ++ if (exp == NULL) { ++ return ENOMEM; ++ } ++ ++ *expanded = exp; ++ return 0; ++ } ++ } ++ ++ return ENOENT; ++} ++ ++static int expand_san_rdn_list(struct sss_certmap_ctx *ctx, ++ enum san_opt san_opt, ++ struct san_list *san_list, ++ const char *conversion, ++ char **expanded) ++{ ++ struct san_list *item; ++ char *exp; ++ int ret; ++ ++ DLIST_FOR_EACH(item, san_list) { ++ if (item->san_opt == san_opt) { ++ ret = get_dn_str(ctx, conversion, item->rdn_list, &exp); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ *expanded = exp; ++ return 0; ++ } ++ } ++ ++ return ENOENT; ++} ++ ++ ++static int expand_san(struct sss_certmap_ctx *ctx, ++ struct parsed_template *parsed_template, ++ struct san_list *san_list, ++ char **expanded) ++{ ++ int ret; ++ ++ if (strcmp("subject_rfc822_name", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_RFC822_NAME, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_dns_name", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_DNS_NAME, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_x400_address", parsed_template->name) == 0) { ++ ret = expand_san_blob(ctx, SAN_X400_ADDRESS, san_list, expanded); ++ } else if (strcmp("subject_directory_name", parsed_template->name) == 0) { ++ ret = expand_san_rdn_list(ctx, SAN_DIRECTORY_NAME, san_list, ++ parsed_template->conversion, expanded); ++ } else if (strcmp("subject_ediparty_name", parsed_template->name) == 0) { ++ ret = expand_san_blob(ctx, SAN_EDIPART_NAME, san_list, expanded); ++ } else if (strcmp("subject_uri", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_URI, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_ip_address", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_IP_ADDRESS, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_registered_id", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_REGISTERED_ID, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_pkinit_principal", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_PKINIT, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_nt_principal", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_NT, san_list, ++ parsed_template->attr_name, expanded); ++ } else if (strcmp("subject_principal", parsed_template->name) == 0) { ++ ret = expand_san_string(ctx, SAN_PRINCIPAL, san_list, ++ parsed_template->attr_name, expanded); ++ } else { ++ CM_DEBUG(ctx, "Unsupported template name [%s].n", ++ parsed_template->name); ++ ret = EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int expand_template(struct sss_certmap_ctx *ctx, ++ struct parsed_template *parsed_template, ++ struct sss_cert_content *cert_content, ++ char **expanded) ++{ ++ int ret; ++ char *exp = NULL; ++ ++ if (strcmp("issuer_dn", parsed_template->name) == 0) { ++ ret = get_dn_str(ctx, parsed_template->conversion, ++ cert_content->issuer_rdn_list, &exp); ++ } else if (strcmp("subject_dn", parsed_template->name) == 0) { ++ ret = get_dn_str(ctx, parsed_template->conversion, ++ cert_content->subject_rdn_list, &exp); ++ } else if (strncmp("subject_", parsed_template->name, 8) == 0) { ++ ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp); ++ } else if (strcmp("cert", parsed_template->name) == 0) { ++ ret = expand_cert(ctx, parsed_template, cert_content, &exp); ++ } else { ++ CM_DEBUG(ctx, "Unsupported template name."); ++ ret = EINVAL; ++ goto done; ++ } ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to expand [%s] template.", parsed_template->name); ++ goto done; ++ } ++ ++ if (exp == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *expanded = exp; ++ } else { ++ talloc_free(exp); ++ } ++ ++ return ret; ++} ++ ++static int get_filter(struct sss_certmap_ctx *ctx, ++ struct ldap_mapping_rule *parsed_mapping_rule, ++ struct sss_cert_content *cert_content, ++ char **filter) ++{ ++ struct ldap_mapping_rule_comp *comp; ++ char *result = NULL; ++ char *expanded = NULL; ++ int ret; ++ ++ result = talloc_strdup(ctx, ""); ++ if (result == NULL) { ++ return ENOMEM; ++ } ++ ++ for (comp = parsed_mapping_rule->list; comp != NULL; comp = comp->next) { ++ if (comp->type == comp_string) { ++ result = talloc_strdup_append(result, comp->val); ++ } else if (comp->type == comp_template) { ++ ret = expand_template(ctx, comp->parsed_template, cert_content, ++ &expanded); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to expanded template."); ++ goto done; ++ } ++ ++ result = talloc_strdup_append(result, expanded); ++ talloc_free(expanded); ++ expanded = NULL; ++ if (result == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } else { ++ ret = EINVAL; ++ CM_DEBUG(ctx, "Unsupported component type."); ++ goto done; ++ } ++ } ++ ++ ret = 0; ++done: ++ talloc_free(expanded); ++ if (ret == 0) { ++ *filter = result; ++ } else { ++ talloc_free(result); ++ } ++ ++ return ret; ++} ++ ++static bool check_san_regexp(struct sss_certmap_ctx *ctx, ++ enum san_opt san_opt, regex_t regexp, ++ struct san_list *san_list) ++{ ++ struct san_list *item; ++ bool match = false; ++ int ret; ++ char *tmp_str = NULL; ++ ++ DLIST_FOR_EACH(item, san_list) { ++ if (item->san_opt == san_opt) { ++ if (item->san_opt == SAN_DIRECTORY_NAME) { ++ /* use LDAP order for matching */ ++ ret = get_dn_str(ctx, NULL, item->rdn_list, &tmp_str); ++ if (ret != 0 || tmp_str == NULL) { ++ return false; ++ } ++ match = (regexec(®exp, tmp_str, 0, NULL, 0) == 0); ++ talloc_free(tmp_str); ++ } else { ++ match = (item->val != NULL ++ && regexec(®exp, item->val, 0, NULL, 0) == 0); ++ } ++ if (!match) { ++ return false; ++ } ++ } ++ } ++ ++ return match; ++} ++ ++static bool check_san_blob(enum san_opt san_opt, ++ uint8_t *bin_val, size_t bin_val_len, ++ struct san_list *san_list) ++{ ++ struct san_list *item; ++ bool match = false; ++ ++ if (bin_val == NULL || bin_val_len == 0) { ++ return false; ++ } ++ ++ DLIST_FOR_EACH(item, san_list) { ++ if (item->san_opt == san_opt) { ++ match = (item->bin_val != NULL && item->bin_val_len != 0 ++ && memmem(item->bin_val, item->bin_val_len, ++ bin_val, bin_val_len) != NULL); ++ if (!match) { ++ return false; ++ } ++ } ++ } ++ ++ return match; ++} ++ ++static bool check_san_str_other_name(enum san_opt san_opt, ++ const char *str_other_name_oid, ++ regex_t regexp, ++ struct san_list *san_list) ++{ ++ struct san_list *item; ++ bool match = false; ++ char *tmp_str; ++ ++ if (str_other_name_oid == NULL) { ++ return false; ++ } ++ ++ DLIST_FOR_EACH(item, san_list) { ++ if (item->san_opt == san_opt ++ && strcmp(item->other_name_oid, str_other_name_oid) == 0) { ++ match = false; ++ if (item->bin_val != NULL && item->bin_val_len != 0) { ++ tmp_str = talloc_strndup(item, (char *) item->bin_val, ++ item->bin_val_len); ++ if (tmp_str != NULL) { ++ match = (regexec(®exp, tmp_str, 0, NULL, 0) == 0); ++ } ++ talloc_free(tmp_str); ++ } ++ if (!match) { ++ return false; ++ } ++ } ++ } ++ ++ return match; ++} ++ ++static bool do_san_match(struct sss_certmap_ctx *ctx, ++ struct component_list *comp, ++ struct san_list *san_list) ++{ ++ switch (comp->san_opt) { ++ case SAN_OTHER_NAME: ++ return check_san_blob(SAN_STRING_OTHER_NAME, ++ comp->bin_val, comp->bin_val_len, ++ san_list); ++ break; ++ case SAN_X400_ADDRESS: ++ case SAN_EDIPART_NAME: ++ return check_san_blob(comp->san_opt, comp->bin_val, comp->bin_val_len, ++ san_list); ++ break; ++ case SAN_RFC822_NAME: ++ case SAN_DNS_NAME: ++ case SAN_DIRECTORY_NAME: ++ case SAN_URI: ++ case SAN_IP_ADDRESS: ++ case SAN_REGISTERED_ID: ++ case SAN_PKINIT: ++ case SAN_NT: ++ case SAN_PRINCIPAL: ++ return check_san_regexp(ctx, comp->san_opt, comp->regexp, san_list); ++ break; ++ case SAN_STRING_OTHER_NAME: ++ return check_san_str_other_name(comp->san_opt, comp->str_other_name_oid, ++ comp->regexp, san_list); ++ break; ++ default: ++ CM_DEBUG(ctx, "Unsupported SAN option [%d].", comp->san_opt); ++ return false; ++ } ++} ++ ++static int do_match(struct sss_certmap_ctx *ctx, ++ struct krb5_match_rule *parsed_match_rule, ++ struct sss_cert_content *cert_content) ++{ ++ struct component_list *comp; ++ bool match = false; ++ size_t c; ++ ++ if (parsed_match_rule == NULL || cert_content == NULL) { ++ return EINVAL; ++ } ++ ++ /* Issuer */ ++ for (comp = parsed_match_rule->issuer; comp != NULL; comp = comp->next) { ++ match = (cert_content->issuer_str != NULL ++ && regexec(&(comp->regexp), cert_content->issuer_str, ++ 0, NULL, 0) == 0); ++ if (match && parsed_match_rule->r == relation_or) { ++ /* match */ ++ return 0; ++ } else if (!match && parsed_match_rule->r == relation_and) { ++ /* no match */ ++ return ENOENT; ++ } ++ ++ } ++ ++ /* Subject */ ++ for (comp = parsed_match_rule->subject; comp != NULL; comp = comp->next) { ++ match = (cert_content->subject_str != NULL ++ && regexec(&(comp->regexp), cert_content->subject_str, ++ 0, NULL, 0) == 0); ++ if (match && parsed_match_rule->r == relation_or) { ++ /* match */ ++ return 0; ++ } else if (!match && parsed_match_rule->r == relation_and) { ++ /* no match */ ++ return ENOENT; ++ } ++ ++ } ++ ++ /* Key Usage */ ++ for (comp = parsed_match_rule->ku; comp != NULL; comp = comp->next) { ++ match = ((cert_content->key_usage & comp->ku) == comp->ku); ++ if (match && parsed_match_rule->r == relation_or) { ++ /* match */ ++ return 0; ++ } else if (!match && parsed_match_rule->r == relation_and) { ++ /* no match */ ++ return ENOENT; ++ } ++ } ++ ++ /* Extended Key Usage */ ++ for (comp = parsed_match_rule->eku; comp != NULL; comp = comp->next) { ++ for (c = 0; comp->eku_oid_list[c] != NULL; c++) { ++ match = string_in_list(comp->eku_oid_list[c], ++ discard_const( ++ cert_content->extended_key_usage_oids), ++ true); ++ if (match && parsed_match_rule->r == relation_or) { ++ /* match */ ++ return 0; ++ } else if (!match && parsed_match_rule->r == relation_and) { ++ /* no match */ ++ return ENOENT; ++ } ++ } ++ } ++ ++ /* SAN */ ++ for (comp = parsed_match_rule->san; comp != NULL; comp = comp->next) { ++ match = do_san_match(ctx, comp, cert_content->san_list); ++ if (match && parsed_match_rule->r == relation_or) { ++ /* match */ ++ return 0; ++ } else if (!match && parsed_match_rule->r == relation_and) { ++ /* no match */ ++ return ENOENT; ++ } ++ } ++ ++ if (match) { ++ /* match */ ++ return 0; ++ } ++ ++ /* no match */ ++ return ENOENT; ++} ++ ++int sss_certmap_match_cert(struct sss_certmap_ctx *ctx, ++ const uint8_t *der_cert, size_t der_size) ++{ ++ int ret; ++ struct match_map_rule *r; ++ struct priority_list *p; ++ struct sss_cert_content *cert_content = NULL; ++ ++ ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to get certificate content."); ++ return ret; ++ } ++ ++ if (ctx->prio_list == NULL) { ++ /* Match all certificates if there are no rules applied */ ++ ret = 0; ++ goto done; ++ } ++ ++ for (p = ctx->prio_list; p != NULL; p = p->next) { ++ for (r = p->rule_list; r != NULL; r = r->next) { ++ ret = do_match(ctx, r->parsed_match_rule, cert_content); ++ if (ret == 0) { ++ /* match */ ++ goto done; ++ } ++ } ++ } ++ ++ ret = ENOENT; ++done: ++ talloc_free(cert_content); ++ ++ return ret; ++} ++ ++int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, ++ const uint8_t *der_cert, size_t der_size, ++ char **_filter, char ***_domains) ++{ ++ int ret; ++ struct match_map_rule *r; ++ struct priority_list *p; ++ struct sss_cert_content *cert_content = NULL; ++ char *filter = NULL; ++ char **domains = NULL; ++ size_t c; ++ ++ if (_filter == NULL || _domains == NULL) { ++ return EINVAL; ++ } ++ ++ ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to get certificate content [%d].", ret); ++ return ret; ++ } ++ ++ if (ctx->prio_list == NULL) { ++ if (ctx->default_mapping_rule == NULL) { ++ CM_DEBUG(ctx, "No matching or mapping rules available."); ++ return EINVAL; ++ } ++ ++ ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, &filter); ++ goto done; ++ } ++ ++ for (p = ctx->prio_list; p != NULL; p = p->next) { ++ for (r = p->rule_list; r != NULL; r = r->next) { ++ ret = do_match(ctx, r->parsed_match_rule, cert_content); ++ if (ret == 0) { ++ /* match */ ++ ret = get_filter(ctx, r->parsed_mapping_rule, cert_content, ++ &filter); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to get filter"); ++ goto done; ++ } ++ ++ if (r->domains != NULL) { ++ for (c = 0; r->domains[c] != NULL; c++); ++ domains = talloc_zero_array(ctx, char *, c + 1); ++ if (domains == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (c = 0; r->domains[c] != NULL; c++) { ++ domains[c] = talloc_strdup(domains, r->domains[c]); ++ if (domains[c] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ ++ ret = 0; ++ goto done; ++ } ++ } ++ } ++ ++ ret = ENOENT; ++ ++done: ++ talloc_free(cert_content); ++ if (ret == 0) { ++ *_filter = filter; ++ *_domains = domains; ++ } else { ++ talloc_free(filter); ++ talloc_free(domains); ++ } ++ ++ return ret; ++} ++ ++int sss_certmap_init(TALLOC_CTX *mem_ctx, ++ sss_certmap_ext_debug *debug, void *debug_priv, ++ struct sss_certmap_ctx **ctx) ++{ ++ int ret; ++ ++ if (ctx == NULL) { ++ return EINVAL; ++ } ++ ++ *ctx = talloc_zero(mem_ctx, struct sss_certmap_ctx); ++ if (*ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ (*ctx)->debug = debug; ++ (*ctx)->debug_priv = debug_priv; ++ ++ ret = parse_mapping_rule(*ctx, DEFAULT_MAP_RULE, ++ &((*ctx)->default_mapping_rule)); ++ if (ret != 0) { ++ CM_DEBUG((*ctx), "Failed to parse default mapping rule."); ++ talloc_free(*ctx); ++ *ctx = NULL; ++ return ret; ++ } ++ ++ CM_DEBUG((*ctx), "sss_certmap initialized."); ++ return EOK; ++} ++ ++void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx) ++{ ++ talloc_free(ctx); ++} ++ ++void sss_certmap_free_filter_and_domains(char *filter, char **domains) ++{ ++ talloc_free(filter); ++ talloc_free(domains); ++} +diff --git a/src/lib/certmap/sss_certmap.doxy.in b/src/lib/certmap/sss_certmap.doxy.in +new file mode 100644 +index 0000000000000000000000000000000000000000..e8959e2099a0f517c314833e47d410306fb02702 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap.doxy.in +@@ -0,0 +1,3 @@ ++PROJECT_NAME = sss_certmap ++OUTPUT_DIRECTORY = certmap_doc ++INPUT = @abs_top_srcdir@/src/lib/certmap/sss_certmap.h +diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports +new file mode 100644 +index 0000000000000000000000000000000000000000..8b5d5366697401649547c09c6b6f3db571c2b518 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap.exports +@@ -0,0 +1,13 @@ ++SSS_CERTMAP_0.0 { ++ global: ++ sss_certmap_init; ++ sss_certmap_free_ctx; ++ sss_certmap_err_msg; ++ sss_certmap_add_rule; ++ sss_certmap_match_cert; ++ sss_certmap_get_search_filter; ++ sss_cert_get_content; ++ sss_certmap_free_filter_and_domains; ++ local: ++ *; ++}; +diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h +new file mode 100644 +index 0000000000000000000000000000000000000000..55485cc35e8bd7cf2cb2b0c5a06a7521025e3c43 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap.h +@@ -0,0 +1,155 @@ ++/* ++ SSSD ++ ++ Library for rule based certificate to user mapping ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef _SSS_CERTMAP_H_ ++#define _SSS_CERTMAP_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/** ++ * @defgroup sss_certmap Allow rule-based mapping of certificates to users ++ * Libsss_certmap provides a mechanism to map X509 certificate to users based ++ * on rules. ++ * @{ ++ */ ++ ++/** ++ * Opaque type for the idmap context ++ */ ++struct sss_certmap_ctx; ++ ++/** ++ * Lowest priority of a rule ++ */ ++#define SSS_CERTMAP_MIN_PRIO UINT32_MAX ++ ++/** ++ * Typedef for external debug callback ++ */ ++typedef void (sss_certmap_ext_debug)(void *pvt, ++ const char *file, long line, ++ const char *function, ++ const char *format, ...); ++/** ++ * @brief Initialize certmap context ++ * ++ * @param[in] mem_ctx Talloc memory context, may be NULL ++ * @param[in] debug Callback to handle debug output, may be NULL ++ * @param[in] debug_priv Private data for debugging callback, may be NULL ++ * @param[out] ctx New certmap context ++ * ++ * @return ++ * - 0: success ++ * - ENOMEM: failed to allocate internal Talloc context ++ * - EINVAL: ctx is NULL ++ */ ++int sss_certmap_init(TALLOC_CTX *mem_ctx, ++ sss_certmap_ext_debug *debug, void *debug_priv, ++ struct sss_certmap_ctx **ctx); ++ ++/** ++ * @brief Free certmap context ++ * ++ * @param[in] ctx certmap context previously initialized with ++ * @ref sss_certmap_init, may be NULL ++ */ ++void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx); ++ ++/** ++ * @brief Add a rule to the certmap context ++ * ++ * @param[in] ctx certmap context previously initialized with ++ * @ref sss_certmap_init ++ * @param[in] priority priority of the rule, 0 is the hightest priority, the ++ * lowest is SSS_CERTMAP_MIN_PRIO ++ * @param[in] match_rule String with the matching rule ++ * @param[in] map_rule String with the mapping rule ++ * @param[in] domains NULL-terminated string array with a list of domains ++ * the rule should be valid for, i.e. only this domains ++ * should be searched for matching users ++ * ++ * @return ++ * - 0: success ++ */ ++int sss_certmap_add_rule(struct sss_certmap_ctx *ctx, ++ uint32_t priority, const char *match_rule, ++ const char *map_rule, const char **domains); ++ ++/** ++ * @brief Check if a certificate matches any of the applied rules ++ * ++ * @param[in] ctx certmap context previously initialized with ++ * @ref sss_certmap_init ++ * @param[in] der_cert binary blog with the DER encoded certificate ++ * @param[in] der_size size of the certificate blob ++ * ++ * @return ++ * - 0: certificate matches a rule ++ * - ENOENT: certificate does not match ++ * - EINVAL: internal error ++ */ ++int sss_certmap_match_cert(struct sss_certmap_ctx *ctx, ++ const uint8_t *der_cert, size_t der_size); ++ ++/** ++ * @brief Get the LDAP filter string for a certificate ++ * ++ * @param[in] ctx certmap context previously initialized with ++ * @ref sss_certmap_init ++ * @param[in] der_cert binary blog with the DER encoded certificate ++ * @param[in] der_size size of the certificate blob ++ * @param[out] filter LDAP filter string, caller should free the data by ++ * calling sss_certmap_free_filter_and_domains ++ * @param[out] domains NULL-terminated array of strings with the domains the ++ * rule applies, caller should free the data by calling ++ * sss_certmap_free_filter_and_domains ++ * ++ * @return ++ * - 0: certificate matches a rule ++ * - ENOENT: certificate does not match ++ * - EINVAL: internal error ++ */ ++int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, ++ const uint8_t *der_cert, size_t der_size, ++ char **filter, char ***domains); ++ ++/** ++ * @brief Free data returned by @ref sss_certmap_get_search_filter ++ * ++ * @param[in] filter LDAP filter strings returned by ++ * sss_certmap_get_search_filter ++ * @param[in] domains string array of domains returned by ++ * sss_certmap_get_search_filter ++ */ ++void sss_certmap_free_filter_and_domains(char *filter, char **domains); ++ ++/** ++ * @} ++ */ ++#endif /* _SSS_CERTMAP_H_ */ +diff --git a/src/lib/certmap/sss_certmap.pc.in b/src/lib/certmap/sss_certmap.pc.in +new file mode 100644 +index 0000000000000000000000000000000000000000..f1a4432fce8ccd5642a622ca6a8d3a7954fc7ba3 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap.pc.in +@@ -0,0 +1,11 @@ ++prefix=@prefix@ ++exec_prefix=@exec_prefix@ ++libdir=@libdir@ ++includedir=@includedir@ ++ ++Name: sss_certmap ++Description: SSS certificate mapping library ++Version: @VERSION@ ++Libs: -L${libdir} -lsss_certmap ++Cflags: ++URL: https://pagure.io/SSSD/sssd/ +diff --git a/src/lib/certmap/sss_certmap_attr_names.c b/src/lib/certmap/sss_certmap_attr_names.c +new file mode 100644 +index 0000000000000000000000000000000000000000..a28a464910728cdd1f316b2b979da84f440685ea +--- /dev/null ++++ b/src/lib/certmap/sss_certmap_attr_names.c +@@ -0,0 +1,107 @@ ++/* ++ SSSD ++ ++ Library for rule based certificate to user mapping - Attribute name ++ mapping for different implementations ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++/* NSS data taken from nss-utils:nss/lib/util/secoid.c and ++ * nss:nss/lib/certdb/alg1485.c */ ++ ++/* AD data taken from ++ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376556%28v=vs.85%29.aspx ++ * and wine source code dlls/crypt32/oid.c  and include/wincrypt.h . */ ++ ++#include ++#include ++#include ++ ++struct oid_attr_name_map { ++ bool nss_ad_differ; ++ const char *oid; ++ const char *nss; ++ const char *ad; ++} oid_attr_name_map[] = { ++ { false, "2.5.4.3", "CN", "CN"}, ++ { true, "2.5.4.8", "ST", "S"}, ++ { false, "2.5.4.10", "O", "O"}, ++ { false, "2.5.4.11", "OU", "OU"}, ++ { false, "2.5.4.46", "dnQualifier", "dnQualifier"}, ++ { false, "2.5.4.6", "C", "C"}, ++ { true, "2.5.4.5", "serialNumber", "SERIALNUMBER"}, ++ { false, "2.5.4.7", "L", "L"}, ++ { true, "2.5.4.12", "title", "T"}, ++ { false, "2.5.4.4", "SN", "SN"}, ++ { true, "2.5.4.42", "givenName", "G"}, ++ { true, "2.5.4.43", "initials", "I"}, ++ { true, "2.5.4.44", "generationQualifier", "OID.2.5.4.44"}, ++ { false, "0.9.2342.19200300.100.1.25", "DC", "DC"}, ++ { true, "0.9.2342.19200300.100.1.3", "MAIL", "OID,0.9.2342.19200300.100.1.3"}, ++ { true, "0.9.2342.19200300.100.1.1", "UID", "OID.0.9.2342.19200300.100.1.1"}, ++ { true, "2.5.4.13", "OID.2.5.4.13", "Description"}, ++ { true, "2.5.4.16", "postalAddress", "OID.2.5.4.16"}, ++ { true, "2.5.4.17", "postalCode", "PostalCode"}, ++ { true, "2.5.4.18", "postOfficeBox", "POBox"}, ++ { true, "2.5.4.51", "houseIdentifier", "OID.2.5.4.51"}, ++ { false, "1.2.840.113549.1.9.1", "E", "E"}, ++ { false, "2.5.4.9", "STREET", "STREET"}, ++ { true, "2.5.4.65", "pseudonym", "OID.2.5.4.65"}, ++ { true, "2.5.4.15", "businessCategory", "OID.2.5.4.15"}, ++ { true, "2.5.4.41", "name", "OID.2.5.4.41"}, ++ ++ { false, NULL, NULL, NULL} ++}; ++ ++char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn) ++{ ++ char *p; ++ size_t c; ++ size_t len; ++ ++ if (rdn == NULL) { ++ return NULL; ++ } ++ ++ p = strchr(rdn, '='); ++ if (p == NULL) { ++ return NULL; ++ } ++ ++ len = p - rdn; ++ if (len == 0) { ++ return NULL; ++ } ++ ++ for (c = 0; oid_attr_name_map[c].oid != NULL; c++) { ++ if (!oid_attr_name_map[c].nss_ad_differ) { ++ continue; ++ } ++ ++ if (strlen(oid_attr_name_map[c].nss) != len ++ || strncmp(rdn, oid_attr_name_map[c].nss, len) != 0) { ++ continue; ++ } ++ ++ return talloc_asprintf(mem_ctx, "%s%s", oid_attr_name_map[c].ad, p); ++ } ++ ++ return NULL; ++} +diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h +new file mode 100644 +index 0000000000000000000000000000000000000000..28f1c596cfb5e78077b6a8e9baefa88b4900a022 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap_int.h +@@ -0,0 +1,187 @@ ++/* ++ SSSD ++ ++ Library for rule based certificate to user mapping ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++ ++#ifndef __SSS_CERTMAP_INT_H__ ++#define __SSS_CERTMAP_INT_H__ ++ ++#define CM_DEBUG(cm_ctx, format, ...) do { \ ++ if (cm_ctx != NULL && cm_ctx->debug != NULL) { \ ++ cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \ ++ format, ##__VA_ARGS__); \ ++ } \ ++} while (0) ++ ++#define DEFAULT_MATCH_RULE "digitalSignatureclientAuth" ++#define DEFAULT_MAP_RULE "LDAP:(userCertificate;binary={cert!bin})" ++ ++enum san_opt { ++ SAN_OTHER_NAME = 0, ++ SAN_RFC822_NAME, ++ SAN_DNS_NAME, ++ SAN_X400_ADDRESS, ++ SAN_DIRECTORY_NAME, ++ SAN_EDIPART_NAME, ++ SAN_URI, ++ SAN_IP_ADDRESS, ++ SAN_REGISTERED_ID, ++ SAN_PKINIT, ++ SAN_NT, ++ SAN_PRINCIPAL, ++ SAN_STRING_OTHER_NAME, ++ ++ SAN_END, ++ SAN_INVALID ++}; ++ ++/* KRB5 matching rule */ ++enum relation_type { ++ relation_none = 0, ++ relation_and, ++ relation_or ++}; ++ ++struct component_list { ++ char *val; ++ regex_t regexp; ++ uint32_t ku; ++ const char **eku_oid_list; ++ enum san_opt san_opt; ++ char *str_other_name_oid; ++ uint8_t *bin_val; ++ size_t bin_val_len; ++ struct component_list *prev; ++ struct component_list *next; ++}; ++ ++struct krb5_match_rule { ++ enum relation_type r; ++ struct component_list *issuer; ++ struct component_list *subject; ++ struct component_list *ku; ++ struct component_list *eku; ++ struct component_list *san; ++}; ++ ++enum comp_type { ++ comp_none = 0, ++ comp_string, ++ comp_template ++}; ++ ++struct parsed_template { ++ char *name; ++ char *attr_name; ++ char *conversion; ++}; ++ ++struct ldap_mapping_rule_comp { ++ enum comp_type type; ++ char *val; ++ struct parsed_template *parsed_template; ++ struct ldap_mapping_rule_comp *prev; ++ struct ldap_mapping_rule_comp *next; ++}; ++ ++struct ldap_mapping_rule { ++ struct ldap_mapping_rule_comp *list; ++}; ++ ++struct match_map_rule { ++ uint32_t priority; ++ char *match_rule; ++ struct krb5_match_rule *parsed_match_rule; ++ char *map_rule; ++ struct ldap_mapping_rule *parsed_mapping_rule; ++ char **domains; ++ struct match_map_rule *prev; ++ struct match_map_rule *next; ++}; ++ ++struct priority_list { ++ uint32_t priority; ++ struct match_map_rule *rule_list; ++ struct priority_list *prev; ++ struct priority_list *next; ++}; ++ ++struct sss_certmap_ctx { ++ struct priority_list *prio_list; ++ sss_certmap_ext_debug *debug; ++ void *debug_priv; ++ struct ldap_mapping_rule *default_mapping_rule; ++}; ++ ++struct san_list { ++ enum san_opt san_opt; ++ char *val; ++ uint8_t *bin_val; ++ size_t bin_val_len; ++ char *other_name_oid; ++ char *short_name; ++ const char **rdn_list; ++ struct san_list *prev; ++ struct san_list *next; ++}; ++ ++/* key usage flags, see RFC 3280 section 4.2.1.3 */ ++#define SSS_KU_DIGITAL_SIGNATURE 0x0080 ++#define SSS_KU_NON_REPUDIATION 0x0040 ++#define SSS_KU_KEY_ENCIPHERMENT 0x0020 ++#define SSS_KU_DATA_ENCIPHERMENT 0x0010 ++#define SSS_KU_KEY_AGREEMENT 0x0008 ++#define SSS_KU_KEY_CERT_SIGN 0x0004 ++#define SSS_KU_CRL_SIGN 0x0002 ++#define SSS_KU_ENCIPHER_ONLY 0x0001 ++#define SSS_KU_DECIPHER_ONLY 0x8000 ++ ++struct sss_cert_content { ++ const char *issuer_str; ++ const char **issuer_rdn_list; ++ const char *subject_str; ++ const char **subject_rdn_list; ++ uint32_t key_usage; ++ const char **extended_key_usage_oids; ++ struct san_list *san_list; ++ ++ uint8_t *cert_der; ++ size_t cert_der_size; ++}; ++ ++int sss_cert_get_content(TALLOC_CTX *mem_ctx, ++ const uint8_t *der_blob, size_t der_size, ++ struct sss_cert_content **content); ++ ++char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn); ++ ++int parse_krb5_match_rule(struct sss_certmap_ctx *ctx, ++ const char *rule_start, ++ struct krb5_match_rule **match_rule); ++ ++int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx, ++ const char *rule_start, ++ struct ldap_mapping_rule **mapping_rule); ++#endif /* __SSS_CERTMAP_INT_H__ */ +diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c +new file mode 100644 +index 0000000000000000000000000000000000000000..e40f17b8ace46e61087e0a2fa570a362a84cead2 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap_krb5_match.c +@@ -0,0 +1,558 @@ ++/* ++ SSSD ++ ++ Library for rule based certificate to user mapping - KRB5 matching rules ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "util/util.h" ++#include "util/cert.h" ++#include "util/crypto/sss_crypto.h" ++#include "lib/certmap/sss_certmap.h" ++#include "lib/certmap/sss_certmap_int.h" ++ ++static bool is_dotted_decimal(const char *s, size_t len) ++{ ++ size_t c = 0; ++ bool has_dot = false; ++ ++ if (s == NULL || !isdigit(s[c++])) { ++ return false; ++ } ++ ++ while ((len == 0 && s[c] != '\0') || (len != 0 && c < len)) { ++ if (s[c] != '.' && !isdigit(s[c])) { ++ return false; ++ } ++ if (!has_dot && s[c] == '.') { ++ has_dot = true; ++ } ++ c++; ++ } ++ ++ return (has_dot && isdigit(s[c - 1])); ++} ++ ++static int component_list_destructor(void *data) ++{ ++ struct component_list *comp = talloc_get_type(data, struct component_list); ++ ++ if (comp != NULL) { ++ regfree(&(comp->regexp)); ++ } ++ ++ return 0; ++} ++ ++/* ++ * The syntax of the MIT Kerberos style matching rules is: ++ * [KRB5:][relation-operator]component-rule ... ++ * ++ * where: ++ * ++ * relation-operator ++ * can be either &&, meaning all component rules must match, or ||, ++ * meaning only one component rule must match. The default is &&. ++ * ++ * component-rule ++ * can be one of the following. Note that there is no punctuation or whitespace between component rules. ++ * regular-expression ++ * regular-expression ++ * regular-expression ++ * extended-key-usage ++ * key-usage ++ * ++ * see man sss-certmap for more details ++ * ++ */ ++ ++static int get_comp_value(TALLOC_CTX *mem_ctx, ++ struct sss_certmap_ctx *ctx, ++ const char **cur, ++ struct component_list **_comp) ++ ++{ ++ struct component_list *comp = NULL; ++ const char *end; ++ int ret; ++ ++ comp = talloc_zero(mem_ctx, struct component_list); ++ if (comp == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ talloc_set_destructor((TALLOC_CTX *) comp, component_list_destructor); ++ ++ end = strchr(*cur, '<'); ++ ++ if (end == NULL) { ++ comp->val = talloc_strdup(comp, *cur); ++ } else { ++ comp->val = talloc_strndup(comp, *cur, end - *cur); ++ } ++ if (comp->val == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ if (*(comp->val) == '\0') { ++ CM_DEBUG(ctx, "Missing component value."); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ *cur += strlen(comp->val); ++ *_comp = comp; ++ ret = 0; ++ ++done: ++ if (ret != 0) { ++ talloc_free(comp); ++ } ++ ++ return ret; ++} ++ ++static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx, ++ struct sss_certmap_ctx *ctx, ++ const char **cur, ++ struct component_list **_comp) ++{ ++ struct component_list *comp = NULL; ++ int ret; ++ char **eku_list; ++ size_t c; ++ size_t k; ++ const char *o; ++ size_t e = 0; ++ int eku_list_size; ++ ++ struct ext_key_usage { ++ const char *name; ++ const char *oid; ++ } ext_key_usage[] = { ++ /* RFC 3280 section 4.2.1.13 */ ++ {"serverAuth", "1.3.6.1.5.5.7.3.1"}, ++ {"clientAuth", "1.3.6.1.5.5.7.3.2"}, ++ {"codeSigning", "1.3.6.1.5.5.7.3.3"}, ++ {"emailProtection", "1.3.6.1.5.5.7.3.4"}, ++ {"timeStamping", "1.3.6.1.5.5.7.3.8"}, ++ {"OCSPSigning", "1.3.6.1.5.5.7.3.9"}, ++ ++ /* RFC 4556 section 3.2.2 */ ++ {"KPClientAuth", "1.3.6.1.5.2.3.4"}, ++ {"pkinit", "1.3.6.1.5.2.3.4"}, ++ ++ /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography*/ ++ {"msScLogin", "1.3.6.1.4.1.311.20.2.2"}, ++ ++ {NULL ,0} ++ }; ++ ++ ret = get_comp_value(mem_ctx, ctx, cur, &comp); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to parse regexp."); ++ goto done; ++ } ++ ++ ret = split_on_separator(mem_ctx, comp->val, ',', true, true, ++ &eku_list, &eku_list_size); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to split list."); ++ goto done; ++ } ++ ++ for (c = 0; eku_list[c] != NULL; c++) { ++ for (k = 0; ext_key_usage[k].name != NULL; k++) { ++CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name); ++ if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) { ++ if (comp->eku_oid_list == NULL) { ++ comp->eku_oid_list = talloc_zero_array(comp, const char *, ++ eku_list_size + 1); ++ if (comp->eku_oid_list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list, ++ ext_key_usage[k].oid); ++ if (comp->eku_oid_list[e] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ e++; ++ break; ++ } ++ } ++ ++ if (ext_key_usage[k].name == NULL) { ++ /* check for an dotted-decimal OID */ ++ if (*(eku_list[c]) != '.') { ++ o = eku_list[c]; ++ if (is_dotted_decimal(o, 0)) { ++ /* looks like a OID, only '.' and digits */ ++ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list, ++ eku_list[c]); ++ if (comp->eku_oid_list[e] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ e++; ++ continue; ++ } ++ } ++ CM_DEBUG(ctx, "No matching extended key usage found."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *_comp = comp; ++ } else { ++ talloc_free(comp); ++ } ++ ++ return ret; ++} ++ ++static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx, ++ struct sss_certmap_ctx *ctx, ++ const char **cur, ++ struct component_list **_comp) ++{ ++ struct component_list *comp = NULL; ++ int ret; ++ char **ku_list; ++ size_t c; ++ size_t k; ++ ++ struct key_usage { ++ const char *name; ++ uint32_t flag; ++ } key_usage[] = { ++ {"digitalSignature" , SSS_KU_DIGITAL_SIGNATURE}, ++ {"nonRepudiation" , SSS_KU_NON_REPUDIATION}, ++ {"keyEncipherment" , SSS_KU_KEY_ENCIPHERMENT}, ++ {"dataEncipherment" , SSS_KU_DATA_ENCIPHERMENT}, ++ {"keyAgreement" , SSS_KU_KEY_AGREEMENT}, ++ {"keyCertSign" , SSS_KU_KEY_CERT_SIGN}, ++ {"cRLSign" , SSS_KU_CRL_SIGN}, ++ {"encipherOnly" , SSS_KU_ENCIPHER_ONLY}, ++ {"decipherOnly" , SSS_KU_DECIPHER_ONLY}, ++ {NULL ,0} ++ }; ++ ++ ++ ret = get_comp_value(mem_ctx, ctx, cur, &comp); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to get value."); ++ goto done; ++ } ++ ++ ret = split_on_separator(mem_ctx, comp->val, ',', true, true, ++ &ku_list, NULL); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to split list."); ++ goto done; ++ } ++ ++ for (c = 0; ku_list[c] != NULL; c++) { ++ for (k = 0; key_usage[k].name != NULL; k++) { ++ if (strcasecmp(ku_list[c], key_usage[k].name) == 0) { ++ comp->ku |= key_usage[k].flag; ++ break; ++ } ++ } ++ ++ if (key_usage[k].name == NULL) { ++ /* FIXME: add check for numerical ku */ ++ CM_DEBUG(ctx, "No matching key usage found."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *_comp = comp; ++ } else { ++ talloc_free(comp); ++ } ++ ++ return ret; ++} ++ ++static int parse_krb5_get_component_value(TALLOC_CTX *mem_ctx, ++ struct sss_certmap_ctx *ctx, ++ const char **cur, ++ struct component_list **_comp) ++{ ++ struct component_list *comp = NULL; ++ int ret; ++ ++ ret = get_comp_value(mem_ctx, ctx, cur, &comp); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to parse regexp."); ++ goto done; ++ } ++ ++ ret = regcomp(&(comp->regexp), comp->val, REG_EXTENDED); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to parse regexp."); ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *_comp = comp; ++ } else { ++ talloc_free(comp); ++ } ++ ++ return ret; ++} ++ ++struct san_name { ++ const char *name; ++ enum san_opt san_opt; ++ bool is_string; ++} san_names[] = { ++ /* https://www.ietf.org/rfc/rfc3280.txt section 4.2.1.7 */ ++ {"otherName", SAN_OTHER_NAME, false}, ++ {"rfc822Name", SAN_RFC822_NAME,true}, ++ {"dNSName", SAN_DNS_NAME, true}, ++ {"x400Address", SAN_X400_ADDRESS, false}, ++ {"directoryName", SAN_DIRECTORY_NAME, true}, ++ {"ediPartyName", SAN_EDIPART_NAME, false}, ++ {"uniformResourceIdentifier", SAN_URI, true}, ++ {"iPAddress", SAN_IP_ADDRESS, true}, ++ {"registeredID", SAN_REGISTERED_ID, true}, ++ /* https://www.ietf.org/rfc/rfc4556.txt section 3.2.2 */ ++ {"pkinitSAN", SAN_PKINIT, true}, ++ /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography */ ++ {"ntPrincipalName", SAN_NT, true}, ++ /* both previous principal types */ ++ {"Principal", SAN_PRINCIPAL, true}, ++ {"stringOtherName", SAN_STRING_OTHER_NAME, true}, ++ {NULL, SAN_END, false} ++}; ++ ++static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx, ++ struct sss_certmap_ctx *ctx, ++ const char **cur, ++ enum san_opt *option, ++ char **str_other_name_oid) ++{ ++ char *end; ++ size_t c; ++ size_t len; ++ ++ end = strchr(*cur, '>'); ++ if (end == NULL) { ++ CM_DEBUG(ctx, "Failed to parse SAN option."); ++ return EINVAL; ++ } ++ ++ len = end - *cur; ++ ++ if (len == 0) { ++ c= SAN_PRINCIPAL; ++ } else { ++ for (c = 0; san_names[c].name != NULL; c++) { ++ if (strncasecmp(*cur, san_names[c].name, len) == 0) { ++ break; ++ } ++ } ++ if (san_names[c].name == NULL) { ++ if (is_dotted_decimal(*cur, len)) { ++ c = SAN_STRING_OTHER_NAME; ++ *str_other_name_oid = talloc_strndup(mem_ctx, *cur, len); ++ if (*str_other_name_oid == NULL) { ++ CM_DEBUG(ctx, "talloc_strndup failed."); ++ return ENOMEM; ++ } ++ } else { ++ CM_DEBUG(ctx, "Unknown SAN option."); ++ return EINVAL; ++ } ++ } ++ } ++ ++ *option = san_names[c].san_opt; ++ *cur = end + 1; ++ ++ return 0; ++} ++ ++static int parse_krb5_get_san_value(TALLOC_CTX *mem_ctx, ++ struct sss_certmap_ctx *ctx, ++ const char **cur, ++ struct component_list **_comp) ++{ ++ struct component_list *comp = NULL; ++ enum san_opt san_opt = SAN_PRINCIPAL; ++ int ret; ++ char *str_other_name_oid = NULL; ++ ++ if (*(*cur - 1) == ':') { ++ ret = parse_krb5_get_san_option(mem_ctx, ctx, cur, &san_opt, ++ &str_other_name_oid); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ ++ if (san_names[san_opt].is_string) { ++ ret = parse_krb5_get_component_value(mem_ctx, ctx, cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ } else { ++ ret = get_comp_value(mem_ctx, ctx, cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ ++ if (comp->val != NULL) { ++ comp->bin_val = sss_base64_decode(comp, comp->val, ++ &comp->bin_val_len); ++ /* for some reasons the NSS version of sss_base64_decode might ++ * return a non-NULL value on error but len is still 0, so better ++ * check both. */ ++ if (comp->bin_val == NULL || comp->bin_val_len == 0) { ++ CM_DEBUG(ctx, "Base64 decode failed."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ } ++ comp->san_opt = san_opt; ++ ++done: ++ if (ret == 0) { ++ comp->str_other_name_oid = talloc_steal(comp, str_other_name_oid); ++ *_comp = comp; ++ } else { ++ talloc_free(comp); ++ talloc_free(str_other_name_oid); ++ } ++ ++ return ret; ++} ++ ++int parse_krb5_match_rule(struct sss_certmap_ctx *ctx, ++ const char *rule_start, ++ struct krb5_match_rule **match_rule) ++{ ++ const char *cur; ++ struct krb5_match_rule *rule; ++ struct component_list *comp; ++ int ret; ++ ++ rule = talloc_zero(ctx, struct krb5_match_rule); ++ if (rule == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cur = rule_start; ++ /* check relation */ ++ if (strncmp(cur, "&&", 2) == 0) { ++ rule->r = relation_and; ++ cur += 2; ++ } else if (strncmp(cur, "||", 2) == 0) { ++ rule->r = relation_or; ++ cur += 2; ++ } else { ++ rule->r = relation_and; ++ } ++ ++ while (*cur != '\0') { ++ /* new component must start with '<' */ ++ if (*cur != '<') { ++ CM_DEBUG(ctx, "Invalid KRB5 matching rule."); ++ ret = EINVAL; ++ goto done; ++ } ++ cur++; ++ ++ if (strncmp(cur, "ISSUER>", 7) == 0) { ++ cur += 7; ++ ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(rule->issuer, comp); ++ } else if (strncmp(cur, "SUBJECT>", 8) == 0) { ++ cur += 8; ++ ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(rule->subject, comp); ++ } else if (strncmp(cur, "KU>", 3) == 0) { ++ cur += 3; ++ ret = parse_krb5_get_ku_value(rule, ctx, &cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(rule->ku, comp); ++ } else if (strncmp(cur, "EKU>", 4) == 0) { ++ cur += 4; ++ ret = parse_krb5_get_eku_value(rule, ctx, &cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(rule->eku, comp); ++ } else if (strncmp(cur, "SAN>", 4) == 0 ++ || strncmp(cur, "SAN:", 4) == 0) { ++ cur += 4; ++ ret = parse_krb5_get_san_value(rule, ctx, &cur, &comp); ++ if (ret != 0) { ++ goto done; ++ } ++ DLIST_ADD(rule->san, comp); ++ } else { ++ CM_DEBUG(ctx, "Invalid KRB5 matching rule."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *match_rule = rule; ++ } else { ++ talloc_free(rule); ++ } ++ ++ return ret; ++} +diff --git a/src/lib/certmap/sss_certmap_ldap_mapping.c b/src/lib/certmap/sss_certmap_ldap_mapping.c +new file mode 100644 +index 0000000000000000000000000000000000000000..c64c05b311f043b4d70f98f718780601c3e6a002 +--- /dev/null ++++ b/src/lib/certmap/sss_certmap_ldap_mapping.c +@@ -0,0 +1,367 @@ ++/* ++ SSSD ++ ++ Library for rule based certificate to user mapping - LDAP mapping rules ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "util/util.h" ++#include "util/cert.h" ++#include "util/crypto/sss_crypto.h" ++#include "lib/certmap/sss_certmap.h" ++#include "lib/certmap/sss_certmap_int.h" ++struct template_table { ++ const char *name; ++ const char **attr_name; ++ const char **conversion; ++}; ++ ++const char *empty[] = {NULL}; ++const char *name_attr[] = {"short_name", NULL}; ++const char *x500_conv[] = {"ad_x500", "ad", "ad_ldap", ++ "nss_x500", "nss", "nss_ldap", NULL}; ++const char *bin_conv[] = {"bin", "base64", NULL}; ++ ++struct template_table template_table[] = { ++ {"issuer_dn", empty, x500_conv}, ++ {"subject_dn", empty, x500_conv}, ++ {"cert", empty, bin_conv}, ++ {"subject_rfc822_name", name_attr, empty}, ++ {"subject_dns_name", name_attr, empty}, ++ {"subject_x400_address", empty, empty}, ++ {"subject_directory_name", empty, empty}, ++ {"subject_ediparty_name", empty, empty}, ++ {"subject_uri", empty, empty}, ++ {"subject_ip_address", empty, empty}, ++ {"subject_registered_id", empty, empty}, ++ {"subject_pkinit_principal", name_attr, empty}, ++ {"subject_nt_principal", name_attr, empty}, ++ {"subject_principal", name_attr, empty}, ++ {NULL, NULL, NULL}}; ++ ++static int check_parsed_template(struct sss_certmap_ctx *ctx, ++ struct parsed_template *parsed) ++{ ++ size_t n; ++ size_t a; ++ size_t c; ++ bool attr_name_valid = false; ++ bool conversion_valid = false; ++ ++ for (n = 0; template_table[n].name != NULL; n++) { ++ if (strcmp(template_table[n].name, parsed->name) != 0) { ++ continue; ++ } ++ ++ if (parsed->attr_name != NULL) { ++ for (a = 0; template_table[n].attr_name[a] != NULL; a++) { ++ if (strcmp(template_table[n].attr_name[a], ++ parsed->attr_name) == 0) { ++ attr_name_valid = true; ++ break; ++ } ++ } ++ } else { ++ attr_name_valid = true; ++ } ++ ++ if (parsed->conversion != NULL) { ++ for (c = 0; template_table[n].conversion[c] != NULL; c++) { ++ if (strcmp(template_table[n].conversion[c], ++ parsed->conversion) == 0) { ++ conversion_valid = true; ++ break; ++ } ++ } ++ } else { ++ conversion_valid = true; ++ } ++ ++ if (attr_name_valid && conversion_valid) { ++ return 0; ++ } ++ } ++ ++ return EINVAL; ++} ++ ++static int parse_template(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx, ++ const char *template, ++ struct parsed_template **parsed_template) ++{ ++ int ret; ++ struct parsed_template *parsed = NULL; ++ const char *dot; ++ const char *excl; ++ const char *p; ++ ++ parsed = talloc_zero(mem_ctx, struct parsed_template); ++ if (parsed == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ dot = strchr(template, '.'); ++ if (dot != NULL) { ++ p = strchr(dot + 1, '.'); ++ if (p != NULL) { ++ CM_DEBUG(ctx, "Only one '.' allowed in template."); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (dot == template) { ++ CM_DEBUG(ctx, "Missing name in template."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ excl = strchr(template, '!'); ++ if (excl != NULL) { ++ p = strchr(excl + 1, '!'); ++ if (p != NULL) { ++ CM_DEBUG(ctx, "Only one '!' allowed in template."); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (excl == template) { ++ CM_DEBUG(ctx, "Missing name in template."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ if (excl != NULL && excl[1] != '\0') { ++ parsed->conversion = talloc_strdup(parsed, excl + 1); ++ if (parsed->conversion == NULL) { ++ CM_DEBUG(ctx, "Memory allocation failed."); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ if (dot != NULL && dot[1] != '\0' && dot[1] != '!') { ++ if (excl == NULL) { ++ parsed->attr_name = talloc_strdup(parsed, dot + 1); ++ } else { ++ parsed->attr_name = talloc_strndup(parsed, dot + 1, ++ (excl - dot - 1)); ++ } ++ if (parsed->attr_name == NULL) { ++ CM_DEBUG(ctx, "Memory allocation failed."); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ if (dot != NULL) { ++ parsed->name = talloc_strndup(parsed, template, (dot - template)); ++ } else if (excl != NULL) { ++ parsed->name = talloc_strndup(parsed, template, (excl - template)); ++ } else { ++ parsed->name = talloc_strdup(parsed, template); ++ } ++ if (parsed->name == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = check_parsed_template(ctx, parsed); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Parse template invalid."); ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *parsed_template = parsed; ++ } else { ++ talloc_free(parsed); ++ } ++ ++ return ret; ++} ++ ++static int add_comp(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule, ++ const char *string, enum comp_type type) ++{ ++ int ret; ++ struct ldap_mapping_rule_comp *comp; ++ ++ comp = talloc_zero(rule, struct ldap_mapping_rule_comp); ++ if (comp == NULL) { ++ return ENOMEM; ++ } ++ ++ comp->type = type; ++ comp->val = talloc_strdup(comp, string); ++ if (comp->val == NULL) { ++ talloc_free(comp); ++ return ENOMEM; ++ } ++ ++ if (type == comp_template) { ++ ret = parse_template(comp, ctx, string, &comp->parsed_template); ++ if (ret != 0) { ++ talloc_free(comp); ++ return ret; ++ } ++ } ++ ++ DLIST_ADD_END(rule->list, comp, struct ldap_mapping_rule_comp *); ++ ++ return 0; ++} ++ ++static int add_string(struct sss_certmap_ctx *ctx, ++ struct ldap_mapping_rule *rule, const char *string) ++{ ++ return add_comp(ctx, rule, string, comp_string); ++} ++ ++static int add_template(struct sss_certmap_ctx *ctx, ++ struct ldap_mapping_rule *rule, const char *string) ++{ ++ return add_comp(ctx, rule, string, comp_template); ++} ++ ++int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx, ++ const char *rule_start, ++ struct ldap_mapping_rule **mapping_rule) ++{ ++ size_t c; ++ const char *cur; ++ char *tmp_string = NULL; ++ size_t tmp_string_size; ++ struct ldap_mapping_rule *rule = NULL; ++ int ret; ++ bool in_template = false; ++ ++ rule = talloc_zero(ctx, struct ldap_mapping_rule); ++ if (rule == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tmp_string_size = strlen(rule_start) + 1; ++ tmp_string = talloc_zero_size(ctx, tmp_string_size); ++ if (tmp_string == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cur = rule_start; ++ c = 0; ++ ++ while (*cur != '\0') { ++ if (c > tmp_string_size) { ++ CM_DEBUG(ctx, "Cannot parse mapping rule."); ++ ret = EIO; ++ goto done; ++ } ++ switch (*cur) { ++ case '{': ++ if (in_template) { ++ CM_DEBUG(ctx, "'{' not allowed in templates."); ++ ret = EINVAL; ++ goto done; ++ } ++ if (cur[1] == '{') { ++ /* Add only a single '{' to the output */ ++ tmp_string[c] = '{'; ++ c++; ++ cur += 2; ++ } else { ++ if (c != 0) { ++ ret = add_string(ctx, rule, tmp_string); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to add string."); ++ ret = EINVAL; ++ goto done; ++ } ++ memset(tmp_string, 0, tmp_string_size); ++ c = 0; ++ } ++ cur++; ++ in_template = true; ++ } ++ break; ++ case '}': ++ if (cur[1] == '}') { ++ if (in_template) { ++ CM_DEBUG(ctx, "'}}' not allowed in templates."); ++ ret = EINVAL; ++ goto done; ++ } else { ++ /* Add only a single '}' to the output */ ++ tmp_string[c] = '}'; ++ c++; ++ cur += 2; ++ } ++ } else { ++ ret = add_template(ctx, rule, tmp_string); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to add template."); ++ ret = EINVAL; ++ goto done; ++ } ++ memset(tmp_string, 0, tmp_string_size); ++ c = 0; ++ cur++; ++ in_template = false; ++ } ++ break; ++ default: ++ tmp_string[c] = *cur; ++ c++; ++ cur++; ++ } ++ } ++ if (in_template) { ++ CM_DEBUG(ctx, "Rule ended inside template."); ++ ret = EINVAL; ++ goto done; ++ } ++ if (c != 0) { ++ ret = add_string(ctx, rule, tmp_string); ++ if (ret != 0) { ++ CM_DEBUG(ctx, "Failed to add string."); ++ ret = EINVAL; ++ goto done; ++ } ++ } ++ ++ ret = 0; ++ ++done: ++ if (ret == 0) { ++ *mapping_rule = rule; ++ } else { ++ talloc_free(rule); ++ } ++ ++ talloc_free(tmp_string); ++ ++ return ret; ++} +diff --git a/src/man/Makefile.am b/src/man/Makefile.am +index 215ce693b56e74db394dbc238c03c87f5f6efe99..142d6e2743f814294e3d92c8342070b8230bb3e5 100644 +--- a/src/man/Makefile.am ++++ b/src/man/Makefile.am +@@ -59,7 +59,7 @@ man_MANS = \ + sss_useradd.8 sss_userdel.8 sss_usermod.8 \ + sss_groupadd.8 sss_groupdel.8 sss_groupmod.8 \ + sssd.8 sssd.conf.5 sssd-ldap.5 \ +- sssd-krb5.5 sssd-simple.5 \ ++ sssd-krb5.5 sssd-simple.5 sss-certmap.5 \ + sssd_krb5_locator_plugin.8 sss_groupshow.8 \ + pam_sss.8 sss_obfuscate.8 sss_cache.8 sss_debuglevel.8 sss_seed.8 \ + sss_override.8 idmap_sss.8 sssctl.8 \ +diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg +index ffcf9a2793da3c0115bc846744ccb9592a9a68ef..d1f6ac39f841c61ae3d2393fb3402dc21b9cbd69 100644 +--- a/src/man/po/po4a.cfg ++++ b/src/man/po/po4a.cfg +@@ -6,6 +6,7 @@ + [type:docbook] pam_sss.8.xml $lang:$(builddir)/$lang/pam_sss.8.xml + [type:docbook] sssd_krb5_locator_plugin.8.xml $lang:$(builddir)/$lang/sssd_krb5_locator_plugin.8.xml + [type:docbook] sssd-simple.5.xml $lang:$(builddir)/$lang/sssd-simple.5.xml ++[type:docbook] sss-certmap.5.xml $lang:$(builddir)/$lang/sss-certmap.5.xml + [type:docbook] sssd-ipa.5.xml $lang:$(builddir)/$lang/sssd-ipa.5.xml + [type:docbook] sssd-ad.5.xml $lang:$(builddir)/$lang/sssd-ad.5.xml + [type:docbook] sssd-sudo.5.xml $lang:$(builddir)/$lang/sssd-sudo.5.xml +diff --git a/src/man/sss-certmap.5.xml b/src/man/sss-certmap.5.xml +new file mode 100644 +index 0000000000000000000000000000000000000000..bbe68509f2222613a7ed69599519d7fca0506df0 +--- /dev/null ++++ b/src/man/sss-certmap.5.xml +@@ -0,0 +1,600 @@ ++ ++ ++ ++SSSD Manual pages ++ ++ ++ ++ ++ sss-certmap ++ 5 ++ File Formats and Conventions ++ ++ ++ ++ sss-certmap ++ SSSD Certificate Matching and Mapping Rules ++ ++ ++ ++ DESCRIPTION ++ ++ The manual page describes the rules which can be used by SSSD and ++ other components to match X.509 certificates and map them to ++ accounts. ++ ++ ++ Each rule has four components, a priority, a ++ matching rule, a mapping rule and a ++ domain list. All components are optional. A missing ++ priority will add the rule with the lowest priority. ++ The default matching rule will match certificates with ++ the digitalSignature key usage and clientAuth extended key usage. If ++ the mapping rule is empty the certificates will be ++ searched in the userCertificate attribute as DER encoded binary. If ++ no domains are given only the local domain will be searched. ++ ++ ++ ++ ++ RULE COMPONENTS ++ ++ PRIORITY ++ ++ The rules are process by priority while the number '0' (zero) ++ indicates the highest priority. The higher the number the lower is ++ the priority. A missing value indicates the lowest priority. ++ ++ ++ Internally the priority is treated as unsigned 32bit integer, using ++ a priority value larger than 4294967295 will cause an error. ++ ++ ++ ++ MATCHING RULE ++ ++ The matching rule is used to select a certificate to which the ++ mapping rule should be applied. It uses a system similar to the one ++ used by pkinit_cert_match option of MIT Kerberos. It ++ consists of a keyword enclosed by '<' and '>' which identified ++ a certain part of the certificate and a pattern which should be ++ found for the rule to match. Multiple keyword pattern pairs can be ++ either joined with '&&' (and) or '||' (or). ++ ++ ++ The available options are: ++ ++ ++ <SUBJECT>regular-expression ++ ++ ++ With this a part or the whole subject name of the ++ certificate can be matched. For the matching POSIX ++ Extended Regular Expression syntax is used, see regex(7) ++ for details. ++ ++ ++ For the matching the subject name stored in the ++ certificate in DER encoded ASN.1 is converted into a ++ string according to RFC 4514. This means the most ++ specific name component comes first. Please note that ++ not all possible attribute names are covered by RFC ++ 4514. The names included are 'CN', 'L', 'ST', 'O', ++ 'OU', 'C', 'STREET', 'DC' and 'UID'. Other attribute ++ names might be shown differently on different platform ++ and by different tools. To avoid confusion those ++ attribute names are best not used or covered by a ++ suitable regular-expression. ++ ++ ++ Example: <SUBJECT>.*,DC=MY,DC=DOMAIN ++ ++ ++ ++ ++ <ISSUER>regular-expression ++ ++ ++ With this a part or the whole issuer name of the ++ certificate can be matched. All comments for ++ <SUBJECT> apply her as well. ++ ++ ++ Example: <ISSUER>^CN=My-CA,DC=MY,DC=DOMAIN$ ++ ++ ++ ++ ++ <KU>key-usage ++ ++ ++ This option can be used to specify which key usage ++ values the certificate should have. The following value ++ can be used in a comma separate list: ++ ++ digitalSignature ++ nonRepudiation ++ keyEncipherment ++ dataEncipherment ++ keyAgreement ++ keyCertSign ++ cRLSign ++ encipherOnly ++ decipherOnly ++ ++ ++ ++ A numerical value in the range of a 32bit unsigned ++ integer can be used as well to cover special use cases. ++ ++ ++ Example: <KU>digitalSignature,keyEncipherment ++ ++ ++ ++ ++ <EKU>extended-key-usage ++ ++ ++ This option can be used to specify which extended key ++ usage the certificate should have. The following value ++ can be used in a comma separated list: ++ ++ serverAuth ++ clientAuth ++ codeSigning ++ emailProtection ++ timeStamping ++ OCSPSigning ++ KPClientAuth ++ pkinit ++ msScLogin ++ ++ ++ ++ Extended key usages which are not listed above can be ++ specified with their OID in dotted-decimal notation. ++ ++ ++ Example: <EKU>clientAuth,1.3.6.1.5.2.3.4 ++ ++ ++ ++ ++ <SAN>regular-expression ++ ++ ++ To be compatible with the usage of MIT Kerberos this ++ option will match the Kerberos principals in the PKINIT ++ or AD NT Principal SAN as <SAN:Principal> does. ++ ++ ++ Example: <SAN>.*@MY\.REALM ++ ++ ++ ++ ++ <SAN:Principal>regular-expression ++ ++ ++ Match the Kerberos principals in the PKINIT or AD NT ++ Principal SAN. ++ ++ ++ Example: <SAN:Principal>.*@MY\.REALM ++ ++ ++ ++ ++ <SAN:ntPrincipalName>regular-expression ++ ++ ++ Match the Kerberos principals from the AD NT Principal ++ SAN. ++ ++ ++ Example: <SAN:ntPrincipalName>.*@MY.AD.REALM ++ ++ ++ ++ ++ <SAN:pkinit>regular-expression ++ ++ ++ Match the Kerberos principals from the PKINIT SAN. ++ ++ ++ Example: <SAN:ntPrincipalName>.*@MY\.PKINIT\.REALM ++ ++ ++ ++ ++ <SAN:dotted-decimal-oid>regular-expression ++ ++ ++ Take the value of the otherName SAN component given by ++ the OID in dotted-decimal notation, interpret it as ++ string and try to match it against the regular ++ expression. ++ ++ ++ Example: <SAN:1.2.3.4>test ++ ++ ++ ++ ++ <SAN:otherName>base64-string ++ ++ ++ Do a binary match with the base64 encoded blob against ++ all otherName SAN components. With this option it is ++ possible to match against custom otherName components ++ with special encodings which could not be treated as ++ strings. ++ ++ ++ Example: <SAN:otherName>MTIz ++ ++ ++ ++ ++ <SAN:rfc822Name>regular-expression ++ ++ ++ Match the value of the rfc822Name SAN. ++ ++ ++ Example: <SAN:rfc822Name>.*@email\.domain ++ ++ ++ ++ ++ <SAN:dNSName>regular-expression ++ ++ ++ Match the value of the dNSName SAN. ++ ++ ++ Example: <SAN:dNSName>.*\.my\.dns\.domain ++ ++ ++ ++ ++ <SAN:x400Address>base64-string ++ ++ ++ Binary match the value of the x400Address SAN. ++ ++ ++ Example: <SAN:x400Address>MTIz ++ ++ ++ ++ ++ <SAN:directoryName>regular-expression ++ ++ ++ Match the value of the directoryName SAN. The same ++ comments as given for <ISSUER> and <SUBJECT> ++ apply here as well. ++ ++ ++ Example: <SAN:directoryName>.*,DC=com ++ ++ ++ ++ ++ <SAN:ediPartyName>base64-string ++ ++ ++ Binary match the value of the ediPartyName SAN. ++ ++ ++ Example: <SAN:ediPartyName>MTIz ++ ++ ++ ++ ++ <SAN:uniformResourceIdentifier>regular-expression ++ ++ ++ Match the value of the uniformResourceIdentifier SAN. ++ ++ ++ Example: <SAN:uniformResourceIdentifier>URN:.* ++ ++ ++ ++ ++ <SAN:iPAddress>regular-expression ++ ++ ++ Match the value of the iPAddress SAN. ++ ++ ++ Example: <SAN:iPAddress>192\.168\..* ++ ++ ++ ++ ++ <SAN:registeredID>regular-expression ++ ++ ++ Match the value of the registeredID SAN as ++ dotted-decimal string. ++ ++ ++ Example: <SAN:registeredID>1\.2\.3\..* ++ ++ ++ ++ ++ ++ ++ ++ MAPPING RULE ++ ++ The mapping rule is used to associate a certificate with one or more ++ accounts. A Smartcard with the certificate and the matching private ++ key can then be used to authenticate as one of those accounts. ++ ++ ++ Currently SSSD basically only supports LDAP to lookup user ++ information (the exception is the proxy provider which is not of ++ relevance here). Because of this the mapping rule is based on LDAP ++ search filter syntax with templates to add certificate content to ++ the filter. It is expected that the filter will only contain the ++ specific data needed for the mapping an that the caller will embed ++ it in another filter to do the actual search. Because of this the ++ filter string should start and stop with '(' and ')' respectively. ++ ++ ++ In general it is recommended to use attributes from the certificate ++ and add them to special attributes to the LDAP user object. E.g. the ++ 'altSecurityIdentities' attribute in AD or the 'ipaCertMapData' ++ attribute for IPA can be used. ++ ++ ++ This should be preferred to read user specific data from the ++ certificate like e.g. an email address and search for it in the LDAP ++ server. The reason is that the user specific data in LDAP might ++ change for various reasons would would break the mapping. On the ++ other hand it would be hard to break the mapping on purpose for a ++ specific user. ++ ++ ++ The templates to add certificate data to the search filter are based ++ on Python-style formatting strings. They consists of a keyword in ++ curly braces with an optional sub-component specifier separated by a ++ '.' or an optional conversion/formatting option separated by a '!'. ++ Allowed values are: ++ ++ ++ {issuer_dn[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]} ++ ++ ++ This template will add the full issuer DN converted to a ++ string according to RFC 4514. If X.500 ordering (most ++ specific RDN comes last) an option with the '_x500' ++ prefix should be used. ++ ++ ++ The conversion options starting with 'ad_' will use ++ attribute names as used by AD, e.g. 'S' instead of 'ST'. ++ ++ ++ The conversion options starting with 'nss_' will use ++ attribute names as used by NSS. ++ ++ ++ The default conversion option is 'nss', i.e. attribute ++ names according to NSS and LDAP/RFC 4514 ordering. ++ ++ ++ Example: (ipacertmapdata=X509:<I>{issuer_dn!ad}<S>{subject_dn!ad}) ++ ++ ++ ++ ++ {subject_dn[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]} ++ ++ ++ This template will add the full subject DN converted to ++ string according to RFC 4514. If X.500 ordering (most ++ specific RDN comes last) an option with the '_x500' ++ prefix should be used. ++ ++ ++ The conversion options starting with 'ad_' will use ++ attribute names as used by AD, e.g. 'S' instead of 'ST'. ++ ++ ++ The conversion options starting with 'nss_' will use ++ attribute names as used by NSS. ++ ++ ++ The default conversion option is 'nss', i.e. attribute ++ names according to NSS and LDAP/RFC 4514 ordering. ++ ++ ++ Example: (ipacertmapdata=X509:<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}) ++ ++ ++ ++ ++ {cert[!(bin|base64)]} ++ ++ ++ This template will add the whole DER encoded certificate ++ as a string to the search filter. Depending on the ++ conversion option the binary certificate is either ++ converted to an escaped hex sequence '\xx' or base64. ++ The escaped hex sequence is the default and can e.g. be ++ used with the LDAP attribute 'userCertificate;binary'. ++ ++ ++ Example: (userCertificate;binary={cert!bin}) ++ ++ ++ ++ ++ {subject_principal[.short_name]} ++ ++ ++ This template will add the Kerberos principal which is ++ taken either from the SAN used by pkinit or the one used ++ by AD. The 'short_name' component represent the first ++ part of the principal before the '@' sign. ++ ++ ++ Example: (|(userPrincipal={subject_principal})(samAccountName={subject_principal.short_name})) ++ ++ ++ ++ ++ {subject_pkinit_principal[.short_name]} ++ ++ ++ This template will add the Kerberos principal which is ++ given by then SAN used by pkinit. The 'short_name' ++ component represent the first part of the principal ++ before the '@' sign. ++ ++ ++ Example: (|(userPrincipal={subject_pkinit_principal})(uid={subject_pkinit_principal.short_name})) ++ ++ ++ ++ ++ {subject_nt_principal[.short_name]} ++ ++ ++ This template will add the Kerberos principal which is ++ given by then SAN used by AD. The 'short_name' component ++ represent the first part of the principal before the '@' ++ sign. ++ ++ ++ Example: (|(userPrincipal={subject_principal})(samAccountName={subject_principal.short_name})) ++ ++ ++ ++ ++ {subject_rfc822_name[.short_name]} ++ ++ ++ This template will add the string which is stored in the ++ rfc822Name component of the SAN, typically an email ++ address. The 'short_name' component represent the first ++ part of the address before the '@' sign. ++ ++ ++ Example: (|(mail={subject_rfc822_name})(uid={subject_rfc822_name.short_name})) ++ ++ ++ ++ ++ {subject_dns_name[.short_name]} ++ ++ ++ This template will add the string which is stored in the ++ dNSName component of the SAN, typically a fully-qualified host name. ++ The 'short_name' component represent the first ++ part of the name before the first '.' sign. ++ ++ ++ Example: (|(fqdn={subject_dns_name})(host={subject_dns_name.short_name})) ++ ++ ++ ++ ++ {subject_uri} ++ ++ ++ This template will add the string which is stored in the ++ uniformResourceIdentifier component of the SAN. ++ ++ ++ Example: (uri={subject_uri}) ++ ++ ++ ++ ++ {subject_ip_address} ++ ++ ++ This template will add the string which is stored in the ++ iPAddress component of the SAN. ++ ++ ++ Example: (ip={subject_ip_address}) ++ ++ ++ ++ ++ {subject_x400_address} ++ ++ ++ This template will add the value which is stored in the ++ x400Address component of the SAN as escaped hex ++ sequence. ++ ++ ++ Example: (attr:binary={subject_x400_address}) ++ ++ ++ ++ ++ {subject_directory_name[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]} ++ ++ ++ This template will add the DN string of the value which ++ is stored in the directoryName component of the SAN. ++ ++ ++ Example: (orig_dn={subject_directory_name}) ++ ++ ++ ++ ++ {subject_ediparty_name} ++ ++ ++ This template will add the value which is stored in the ++ ediPartyName component of the SAN as escaped hex ++ sequence. ++ ++ ++ Example: (attr:binary={subject_ediparty_name}) ++ ++ ++ ++ ++ {subject_registered_id} ++ ++ ++ This template will add the OID which is stored in the ++ registeredID component of the SAN as as dotted-decimal ++ string. ++ ++ ++ Example: (oid={subject_registered_id}) ++ ++ ++ ++ ++ ++ ++ ++ DOMAIN LIST ++ ++ If the domain list is not empty users mapped to a given certificate ++ are not only searched in the local domain but in the listed domains ++ as well as long as they are know by SSSD. Domains not know to SSSD ++ will be ignored. ++ ++ ++ ++ ++ +diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..c998443d086eaa72cc2a05c38ddfc5ba590a1ce7 +--- /dev/null ++++ b/src/tests/cmocka/test_certmap.c +@@ -0,0 +1,1443 @@ ++/* ++ SSSD ++ ++ certmap - Tests for SSSD's certificate mapping library ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/certmap/sss_certmap.h" ++#include "lib/certmap/sss_certmap_int.h" ++ ++#include "util/crypto/sss_crypto.h" ++ ++#include "tests/cmocka/common_mock.h" ++#include "tests/common.h" ++ ++#ifdef HAVE_NSS ++#include "util/crypto/nss/nss_util.h" ++#endif ++ ++struct priv_sss_debug { ++ int level; ++}; ++ ++void ext_debug(void *private, const char *file, long line, const char *function, ++ const char *format, ...) ++{ ++ va_list ap; ++ struct priv_sss_debug *data = private; ++ int level = SSSDBG_OP_FAILURE; ++ ++ if (data != NULL) { ++ level = data->level; ++ } ++ ++ if (DEBUG_IS_SET(level)) { ++ va_start(ap, format); ++ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED, ++ format, ap); ++ va_end(ap); ++ } ++} ++ ++static void test_sss_certmap_init(void **state) ++{ ++ int ret; ++ struct sss_certmap_ctx *ctx; ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ ++ sss_certmap_free_ctx(ctx); ++} ++ ++static struct sss_certmap_ctx *setup_prio(const int *l) ++{ ++ int ret; ++ size_t c; ++ struct sss_certmap_ctx *ctx; ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ ++ for (c = 0; c < 10; c++) { ++ ret = sss_certmap_add_rule(ctx, l[c], NULL, NULL, NULL); ++ assert_int_equal(ret, EOK); ++ } ++ ++ return ctx; ++} ++ ++static void test_sss_certmap_add_rule(void **state) ++{ ++ struct sss_certmap_ctx *ctx; ++ int i; ++ struct priority_list *p; ++ struct priority_list *last; ++ size_t c; ++ ++ const int tests_a[][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, ++ {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, ++ {1, 3, 5 ,7, 9, 0, 2, 4, 6, 8}, ++ {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; ++ ++ const int tests_b[][10] = {{0, 0, 0, 0, 1, 1, 1, 2, 2, 2}, ++ {2, 2, 2, 1, 1, 1, 0, 0, 0, 0}, ++ {0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, ++ {0, 2, 1, 0, 2, 1, 0, 2, 1, 0}, ++ {0, 1, 2, 0, 2, 1, 0, 0, 1, 2}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; ++ ++ for (c = 0; tests_a[c][0] != 0 || tests_a[c][9] != 0; c++) { ++ ctx = setup_prio(tests_a[0]); ++ assert_non_null(ctx); ++ i = 0; ++ for (p = ctx->prio_list; p != NULL; p = p->next) { ++ assert_int_equal(i, p->priority); ++ assert_non_null(p->rule_list); ++ assert_int_equal(i, p->rule_list->priority); ++ assert_null(p->rule_list->prev); ++ assert_null(p->rule_list->next); ++ i++; ++ } ++ ++ i = 9; ++ for (last = ctx->prio_list; last->next != NULL; last = last->next); ++ for (p = last; p != NULL; p = p->prev) { ++ assert_int_equal(i, p->priority); ++ assert_int_equal(i, p->rule_list->priority); ++ i--; ++ } ++ ++ sss_certmap_free_ctx(ctx); ++ } ++ for (c = 0; tests_b[c][0] != 0 || tests_b[c][9] != 0; c++) { ++ ctx = setup_prio(tests_b[0]); ++ assert_non_null(ctx); ++ i = 0; ++ for (p = ctx->prio_list; p != NULL; p = p->next) { ++ assert_int_equal(i, p->priority); ++ assert_non_null(p->rule_list); ++ assert_int_equal(i, p->rule_list->priority); ++ assert_null(p->rule_list->prev); ++ assert_non_null(p->rule_list->next); ++ assert_ptr_equal(p->rule_list, p->rule_list->next->prev); ++ assert_non_null(p->rule_list->next->next); ++ assert_ptr_equal(p->rule_list->next, ++ p->rule_list->next->next->prev); ++ if (i == 0) { ++ assert_non_null(p->rule_list->next->next->next); ++ assert_ptr_equal(p->rule_list->next->next, ++ p->rule_list->next->next->next->prev); ++ assert_null(p->rule_list->next->next->next->next); ++ } else { ++ assert_null(p->rule_list->next->next->next); ++ } ++ i++; ++ } ++ sss_certmap_free_ctx(ctx); ++ } ++} ++ ++static void test_sss_certmap_add_matching_rule(void **state) ++{ ++ struct sss_certmap_ctx *ctx; ++ int ret; ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "fsdf", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "FDSF:fsdf", NULL, NULL); ++ assert_int_equal(ret, ESRCH); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "KRB5:", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "ddqwdq", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "digitalSignature,dddq", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ++ ret = sss_certmap_add_rule(ctx, 1, "", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "dwqwqw", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, ".", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, ".1.2.3", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "1.2.3.", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "1.a.3", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "prio_list); ++ ++ /* invalid base64 input */ ++ ret = sss_certmap_add_rule(ctx, 1, "...", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ /* invalid OID input */ ++ ret = sss_certmap_add_rule(ctx, 1, "dqq", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "dqq", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "dqq", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "dqq", NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "a", NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1, "&&a", NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1, "KRB5:||a", NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_or); ++ assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1, "KRB5:ab", NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_string_equal("b", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->val); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1000, ++ "KRB5:abcd", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_string_equal("d", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->val); ++ assert_string_equal("b", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->next->val); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("c", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val); ++ ++ ret = sss_certmap_add_rule(ctx, 99, ++ "KRB5:ab" ++ "dataEncipherment,cRLSignc" ++ "d", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_string_equal("d", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->val); ++ assert_string_equal("b", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->next->val); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("c", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku); ++ assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT, ++ ctx->prio_list->rule_list->parsed_match_rule->ku->ku); ++ ++ ret = sss_certmap_add_rule(ctx, 98, ++ "KRB5:ab" ++ "dataEncipherment,cRLSignc" ++ "clientAuth,emailProtection" ++ "d", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); ++ assert_string_equal("d", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->val); ++ assert_string_equal("b", ++ ctx->prio_list->rule_list->parsed_match_rule->subject->next->val); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); ++ assert_string_equal("c", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->val); ++ assert_string_equal("a", ++ ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku); ++ assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT, ++ ctx->prio_list->rule_list->parsed_match_rule->ku->ku); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.2", ++ discard_const( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), ++ true)); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.4", ++ discard_const( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), ++ true)); ++ assert_null( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[2]); ++ ++ ret = sss_certmap_add_rule(ctx, 97, ++ "KRB5:clientAuth,1.2.3,emailProtection", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.2", ++ discard_const( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), ++ true)); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.4", ++ discard_const( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), ++ true)); ++ assert_true(string_in_list("1.2.3", ++ discard_const( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), ++ true)); ++ assert_null( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]); ++ ++ /* SAN tests */ ++ ret = sss_certmap_add_rule(ctx, 89, "KRB5:abc", NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, ++ SAN_PRINCIPAL); ++ assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val, ++ "abc"); ++ ++ ret = sss_certmap_add_rule(ctx, 88, "KRB5:def", NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, ++ SAN_DNS_NAME); ++ assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val, ++ "def"); ++ ++ ret = sss_certmap_add_rule(ctx, 87, "KRB5:aGlq", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, ++ SAN_X400_ADDRESS); ++ assert_int_equal( ++ ctx->prio_list->rule_list->parsed_match_rule->san->bin_val_len, ++ 3); ++ assert_memory_equal( ++ ctx->prio_list->rule_list->parsed_match_rule->san->bin_val, ++ "hij", 3); ++ ++ ret = sss_certmap_add_rule(ctx, 86, "KRB5:klm", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, ++ SAN_STRING_OTHER_NAME); ++ assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val, ++ "klm"); ++ assert_string_equal("1.2.3.4", ++ ctx->prio_list->rule_list->parsed_match_rule->san->str_other_name_oid); ++ ++ talloc_free(ctx); ++} ++ ++static void test_check_ad_attr_name(void **state) ++{ ++ char *res; ++ ++ res = check_ad_attr_name(NULL, NULL); ++ assert_null(res); ++ ++ res = check_ad_attr_name(NULL, ""); ++ assert_null(res); ++ ++ res = check_ad_attr_name(NULL, "dsddqwdas"); ++ assert_null(res); ++ ++ res = check_ad_attr_name(NULL, "dsddq=wdas"); ++ assert_null(res); ++ ++ res = check_ad_attr_name(NULL, "CN=abc"); ++ assert_null(res); ++ ++ res = check_ad_attr_name(NULL, "O=xyz"); ++ assert_null(res); ++ ++ res = check_ad_attr_name(NULL, "ST=def"); ++ assert_non_null(res); ++ assert_string_equal(res, "S=def"); ++ talloc_free(res); ++} ++ ++const uint8_t test_cert_der[] = { ++0x30, 0x82, 0x04, 0x09, 0x30, 0x82, 0x02, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x09, ++0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, ++0x34, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, ++0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, ++0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, ++0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x34, 0x32, 0x38, 0x31, ++0x30, 0x32, 0x31, 0x31, 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x32, 0x38, 0x31, 0x30, ++0x32, 0x31, 0x31, 0x31, 0x5a, 0x30, 0x32, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, ++0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, ++0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x69, 0x70, 0x61, 0x2d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2e, ++0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, ++0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, ++0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x32, 0x92, 0xab, 0x47, 0xb8, ++0x0c, 0x13, 0x54, 0x4a, 0x1f, 0x1e, 0x29, 0x06, 0xff, 0xd0, 0x50, 0xcb, 0xf7, 0x5f, 0x79, 0x91, ++0x65, 0xb1, 0x39, 0x01, 0x83, 0x6a, 0xad, 0x9e, 0x77, 0x3b, 0xf3, 0x0d, 0xd7, 0xb9, 0xf6, 0xdc, ++0x9e, 0x4a, 0x49, 0xa7, 0xd0, 0x66, 0x72, 0xcc, 0xbf, 0x77, 0xd6, 0xde, 0xa9, 0xfe, 0x67, 0x96, ++0xcc, 0x49, 0xf1, 0x37, 0x23, 0x2e, 0xc4, 0x50, 0xf4, 0xeb, 0xba, 0x62, 0xd4, 0x23, 0x4d, 0xf3, ++0x37, 0x38, 0x82, 0xee, 0x3b, 0x3f, 0x2c, 0xd0, 0x80, 0x9b, 0x17, 0xaa, 0x9b, 0xeb, 0xa6, 0xdd, ++0xf6, 0x15, 0xff, 0x06, 0xb2, 0xce, 0xff, 0xdf, 0x8a, 0x9e, 0x95, 0x85, 0x49, 0x1f, 0x84, 0xfd, ++0x81, 0x26, 0xce, 0x06, 0x32, 0x0d, 0x36, 0xca, 0x7c, 0x15, 0x81, 0x68, 0x6b, 0x8f, 0x3e, 0xb3, ++0xa2, 0xfc, 0xae, 0xaf, 0xc2, 0x44, 0x58, 0x15, 0x95, 0x40, 0xfc, 0x56, 0x19, 0x91, 0x80, 0xed, ++0x42, 0x11, 0x66, 0x04, 0xef, 0x3c, 0xe0, 0x76, 0x33, 0x4b, 0x83, 0xfa, 0x7e, 0xb4, 0x47, 0xdc, ++0xfb, 0xed, 0x46, 0xa5, 0x8d, 0x0a, 0x66, 0x87, 0xa5, 0xef, 0x7b, 0x74, 0x62, 0xac, 0xbe, 0x73, ++0x36, 0xc9, 0xb4, 0xfe, 0x20, 0xc4, 0x81, 0xf3, 0xfe, 0x78, 0x19, 0xa8, 0xd0, 0xaf, 0x7f, 0x81, ++0x72, 0x24, 0x61, 0xd9, 0x76, 0x93, 0xe3, 0x0b, 0xd2, 0x4f, 0x19, 0x17, 0x33, 0x57, 0xd4, 0x82, ++0xb0, 0xf1, 0xa8, 0x03, 0xf6, 0x01, 0x99, 0xa9, 0xb8, 0x8c, 0x83, 0xc9, 0xba, 0x19, 0x87, 0xea, ++0xd6, 0x3b, 0x06, 0xeb, 0x4c, 0xf7, 0xf1, 0xe5, 0x28, 0xa9, 0x10, 0xb6, 0x46, 0xde, 0xe1, 0xe1, ++0x3f, 0xc1, 0xcc, 0x72, 0xbe, 0x2a, 0x43, 0xc6, 0xf6, 0xd0, 0xb5, 0xa0, 0xc4, 0x24, 0x6e, 0x4f, ++0xbd, 0xec, 0x22, 0x8a, 0x07, 0x11, 0x3d, 0xf9, 0xd3, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, ++0x82, 0x01, 0x26, 0x30, 0x82, 0x01, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, ++0x30, 0x16, 0x80, 0x14, 0xf2, 0x9d, 0x42, 0x4e, 0x0f, 0xc4, 0x48, 0x25, 0x58, 0x2f, 0x1c, 0xce, ++0x0f, 0xa1, 0x3f, 0x22, 0xc8, 0x55, 0xc8, 0x91, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, ++0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, ++0x05, 0x07, 0x30, 0x01, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, 0x61, ++0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x63, 0x61, ++0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, ++0x04, 0x03, 0x02, 0x04, 0xf0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, ++0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, ++0x05, 0x07, 0x03, 0x02, 0x30, 0x74, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x6d, 0x30, 0x6b, 0x30, ++0x69, 0xa0, 0x31, 0xa0, 0x2f, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, ++0x61, 0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x69, ++0x70, 0x61, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x52, 0x4c, ++0x2e, 0x62, 0x69, 0x6e, 0xa2, 0x34, 0xa4, 0x32, 0x30, 0x30, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, ++0x55, 0x04, 0x0a, 0x0c, 0x05, 0x69, 0x70, 0x61, 0x63, 0x61, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, ++0x55, 0x04, 0x03, 0x0c, 0x15, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, ++0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, ++0x0e, 0x04, 0x16, 0x04, 0x14, 0x2d, 0x2b, 0x3f, 0xcb, 0xf5, 0xb2, 0xff, 0x32, 0x2c, 0xa8, 0xc2, ++0x1c, 0xdd, 0xbd, 0x8c, 0x80, 0x1e, 0xdd, 0x31, 0x82, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ++0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x47, 0x2e, ++0x50, 0xa7, 0x4d, 0x1d, 0x53, 0x0f, 0xc9, 0x71, 0x42, 0x0c, 0xe5, 0xda, 0x7d, 0x49, 0x64, 0xe7, ++0xab, 0xc8, 0xdf, 0xdf, 0x02, 0xc1, 0x87, 0xd1, 0x5b, 0xde, 0xda, 0x6f, 0x2b, 0xe4, 0xf0, 0xbe, ++0xba, 0x09, 0xdf, 0x02, 0x85, 0x0b, 0x8a, 0xe6, 0x9b, 0x06, 0x7d, 0x69, 0x38, 0x6c, 0x72, 0xff, ++0x4c, 0x7b, 0x2a, 0x0d, 0x3f, 0x23, 0x2f, 0x16, 0x46, 0xff, 0x05, 0x93, 0xb0, 0xea, 0x24, 0x28, ++0xd7, 0x12, 0xa1, 0x57, 0xb8, 0x59, 0x19, 0x25, 0xf3, 0x43, 0x0a, 0xd3, 0xfd, 0x0f, 0x37, 0x8d, ++0xb8, 0xca, 0x15, 0xe7, 0x48, 0x8a, 0xa0, 0xc7, 0xc7, 0x4b, 0x7f, 0x01, 0x3c, 0x58, 0xd7, 0x37, ++0xe5, 0xff, 0x7d, 0x2b, 0x01, 0xac, 0x0d, 0x9f, 0x51, 0x6a, 0xe5, 0x40, 0x24, 0xe6, 0x5e, 0x55, ++0x0d, 0xf7, 0xb8, 0x2f, 0x42, 0xac, 0x6d, 0xe5, 0x29, 0x6b, 0xc6, 0x0b, 0xa4, 0xbf, 0x19, 0xbd, ++0x39, 0x27, 0xee, 0xfe, 0xc5, 0xb3, 0xdb, 0x62, 0xd4, 0xbe, 0xd2, 0x47, 0xba, 0x96, 0x30, 0x5a, ++0xfd, 0x62, 0x00, 0xb8, 0x27, 0x5d, 0x2f, 0x3a, 0x94, 0x0b, 0x95, 0x35, 0x85, 0x40, 0x2c, 0xbc, ++0x67, 0xdf, 0x8a, 0xf9, 0xf1, 0x7b, 0x19, 0x96, 0x3e, 0x42, 0x48, 0x13, 0x23, 0x04, 0x95, 0xa9, ++0x6b, 0x11, 0x33, 0x81, 0x47, 0x5a, 0x83, 0x72, 0xf6, 0x20, 0xfa, 0x8e, 0x41, 0x7b, 0x8f, 0x77, ++0x47, 0x7c, 0xc7, 0x5d, 0x46, 0xf4, 0x4f, 0xfd, 0x81, 0x0a, 0xae, 0x39, 0x27, 0xb6, 0x6a, 0x26, ++0x63, 0xb1, 0xd3, 0xbf, 0x55, 0x83, 0x82, 0x9b, 0x36, 0x6c, 0x33, 0x64, 0x0f, 0x50, 0xc0, 0x55, ++0x94, 0x13, 0xc3, 0x85, 0xf4, 0xd5, 0x71, 0x65, 0xd0, 0xc0, 0xdd, 0xfc, 0xe6, 0xec, 0x9c, 0x5b, ++0xf0, 0x11, 0xb5, 0x2c, 0xf3, 0x48, 0xc1, 0x36, 0x8c, 0xa2, 0x96, 0x48, 0x84}; ++ ++const uint8_t test_cert2_der[] = { ++0x30, 0x82, 0x06, 0x98, 0x30, 0x82, 0x05, 0x80, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x61, ++0x22, 0x88, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ++0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a, ++0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65, ++0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, ++0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, ++0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x30, ++0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a, ++0x17, 0x0d, 0x31, 0x37, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a, 0x30, ++0x70, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, ++0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, ++0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x0e, 0x30, 0x0c, ++0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x31, 0x0c, 0x30, 0x0a, ++0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x20, 0x75, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, ++0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e, ++0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, ++0x6e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, ++0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, ++0x01, 0x00, 0x9c, 0xcf, 0x36, 0x99, 0xde, 0x63, 0x74, 0x2b, 0x77, 0x25, 0x9e, 0x24, 0xd9, 0x77, ++0x4b, 0x5f, 0x98, 0xc0, 0x8c, 0xd7, 0x20, 0x91, 0xc0, 0x1c, 0xe8, 0x37, 0x45, 0xbf, 0x3c, 0xd9, ++0x33, 0xbd, 0xe9, 0xde, 0xc9, 0x5d, 0xd4, 0xcd, 0x06, 0x0a, 0x0d, 0xd4, 0xf1, 0x7c, 0x74, 0x5b, ++0x29, 0xd5, 0x66, 0x9c, 0x2c, 0x9f, 0x6b, 0x1a, 0x0f, 0x0d, 0xe6, 0x6c, 0x62, 0xa5, 0x41, 0x4f, ++0xc3, 0xa4, 0x88, 0x27, 0x11, 0x5d, 0xb7, 0xb1, 0xfb, 0xf8, 0x8d, 0xee, 0x43, 0x8d, 0x93, 0xb5, ++0x8c, 0xb4, 0x34, 0x06, 0xf5, 0xe9, 0x2f, 0x5a, 0x26, 0x68, 0xd7, 0x43, 0x60, 0x82, 0x5e, 0x22, ++0xa7, 0xc6, 0x34, 0x40, 0x19, 0xa5, 0x8e, 0xf0, 0x58, 0x9f, 0x16, 0x2d, 0x43, 0x3f, 0x0c, 0xda, ++0xe2, 0x23, 0xf6, 0x09, 0x2a, 0x5e, 0xbd, 0x84, 0x27, 0xc8, 0xab, 0xd5, 0x70, 0xf8, 0x3d, 0x9c, ++0x14, 0xc2, 0xc2, 0xa2, 0x77, 0xe8, 0x44, 0x73, 0x10, 0x01, 0x34, 0x40, 0x1f, 0xc6, 0x2f, 0xa0, ++0x70, 0xee, 0x2f, 0xd5, 0x4b, 0xbe, 0x4c, 0xc7, 0x45, 0xf7, 0xac, 0x9c, 0xc3, 0x68, 0x5b, 0x1d, ++0x5a, 0x4b, 0x77, 0x65, 0x76, 0xe4, 0xb3, 0x92, 0xf4, 0x84, 0x0a, 0x9e, 0x6a, 0x9c, 0xc9, 0x53, ++0x42, 0x9f, 0x6d, 0xfe, 0xf9, 0xf5, 0xf2, 0x9a, 0x15, 0x50, 0x47, 0xef, 0xf4, 0x06, 0x59, 0xc8, ++0x50, 0x48, 0x4b, 0x46, 0x95, 0x68, 0x25, 0xc5, 0xbd, 0x4f, 0x65, 0x34, 0x00, 0xfc, 0x31, 0x69, ++0xf8, 0x3e, 0xe0, 0x20, 0x83, 0x41, 0x27, 0x0b, 0x5c, 0x46, 0x98, 0x14, 0xf0, 0x07, 0xde, 0x02, ++0x17, 0xb1, 0xd2, 0x9c, 0xbe, 0x1c, 0x0d, 0x56, 0x22, 0x1b, 0x02, 0xfe, 0xda, 0x69, 0xb9, 0xef, ++0x91, 0x37, 0x39, 0x7f, 0x24, 0xda, 0xc4, 0x81, 0x5e, 0x82, 0x31, 0x2f, 0x98, 0x1d, 0xf7, 0x73, ++0x5b, 0x23, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x03, 0x5d, 0x30, 0x82, 0x03, 0x59, 0x30, ++0x3d, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x07, 0x04, 0x30, 0x30, 0x2e, ++0x06, 0x26, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x08, 0x87, 0x85, 0xa1, 0x23, 0x84, ++0xc8, 0xb2, 0x26, 0x83, 0x9d, 0x9d, 0x21, 0x82, 0xd4, 0xa6, 0x1b, 0x86, 0xa3, 0xba, 0x37, 0x81, ++0x10, 0x85, 0x89, 0xd5, 0x02, 0xd6, 0x8f, 0x24, 0x02, 0x01, 0x64, 0x02, 0x01, 0x02, 0x30, 0x29, ++0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x22, 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, ++0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x06, 0x0a, 0x2b, ++0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, ++0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x35, 0x06, 0x09, 0x2b, 0x06, 0x01, ++0x04, 0x01, 0x82, 0x37, 0x15, 0x0a, 0x04, 0x28, 0x30, 0x26, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, ++0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, ++0x03, 0x04, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04, ++0x30, 0x81, 0x94, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x04, 0x81, ++0x86, 0x30, 0x81, 0x83, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, ++0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d, 0x30, 0x0b, ++0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60, ++0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x19, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, ++0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, ++0x01, 0x05, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x07, ++0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, ++0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, ++0x0d, 0x03, 0x04, 0x02, 0x02, 0x02, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, ++0x04, 0x14, 0x49, 0xac, 0xad, 0xe0, 0x65, 0x30, 0xc4, 0xce, 0xa0, 0x09, 0x03, 0x5b, 0xad, 0x4a, ++0x7b, 0x49, 0x5e, 0xc9, 0x6c, 0xb4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, ++0x16, 0x80, 0x14, 0x62, 0x50, 0xb6, 0x8d, 0xa1, 0xe6, 0x2d, 0x91, 0xbf, 0xb0, 0x54, 0x4d, 0x8f, ++0xa8, 0xca, 0x10, 0xae, 0xb8, 0xdd, 0x54, 0x30, 0x81, 0xcc, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, ++0x81, 0xc4, 0x30, 0x81, 0xc1, 0x30, 0x81, 0xbe, 0xa0, 0x81, 0xbb, 0xa0, 0x81, 0xb8, 0x86, 0x81, ++0xb5, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41, ++0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x61, ++0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x44, 0x50, 0x2c, ++0x43, 0x4e, 0x3d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, ++0x32, 0x30, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, ++0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, ++0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43, ++0x3d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x3f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, ++0x74, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, ++0x3f, 0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, ++0x73, 0x3d, 0x63, 0x52, 0x4c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, ++0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x30, 0x81, 0xbe, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, ++0x07, 0x01, 0x01, 0x04, 0x81, 0xb1, 0x30, 0x81, 0xae, 0x30, 0x81, 0xab, 0x06, 0x08, 0x2b, 0x06, ++0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x81, 0x9e, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, ++0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, ++0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x49, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x50, 0x75, ++0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, 0x32, 0x30, 0x53, 0x65, 0x72, ++0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, ++0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, ++0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43, 0x3d, 0x64, 0x65, 0x76, 0x65, ++0x6c, 0x3f, 0x63, 0x41, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3f, ++0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, ++0x3d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x75, ++0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x3f, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x38, ++0x30, 0x36, 0xa0, 0x1c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03, ++0xa0, 0x0e, 0x0c, 0x0c, 0x74, 0x75, 0x31, 0x40, 0x61, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, ++0x81, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, ++0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, ++0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x41, 0x45, 0x0a, 0x6d, ++0xbb, 0x7f, 0x5c, 0x07, 0x0c, 0xc9, 0xb0, 0x39, 0x55, 0x6d, 0x7c, 0xb5, 0x02, 0xcd, 0xe8, 0xb2, ++0xe5, 0x02, 0x94, 0x77, 0x60, 0xdb, 0xd1, 0xaf, 0x1d, 0xdb, 0x44, 0x5f, 0xce, 0x83, 0xdb, 0x80, ++0x2e, 0xe2, 0xb2, 0x08, 0x25, 0x82, 0x14, 0xcb, 0x48, 0x95, 0x20, 0x13, 0x6c, 0xa9, 0xaa, 0xf8, ++0x31, 0x56, 0xed, 0xc0, 0x3b, 0xd4, 0xae, 0x2e, 0xe3, 0x8f, 0x05, 0xfc, 0xab, 0x5f, 0x2a, 0x69, ++0x23, 0xbc, 0xb8, 0x8c, 0xec, 0x2d, 0xa9, 0x0b, 0x86, 0x95, 0x73, 0x73, 0xdb, 0x17, 0xce, 0xc6, ++0xae, 0xc5, 0xb4, 0xc1, 0x25, 0x87, 0x3b, 0x67, 0x43, 0x9e, 0x87, 0x5a, 0xe6, 0xb9, 0xa0, 0x28, ++0x12, 0x3d, 0xa8, 0x2e, 0xd7, 0x5e, 0xef, 0x65, 0x2d, 0xe6, 0xa5, 0x67, 0x84, 0xac, 0xfd, 0x31, ++0xc1, 0x78, 0xd8, 0x72, 0x51, 0xa2, 0x88, 0x55, 0x0f, 0x97, 0x47, 0x93, 0x07, 0xea, 0x8a, 0x53, ++0x27, 0x4e, 0x34, 0x54, 0x34, 0x1f, 0xa0, 0x6a, 0x03, 0x44, 0xfb, 0x23, 0x61, 0x8e, 0x87, 0x8e, ++0x3c, 0xd0, 0x8f, 0xae, 0xe4, 0xcf, 0xee, 0x65, 0xa8, 0xba, 0x96, 0x68, 0x08, 0x1c, 0x60, 0xe2, ++0x4e, 0x11, 0xa3, 0x74, 0xb8, 0xa5, 0x4e, 0xea, 0x6a, 0x82, 0x4c, 0xc2, 0x4d, 0x63, 0x8e, 0x9f, ++0x7c, 0x2f, 0xa8, 0xc0, 0x62, 0xf8, 0xf7, 0xd9, 0x25, 0xc4, 0x91, 0xab, 0x4d, 0x6a, 0x44, 0xaf, ++0x75, 0x93, 0x53, 0x03, 0xa4, 0x99, 0xc8, 0xcd, 0x91, 0x89, 0x60, 0x75, 0x30, 0x99, 0x76, 0x05, ++0x5a, 0xa0, 0x03, 0xa7, 0xa1, 0x2c, 0x03, 0x04, 0x8f, 0xd4, 0x5a, 0x31, 0x52, 0x28, 0x5a, 0xe6, ++0xa2, 0xd3, 0x43, 0x21, 0x5b, 0xdc, 0xa2, 0x1d, 0x55, 0xa9, 0x48, 0xc5, 0xc4, 0xaa, 0xf3, 0x8b, ++0xe6, 0x3e, 0x75, 0x96, 0xe4, 0x3e, 0x64, 0xaf, 0xe8, 0xa7, 0x6a, 0xb6}; ++ ++void test_sss_cert_get_content(void **state) ++{ ++ int ret; ++ struct sss_cert_content *content; ++ ++ ret = sss_cert_get_content(NULL, test_cert_der, sizeof(test_cert_der), ++ &content); ++ assert_int_equal(ret , 0); ++ assert_non_null(content); ++ assert_non_null(content->issuer_str); ++ assert_string_equal(content->issuer_str, "CN=Certificate Authority,O=IPA.DEVEL"); ++ assert_non_null(content->subject_str); ++ assert_string_equal(content->subject_str, "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); ++ assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE ++ |SSS_KU_NON_REPUDIATION ++ |SSS_KU_KEY_ENCIPHERMENT ++ |SSS_KU_DATA_ENCIPHERMENT); ++ assert_non_null(content->extended_key_usage_oids); ++ assert_non_null(content->extended_key_usage_oids[0]); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.1", ++ discard_const(content->extended_key_usage_oids), true)); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.2", ++ discard_const(content->extended_key_usage_oids), true)); ++ assert_null(content->extended_key_usage_oids[2]); ++ assert_int_equal(content->cert_der_size, sizeof(test_cert_der)); ++ assert_memory_equal(content->cert_der, test_cert_der, sizeof(test_cert_der)); ++ ++ assert_non_null(content->issuer_rdn_list); ++ assert_string_equal(content->issuer_rdn_list[0], "O=IPA.DEVEL"); ++ assert_string_equal(content->issuer_rdn_list[1], "CN=Certificate Authority"); ++ assert_null(content->issuer_rdn_list[2]); ++ ++ assert_non_null(content->subject_rdn_list); ++ assert_string_equal(content->subject_rdn_list[0], "O=IPA.DEVEL"); ++ assert_string_equal(content->subject_rdn_list[1], "CN=ipa-devel.ipa.devel"); ++ assert_null(content->subject_rdn_list[2]); ++ ++ ++ talloc_free(content); ++} ++ ++void test_sss_cert_get_content_2(void **state) ++{ ++ int ret; ++ struct sss_cert_content *content; ++ struct san_list *i; ++ ++ ret = sss_cert_get_content(NULL, test_cert2_der, sizeof(test_cert2_der), ++ &content); ++ assert_int_equal(ret, 0); ++ assert_non_null(content); ++ assert_non_null(content->issuer_str); ++ assert_string_equal(content->issuer_str, ++ "CN=ad-AD-SERVER-CA,DC=ad,DC=devel"); ++ assert_non_null(content->subject_str); ++#if 0 ++FIXME: ++ assert_string_equal(content->subject_str, ++ "E=test.user@email.domain,CN=t u,CN=Users,DC=ad,DC=devel,DC=ad,DC=devel"); ++ //"CN=t u/emailAddress=test.user@email.domain,DC=ad,DC=devel"); ++#endif ++ assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE ++ |SSS_KU_KEY_ENCIPHERMENT); ++ assert_non_null(content->extended_key_usage_oids); ++ assert_non_null(content->extended_key_usage_oids[0]); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.2", ++ discard_const(content->extended_key_usage_oids), true)); ++ assert_true(string_in_list("1.3.6.1.5.5.7.3.4", ++ discard_const(content->extended_key_usage_oids), true)); ++ /* Can use Microsoft Encrypted File System OID */ ++ assert_true(string_in_list("1.3.6.1.4.1.311.10.3.4", ++ discard_const(content->extended_key_usage_oids), true)); ++ assert_null(content->extended_key_usage_oids[3]); ++ assert_int_equal(content->cert_der_size, sizeof(test_cert2_der)); ++ assert_memory_equal(content->cert_der, test_cert2_der, ++ sizeof(test_cert2_der)); ++ ++ assert_non_null(content->issuer_rdn_list); ++ assert_string_equal(content->issuer_rdn_list[0], "DC=devel"); ++ assert_string_equal(content->issuer_rdn_list[1], "DC=ad"); ++ assert_string_equal(content->issuer_rdn_list[2], "CN=ad-AD-SERVER-CA"); ++ assert_null(content->issuer_rdn_list[3]); ++ ++ assert_non_null(content->subject_rdn_list); ++ assert_string_equal(content->subject_rdn_list[0], "DC=devel"); ++ assert_string_equal(content->subject_rdn_list[1], "DC=ad"); ++ assert_string_equal(content->subject_rdn_list[2], "CN=Users"); ++ assert_string_equal(content->subject_rdn_list[3], "CN=t u"); ++ assert_string_equal(content->subject_rdn_list[4], ++ "E=test.user@email.domain"); ++ //"CN=t u/emailAddress=test.user@email.domain"); ++ assert_null(content->subject_rdn_list[5]); ++ ++ assert_non_null(content->san_list); ++ ++ DLIST_FOR_EACH(i, content->san_list) { ++ switch (i->san_opt) { ++ case SAN_RFC822_NAME: ++ assert_string_equal(i->val, "test.user@email.domain"); ++ assert_string_equal(i->short_name, "test.user"); ++ break; ++ case SAN_STRING_OTHER_NAME: ++ assert_string_equal(i->other_name_oid, "1.3.6.1.4.1.311.20.2.3"); ++ assert_int_equal(i->bin_val_len, 14); ++ assert_memory_equal(i->bin_val, "\f\ftu1@ad.devel", 14); ++ break; ++ case SAN_NT: ++ case SAN_PRINCIPAL: ++ assert_string_equal(i->val, "tu1@ad.devel"); ++ assert_string_equal(i->short_name, "tu1"); ++ break; ++ default: ++ assert_true(false); ++ } ++ } ++ ++ talloc_free(content); ++} ++ ++static void test_sss_certmap_match_cert(void **state) ++{ ++ struct sss_certmap_ctx *ctx; ++ int ret; ++ size_t c; ++ ++ struct match_tests { ++ const char *rule; ++ int result; ++ } match_tests[] = { ++ {"KRB5:digitalSignature", 0}, ++ {"KRB5:digitalSignature,nonRepudiation", 0}, ++ {"KRB5:digitalSignature,cRLSign", ENOENT}, ++ {"KRB5:clientAuth", 0}, ++ {"KRB5:clientAuth,OCSPSigning", ENOENT}, ++ {"KRB5:clientAuth,serverAuth", 0}, ++ {NULL, 0} ++ }; ++ ++ struct match_tests match_tests_2[] = { ++ {"KRB5:digitalSignature", 0}, ++ {"KRB5:keyEncipherment", 0}, ++ {"KRB5:digitalSignature,keyEncipherment", 0}, ++ {"KRB5:digitalSignature,keyEncipherment,cRLSign", ENOENT}, ++ {"KRB5:clientAuth", 0}, ++ {"KRB5:clientAuth,1.3.6.1.4.1.311.10.3.4", 0}, ++ {"KRB5:clientAuth,1.3.6.1.4.1.311.10.3.41", ENOENT}, ++ {"KRB5:tu1", 0}, ++ {"KRB5:tu1", 0}, ++ {"KRB5:tu1", 0}, ++ {"KRB5:tu1", ENOENT}, ++ {"KRB5:^tu1@ad.devel$", 0}, ++ {"KRB5:tu", ENOENT}, ++ {"KRB5:test.user", 0}, ++ {"KRB5:test.usertu1", 0}, ++ {"KRB5:||test.usertu1", 0}, ++ {"KRB5:&&tu1tu1", ENOENT}, ++ {"KRB5:||tu1tu1", 0}, ++ {"KRB5:MTIz", ENOENT}, /* 123 */ ++ {"KRB5:DAx0dTFAYWQuZGV2ZWw=", 0}, /* "\f\ftu1@ad.devel" */ ++ {"KRB5:DAx0dTFAYWQuZGV2ZWx4", ENOENT}, /* "\f\ftu1@ad.develx" */ ++ {"KRB5:dHUxQGFkLmRldmVs", 0}, /* "tu1@ad.devel" */ ++ {"KRB5:test", ENOENT}, ++ {"KRB5:tu1@ad", 0}, ++ /* Fails becasue the NT principal SAN starts with binary values */ ++ {"KRB5:^tu1@ad.devel$", ENOENT}, ++ {NULL, 0} ++ }; ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, "KRB5:xyzxyz", ++ NULL, NULL); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der)); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = sss_certmap_add_rule(ctx, 1, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ NULL, NULL); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der)); ++ assert_int_equal(ret, 0); ++ ++ sss_certmap_free_ctx(ctx); ++ ++ for (c = 0; match_tests[c].rule != NULL; c++) { ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, match_tests[c].rule, NULL, NULL); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der)); ++ assert_int_equal(ret, match_tests[c].result); ++ ++ sss_certmap_free_ctx(ctx); ++ } ++ ++ for (c = 0; match_tests_2[c].rule != NULL; c++) { ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ++ print_error("Checking matching rule [%s]\n", match_tests_2[c].rule); ++ ++ ret = sss_certmap_add_rule(ctx, 1, match_tests_2[c].rule, NULL, NULL); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_certmap_match_cert(ctx, discard_const(test_cert2_der), ++ sizeof(test_cert2_der)); ++ assert_int_equal(ret, match_tests_2[c].result); ++ ++ sss_certmap_free_ctx(ctx); ++ } ++} ++ ++static void test_sss_certmap_add_mapping_rule(void **state) ++{ ++ struct sss_certmap_ctx *ctx; ++ int ret; ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 1, NULL, "FWEAWEF:fwefwe", NULL); ++ assert_int_equal(ret, ESRCH); ++ ++ ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc", NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); ++ assert_int_equal(comp_string, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->type); ++ assert_string_equal("abc", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc{issuer_dn}", NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); ++ assert_int_equal(comp_string, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->type); ++ assert_string_equal("abc", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->val); ++ assert_int_equal(comp_template, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type); ++ assert_string_equal("issuer_dn", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1, NULL, "{issuer_dn}a:b{{c}}", NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); ++ assert_int_equal(comp_template, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->type); ++ assert_string_equal("issuer_dn", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->val); ++ assert_int_equal(comp_string, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type); ++ assert_string_equal("a:b{c}", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val); ++ talloc_free(ctx); ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:{issuer_dn}{subject_dn}", ++ NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); ++ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); ++ assert_int_equal(comp_template, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->type); ++ assert_string_equal("issuer_dn", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->val); ++ assert_int_equal(comp_template, ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type); ++ assert_string_equal("subject_dn", ++ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val); ++ talloc_free(ctx); ++} ++ ++#define TEST_CERT_BIN \ ++ "\\30\\82\\04\\09\\30\\82\\02\\f1\\a0\\03\\02\\01\\02\\02\\01\\09" \ ++ "\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\0b\\05\\00\\30" \ ++ "\\34\\31\\12\\30\\10\\06\\03\\55\\04\\0a\\0c\\09\\49\\50\\41\\2e" \ ++ "\\44\\45\\56\\45\\4c\\31\\1e\\30\\1c\\06\\03\\55\\04\\03\\0c\\15" \ ++ "\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\20\\41\\75\\74\\68" \ ++ "\\6f\\72\\69\\74\\79\\30\\1e\\17\\0d\\31\\35\\30\\34\\32\\38\\31" \ ++ "\\30\\32\\31\\31\\31\\5a\\17\\0d\\31\\37\\30\\34\\32\\38\\31\\30" \ ++ "\\32\\31\\31\\31\\5a\\30\\32\\31\\12\\30\\10\\06\\03\\55\\04\\0a" \ ++ "\\0c\\09\\49\\50\\41\\2e\\44\\45\\56\\45\\4c\\31\\1c\\30\\1a\\06" \ ++ "\\03\\55\\04\\03\\0c\\13\\69\\70\\61\\2d\\64\\65\\76\\65\\6c\\2e" \ ++ "\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\30\\82\\01\\22\\30\\0d\\06" \ ++ "\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\01\\05\\00\\03\\82\\01\\0f" \ ++ "\\00\\30\\82\\01\\0a\\02\\82\\01\\01\\00\\b2\\32\\92\\ab\\47\\b8" \ ++ "\\0c\\13\\54\\4a\\1f\\1e\\29\\06\\ff\\d0\\50\\cb\\f7\\5f\\79\\91" \ ++ "\\65\\b1\\39\\01\\83\\6a\\ad\\9e\\77\\3b\\f3\\0d\\d7\\b9\\f6\\dc" \ ++ "\\9e\\4a\\49\\a7\\d0\\66\\72\\cc\\bf\\77\\d6\\de\\a9\\fe\\67\\96" \ ++ "\\cc\\49\\f1\\37\\23\\2e\\c4\\50\\f4\\eb\\ba\\62\\d4\\23\\4d\\f3" \ ++ "\\37\\38\\82\\ee\\3b\\3f\\2c\\d0\\80\\9b\\17\\aa\\9b\\eb\\a6\\dd" \ ++ "\\f6\\15\\ff\\06\\b2\\ce\\ff\\df\\8a\\9e\\95\\85\\49\\1f\\84\\fd" \ ++ "\\81\\26\\ce\\06\\32\\0d\\36\\ca\\7c\\15\\81\\68\\6b\\8f\\3e\\b3" \ ++ "\\a2\\fc\\ae\\af\\c2\\44\\58\\15\\95\\40\\fc\\56\\19\\91\\80\\ed" \ ++ "\\42\\11\\66\\04\\ef\\3c\\e0\\76\\33\\4b\\83\\fa\\7e\\b4\\47\\dc" \ ++ "\\fb\\ed\\46\\a5\\8d\\0a\\66\\87\\a5\\ef\\7b\\74\\62\\ac\\be\\73" \ ++ "\\36\\c9\\b4\\fe\\20\\c4\\81\\f3\\fe\\78\\19\\a8\\d0\\af\\7f\\81" \ ++ "\\72\\24\\61\\d9\\76\\93\\e3\\0b\\d2\\4f\\19\\17\\33\\57\\d4\\82" \ ++ "\\b0\\f1\\a8\\03\\f6\\01\\99\\a9\\b8\\8c\\83\\c9\\ba\\19\\87\\ea" \ ++ "\\d6\\3b\\06\\eb\\4c\\f7\\f1\\e5\\28\\a9\\10\\b6\\46\\de\\e1\\e1" \ ++ "\\3f\\c1\\cc\\72\\be\\2a\\43\\c6\\f6\\d0\\b5\\a0\\c4\\24\\6e\\4f" \ ++ "\\bd\\ec\\22\\8a\\07\\11\\3d\\f9\\d3\\15\\02\\03\\01\\00\\01\\a3" \ ++ "\\82\\01\\26\\30\\82\\01\\22\\30\\1f\\06\\03\\55\\1d\\23\\04\\18" \ ++ "\\30\\16\\80\\14\\f2\\9d\\42\\4e\\0f\\c4\\48\\25\\58\\2f\\1c\\ce" \ ++ "\\0f\\a1\\3f\\22\\c8\\55\\c8\\91\\30\\3b\\06\\08\\2b\\06\\01\\05" \ ++ "\\05\\07\\01\\01\\04\\2f\\30\\2d\\30\\2b\\06\\08\\2b\\06\\01\\05" \ ++ "\\05\\07\\30\\01\\86\\1f\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70\\61" \ ++ "\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\63\\61" \ ++ "\\2f\\6f\\63\\73\\70\\30\\0e\\06\\03\\55\\1d\\0f\\01\\01\\ff\\04" \ ++ "\\04\\03\\02\\04\\f0\\30\\1d\\06\\03\\55\\1d\\25\\04\\16\\30\\14" \ ++ "\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\01\\06\\08\\2b\\06\\01\\05" \ ++ "\\05\\07\\03\\02\\30\\74\\06\\03\\55\\1d\\1f\\04\\6d\\30\\6b\\30" \ ++ "\\69\\a0\\31\\a0\\2f\\86\\2d\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70" \ ++ "\\61\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\69" \ ++ "\\70\\61\\2f\\63\\72\\6c\\2f\\4d\\61\\73\\74\\65\\72\\43\\52\\4c" \ ++ "\\2e\\62\\69\\6e\\a2\\34\\a4\\32\\30\\30\\31\\0e\\30\\0c\\06\\03" \ ++ "\\55\\04\\0a\\0c\\05\\69\\70\\61\\63\\61\\31\\1e\\30\\1c\\06\\03" \ ++ "\\55\\04\\03\\0c\\15\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65" \ ++ "\\20\\41\\75\\74\\68\\6f\\72\\69\\74\\79\\30\\1d\\06\\03\\55\\1d" \ ++ "\\0e\\04\\16\\04\\14\\2d\\2b\\3f\\cb\\f5\\b2\\ff\\32\\2c\\a8\\c2" \ ++ "\\1c\\dd\\bd\\8c\\80\\1e\\dd\\31\\82\\30\\0d\\06\\09\\2a\\86\\48" \ ++ "\\86\\f7\\0d\\01\\01\\0b\\05\\00\\03\\82\\01\\01\\00\\9a\\47\\2e" \ ++ "\\50\\a7\\4d\\1d\\53\\0f\\c9\\71\\42\\0c\\e5\\da\\7d\\49\\64\\e7" \ ++ "\\ab\\c8\\df\\df\\02\\c1\\87\\d1\\5b\\de\\da\\6f\\2b\\e4\\f0\\be" \ ++ "\\ba\\09\\df\\02\\85\\0b\\8a\\e6\\9b\\06\\7d\\69\\38\\6c\\72\\ff" \ ++ "\\4c\\7b\\2a\\0d\\3f\\23\\2f\\16\\46\\ff\\05\\93\\b0\\ea\\24\\28" \ ++ "\\d7\\12\\a1\\57\\b8\\59\\19\\25\\f3\\43\\0a\\d3\\fd\\0f\\37\\8d" \ ++ "\\b8\\ca\\15\\e7\\48\\8a\\a0\\c7\\c7\\4b\\7f\\01\\3c\\58\\d7\\37" \ ++ "\\e5\\ff\\7d\\2b\\01\\ac\\0d\\9f\\51\\6a\\e5\\40\\24\\e6\\5e\\55" \ ++ "\\0d\\f7\\b8\\2f\\42\\ac\\6d\\e5\\29\\6b\\c6\\0b\\a4\\bf\\19\\bd" \ ++ "\\39\\27\\ee\\fe\\c5\\b3\\db\\62\\d4\\be\\d2\\47\\ba\\96\\30\\5a" \ ++ "\\fd\\62\\00\\b8\\27\\5d\\2f\\3a\\94\\0b\\95\\35\\85\\40\\2c\\bc" \ ++ "\\67\\df\\8a\\f9\\f1\\7b\\19\\96\\3e\\42\\48\\13\\23\\04\\95\\a9" \ ++ "\\6b\\11\\33\\81\\47\\5a\\83\\72\\f6\\20\\fa\\8e\\41\\7b\\8f\\77" \ ++ "\\47\\7c\\c7\\5d\\46\\f4\\4f\\fd\\81\\0a\\ae\\39\\27\\b6\\6a\\26" \ ++ "\\63\\b1\\d3\\bf\\55\\83\\82\\9b\\36\\6c\\33\\64\\0f\\50\\c0\\55" \ ++ "\\94\\13\\c3\\85\\f4\\d5\\71\\65\\d0\\c0\\dd\\fc\\e6\\ec\\9c\\5b" \ ++ "\\f0\\11\\b5\\2c\\f3\\48\\c1\\36\\8c\\a2\\96\\48\\84" ++ ++#define TEST_CERT2_BIN \ ++ "\\30\\82\\06\\98\\30\\82\\05\\80\\a0\\03\\02\\01\\02\\02\\0a\\61" \ ++ "\\22\\88\\c2\\00\\00\\00\\00\\02\\a6\\30\\0d\\06\\09\\2a\\86\\48" \ ++ "\\86\\f7\\0d\\01\\01\\05\\05\\00\\30\\45\\31\\15\\30\\13\\06\\0a" \ ++ "\\09\\92\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\05\\64\\65\\76\\65" \ ++ "\\6c\\31\\12\\30\\10\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \ ++ "\\19\\16\\02\\61\\64\\31\\18\\30\\16\\06\\03\\55\\04\\03\\13\\0f" \ ++ "\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\30" \ ++ "\\1e\\17\\0d\\31\\36\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a" \ ++ "\\17\\0d\\31\\37\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a\\30" \ ++ "\\70\\31\\15\\30\\13\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \ ++ "\\19\\16\\05\\64\\65\\76\\65\\6c\\31\\12\\30\\10\\06\\0a\\09\\92" \ ++ "\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\02\\61\\64\\31\\0e\\30\\0c" \ ++ "\\06\\03\\55\\04\\03\\13\\05\\55\\73\\65\\72\\73\\31\\0c\\30\\0a" \ ++ "\\06\\03\\55\\04\\03\\13\\03\\74\\20\\75\\31\\25\\30\\23\\06\\09" \ ++ "\\2a\\86\\48\\86\\f7\\0d\\01\\09\\01\\16\\16\\74\\65\\73\\74\\2e" \ ++ "\\75\\73\\65\\72\\40\\65\\6d\\61\\69\\6c\\2e\\64\\6f\\6d\\61\\69" \ ++ "\\6e\\30\\82\\01\\22\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01" \ ++ "\\01\\01\\05\\00\\03\\82\\01\\0f\\00\\30\\82\\01\\0a\\02\\82\\01" \ ++ "\\01\\00\\9c\\cf\\36\\99\\de\\63\\74\\2b\\77\\25\\9e\\24\\d9\\77" \ ++ "\\4b\\5f\\98\\c0\\8c\\d7\\20\\91\\c0\\1c\\e8\\37\\45\\bf\\3c\\d9" \ ++ "\\33\\bd\\e9\\de\\c9\\5d\\d4\\cd\\06\\0a\\0d\\d4\\f1\\7c\\74\\5b" \ ++ "\\29\\d5\\66\\9c\\2c\\9f\\6b\\1a\\0f\\0d\\e6\\6c\\62\\a5\\41\\4f" \ ++ "\\c3\\a4\\88\\27\\11\\5d\\b7\\b1\\fb\\f8\\8d\\ee\\43\\8d\\93\\b5" \ ++ "\\8c\\b4\\34\\06\\f5\\e9\\2f\\5a\\26\\68\\d7\\43\\60\\82\\5e\\22" \ ++ "\\a7\\c6\\34\\40\\19\\a5\\8e\\f0\\58\\9f\\16\\2d\\43\\3f\\0c\\da" \ ++ "\\e2\\23\\f6\\09\\2a\\5e\\bd\\84\\27\\c8\\ab\\d5\\70\\f8\\3d\\9c" \ ++ "\\14\\c2\\c2\\a2\\77\\e8\\44\\73\\10\\01\\34\\40\\1f\\c6\\2f\\a0" \ ++ "\\70\\ee\\2f\\d5\\4b\\be\\4c\\c7\\45\\f7\\ac\\9c\\c3\\68\\5b\\1d" \ ++ "\\5a\\4b\\77\\65\\76\\e4\\b3\\92\\f4\\84\\0a\\9e\\6a\\9c\\c9\\53" \ ++ "\\42\\9f\\6d\\fe\\f9\\f5\\f2\\9a\\15\\50\\47\\ef\\f4\\06\\59\\c8" \ ++ "\\50\\48\\4b\\46\\95\\68\\25\\c5\\bd\\4f\\65\\34\\00\\fc\\31\\69" \ ++ "\\f8\\3e\\e0\\20\\83\\41\\27\\0b\\5c\\46\\98\\14\\f0\\07\\de\\02" \ ++ "\\17\\b1\\d2\\9c\\be\\1c\\0d\\56\\22\\1b\\02\\fe\\da\\69\\b9\\ef" \ ++ "\\91\\37\\39\\7f\\24\\da\\c4\\81\\5e\\82\\31\\2f\\98\\1d\\f7\\73" \ ++ "\\5b\\23\\02\\03\\01\\00\\01\\a3\\82\\03\\5d\\30\\82\\03\\59\\30" \ ++ "\\3d\\06\\09\\2b\\06\\01\\04\\01\\82\\37\\15\\07\\04\\30\\30\\2e" \ ++ "\\06\\26\\2b\\06\\01\\04\\01\\82\\37\\15\\08\\87\\85\\a1\\23\\84" \ ++ "\\c8\\b2\\26\\83\\9d\\9d\\21\\82\\d4\\a6\\1b\\86\\a3\\ba\\37\\81" \ ++ "\\10\\85\\89\\d5\\02\\d6\\8f\\24\\02\\01\\64\\02\\01\\02\\30\\29" \ ++ "\\06\\03\\55\\1d\\25\\04\\22\\30\\20\\06\\08\\2b\\06\\01\\05\\05" \ ++ "\\07\\03\\02\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\04\\06\\0a\\2b" \ ++ "\\06\\01\\04\\01\\82\\37\\0a\\03\\04\\30\\0e\\06\\03\\55\\1d\\0f" \ ++ "\\01\\01\\ff\\04\\04\\03\\02\\05\\a0\\30\\35\\06\\09\\2b\\06\\01" \ ++ "\\04\\01\\82\\37\\15\\0a\\04\\28\\30\\26\\30\\0a\\06\\08\\2b\\06" \ ++ "\\01\\05\\05\\07\\03\\02\\30\\0a\\06\\08\\2b\\06\\01\\05\\05\\07" \ ++ "\\03\\04\\30\\0c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\0a\\03\\04" \ ++ "\\30\\81\\94\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\09\\0f\\04\\81" \ ++ "\\86\\30\\81\\83\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01" \ ++ "\\2a\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\2d\\30\\0b" \ ++ "\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\16\\30\\0b\\06\\09\\60" \ ++ "\\86\\48\\01\\65\\03\\04\\01\\19\\30\\0b\\06\\09\\60\\86\\48\\01" \ ++ "\\65\\03\\04\\01\\02\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04" \ ++ "\\01\\05\\30\\0a\\06\\08\\2a\\86\\48\\86\\f7\\0d\\03\\07\\30\\07" \ ++ "\\06\\05\\2b\\0e\\03\\02\\07\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \ ++ "\\0d\\03\\02\\02\\02\\00\\80\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \ ++ "\\0d\\03\\04\\02\\02\\02\\00\\30\\1d\\06\\03\\55\\1d\\0e\\04\\16" \ ++ "\\04\\14\\49\\ac\\ad\\e0\\65\\30\\c4\\ce\\a0\\09\\03\\5b\\ad\\4a" \ ++ "\\7b\\49\\5e\\c9\\6c\\b4\\30\\1f\\06\\03\\55\\1d\\23\\04\\18\\30" \ ++ "\\16\\80\\14\\62\\50\\b6\\8d\\a1\\e6\\2d\\91\\bf\\b0\\54\\4d\\8f" \ ++ "\\a8\\ca\\10\\ae\\b8\\dd\\54\\30\\81\\cc\\06\\03\\55\\1d\\1f\\04" \ ++ "\\81\\c4\\30\\81\\c1\\30\\81\\be\\a0\\81\\bb\\a0\\81\\b8\\86\\81" \ ++ "\\b5\\6c\\64\\61\\70\\3a\\2f\\2f\\2f\\43\\4e\\3d\\61\\64\\2d\\41" \ ++ "\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\2c\\43\\4e\\3d\\61" \ ++ "\\64\\2d\\73\\65\\72\\76\\65\\72\\2c\\43\\4e\\3d\\43\\44\\50\\2c" \ ++ "\\43\\4e\\3d\\50\\75\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25" \ ++ "\\32\\30\\53\\65\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65" \ ++ "\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67" \ ++ "\\75\\72\\61\\74\\69\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43" \ ++ "\\3d\\64\\65\\76\\65\\6c\\3f\\63\\65\\72\\74\\69\\66\\69\\63\\61" \ ++ "\\74\\65\\52\\65\\76\\6f\\63\\61\\74\\69\\6f\\6e\\4c\\69\\73\\74" \ ++ "\\3f\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73" \ ++ "\\73\\3d\\63\\52\\4c\\44\\69\\73\\74\\72\\69\\62\\75\\74\\69\\6f" \ ++ "\\6e\\50\\6f\\69\\6e\\74\\30\\81\\be\\06\\08\\2b\\06\\01\\05\\05" \ ++ "\\07\\01\\01\\04\\81\\b1\\30\\81\\ae\\30\\81\\ab\\06\\08\\2b\\06" \ ++ "\\01\\05\\05\\07\\30\\02\\86\\81\\9e\\6c\\64\\61\\70\\3a\\2f\\2f" \ ++ "\\2f\\43\\4e\\3d\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52" \ ++ "\\2d\\43\\41\\2c\\43\\4e\\3d\\41\\49\\41\\2c\\43\\4e\\3d\\50\\75" \ ++ "\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25\\32\\30\\53\\65\\72" \ ++ "\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65\\72\\76\\69\\63\\65" \ ++ "\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67\\75\\72\\61\\74\\69" \ ++ "\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43\\3d\\64\\65\\76\\65" \ ++ "\\6c\\3f\\63\\41\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\3f" \ ++ "\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73\\73" \ ++ "\\3d\\63\\65\\72\\74\\69\\66\\69\\63\\61\\74\\69\\6f\\6e\\41\\75" \ ++ "\\74\\68\\6f\\72\\69\\74\\79\\30\\3f\\06\\03\\55\\1d\\11\\04\\38" \ ++ "\\30\\36\\a0\\1c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\14\\02\\03" \ ++ "\\a0\\0e\\0c\\0c\\74\\75\\31\\40\\61\\64\\2e\\64\\65\\76\\65\\6c" \ ++ "\\81\\16\\74\\65\\73\\74\\2e\\75\\73\\65\\72\\40\\65\\6d\\61\\69" \ ++ "\\6c\\2e\\64\\6f\\6d\\61\\69\\6e\\30\\0d\\06\\09\\2a\\86\\48\\86" \ ++ "\\f7\\0d\\01\\01\\05\\05\\00\\03\\82\\01\\01\\00\\41\\45\\0a\\6d" \ ++ "\\bb\\7f\\5c\\07\\0c\\c9\\b0\\39\\55\\6d\\7c\\b5\\02\\cd\\e8\\b2" \ ++ "\\e5\\02\\94\\77\\60\\db\\d1\\af\\1d\\db\\44\\5f\\ce\\83\\db\\80" \ ++ "\\2e\\e2\\b2\\08\\25\\82\\14\\cb\\48\\95\\20\\13\\6c\\a9\\aa\\f8" \ ++ "\\31\\56\\ed\\c0\\3b\\d4\\ae\\2e\\e3\\8f\\05\\fc\\ab\\5f\\2a\\69" \ ++ "\\23\\bc\\b8\\8c\\ec\\2d\\a9\\0b\\86\\95\\73\\73\\db\\17\\ce\\c6" \ ++ "\\ae\\c5\\b4\\c1\\25\\87\\3b\\67\\43\\9e\\87\\5a\\e6\\b9\\a0\\28" \ ++ "\\12\\3d\\a8\\2e\\d7\\5e\\ef\\65\\2d\\e6\\a5\\67\\84\\ac\\fd\\31" \ ++ "\\c1\\78\\d8\\72\\51\\a2\\88\\55\\0f\\97\\47\\93\\07\\ea\\8a\\53" \ ++ "\\27\\4e\\34\\54\\34\\1f\\a0\\6a\\03\\44\\fb\\23\\61\\8e\\87\\8e" \ ++ "\\3c\\d0\\8f\\ae\\e4\\cf\\ee\\65\\a8\\ba\\96\\68\\08\\1c\\60\\e2" \ ++ "\\4e\\11\\a3\\74\\b8\\a5\\4e\\ea\\6a\\82\\4c\\c2\\4d\\63\\8e\\9f" \ ++ "\\7c\\2f\\a8\\c0\\62\\f8\\f7\\d9\\25\\c4\\91\\ab\\4d\\6a\\44\\af" \ ++ "\\75\\93\\53\\03\\a4\\99\\c8\\cd\\91\\89\\60\\75\\30\\99\\76\\05" \ ++ "\\5a\\a0\\03\\a7\\a1\\2c\\03\\04\\8f\\d4\\5a\\31\\52\\28\\5a\\e6" \ ++ "\\a2\\d3\\43\\21\\5b\\dc\\a2\\1d\\55\\a9\\48\\c5\\c4\\aa\\f3\\8b" \ ++ "\\e6\\3e\\75\\96\\e4\\3e\\64\\af\\e8\\a7\\6a\\b6" ++ ++static void test_sss_certmap_get_search_filter(void **state) ++{ ++ int ret; ++ struct sss_certmap_ctx *ctx; ++ char *filter; ++ char **domains; ++ const char *dom_list[] = {"test.dom", NULL}; ++ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ++ ret = sss_certmap_add_rule(ctx, 100, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ "LDAP:rule100={issuer_dn}{subject_dn}", NULL); ++ assert_int_equal(ret, 0); ++ ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule100=CN=Certificate Authority,O=IPA.DEVEL" ++ "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); ++ assert_null(domains); ++ ++ ret = sss_certmap_add_rule(ctx, 99, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ "LDAP:rule99={issuer_dn}{subject_dn}", ++ dom_list); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule99=CN=Certificate Authority,O=IPA.DEVEL" ++ "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); ++ assert_non_null(domains); ++ assert_string_equal(domains[0], "test.dom"); ++ assert_null(domains[1]); ++ ++ ret = sss_certmap_add_rule(ctx, 98, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ "LDAP:rule98=userCertificate;binary={cert!bin}", ++ dom_list); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN); ++ assert_non_null(domains); ++ assert_string_equal(domains[0], "test.dom"); ++ assert_null(domains[1]); ++ ++ ret = sss_certmap_add_rule(ctx, 97, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ "LDAP:rule97={issuer_dn!nss_x500}{subject_dn}", ++ dom_list); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule97=O=IPA.DEVEL,CN=Certificate Authority" ++ "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); ++ assert_non_null(domains); ++ assert_string_equal(domains[0], "test.dom"); ++ assert_null(domains[1]); ++ ++ ret = sss_certmap_add_rule(ctx, 96, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ "LDAP:rule96={issuer_dn!nss_x500}{subject_dn!nss_x500}", ++ dom_list); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule96=O=IPA.DEVEL,CN=Certificate Authority" ++ "O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); ++ assert_non_null(domains); ++ assert_string_equal(domains[0], "test.dom"); ++ assert_null(domains[1]); ++ ++ ret = sss_certmap_add_rule(ctx, 95, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")"); ++ assert_null(domains); ++ ++ ret = sss_certmap_add_rule(ctx, 94, ++ "KRB5:CN=Certificate Authority,O=IPA.DEVEL", ++ "LDAP:rule94={issuer_dn!ad_x500}{subject_dn!ad_x500}", ++ dom_list); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), ++ sizeof(test_cert_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule94=O=IPA.DEVEL,CN=Certificate Authority" ++ "O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); ++ assert_non_null(domains); ++ assert_string_equal(domains[0], "test.dom"); ++ assert_null(domains[1]); ++ ++ ++ ret = sss_certmap_add_rule(ctx, 89, NULL, ++ "(rule89={subject_nt_principal})", ++ NULL); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), ++ sizeof(test_cert2_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "(rule89=tu1@ad.devel)"); ++ assert_null(domains); ++ ++ ret = sss_certmap_add_rule(ctx, 88, NULL, ++ "(rule88={subject_nt_principal.short_name})", ++ NULL); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), ++ sizeof(test_cert2_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "(rule88=tu1)"); ++ assert_null(domains); ++ ++ ret = sss_certmap_add_rule(ctx, 87, NULL, ++ "LDAP:rule87={issuer_dn!nss_x500}{subject_dn!nss_x500}", ++ NULL); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), ++ sizeof(test_cert2_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule87=DC=devel,DC=ad,CN=ad-AD-SERVER-CA" ++ "DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain"); ++ assert_null(domains); ++ ++ ret = sss_certmap_add_rule(ctx, 86, NULL, ++ "LDAP:rule86={issuer_dn!ad_x500}{subject_dn!ad_x500}", ++ NULL); ++ assert_int_equal(ret, 0); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), ++ sizeof(test_cert2_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "rule86=DC=devel,DC=ad,CN=ad-AD-SERVER-CA" ++ "DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain"); ++ assert_null(domains); ++ ++ ++ sss_certmap_free_ctx(ctx); ++ ++ /* check defaults when no rules are added yet */ ++ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(ctx); ++ assert_null(ctx->prio_list); ++ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), ++ sizeof(test_cert2_der), ++ &filter, &domains); ++ assert_int_equal(ret, 0); ++ assert_non_null(filter); ++ assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT2_BIN")"); ++ assert_null(domains); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ int rv; ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(test_sss_certmap_init), ++ cmocka_unit_test(test_sss_certmap_add_rule), ++ cmocka_unit_test(test_sss_certmap_add_matching_rule), ++ cmocka_unit_test(test_check_ad_attr_name), ++ cmocka_unit_test(test_sss_cert_get_content), ++ cmocka_unit_test(test_sss_cert_get_content_2), ++ cmocka_unit_test(test_sss_certmap_match_cert), ++ cmocka_unit_test(test_sss_certmap_add_mapping_rule), ++ cmocka_unit_test(test_sss_certmap_get_search_filter), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++#ifdef HAVE_NSS ++ nspr_nss_init(); ++#endif ++ ++ tests_set_cwd(); ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ ++#ifdef HAVE_NSS ++ /* Cleanup NSS and NSPR to make valgrind happy. */ ++ nspr_nss_cleanup(); ++#endif ++ ++ return rv; ++} +diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c +index 419857cc739d197493e46629d00aa5fb6cfde824..3914317de90f870fab42d2d72d8e2eb5ea8d9e14 100644 +--- a/src/tests/dlopen-tests.c ++++ b/src/tests/dlopen-tests.c +@@ -45,6 +45,7 @@ struct so { + { "libsss_idmap.so", { LIBPFX"libsss_idmap.so", NULL } }, + { "libsss_nss_idmap.so", { LIBPFX"libsss_nss_idmap.so", NULL } }, + { "libnss_sss.so", { LIBPFX"libnss_sss.so", NULL } }, ++ { "libsss_certmap.so", { LIBPFX"libsss_certmap.so", NULL } }, + { "pam_sss.so", { LIBPFX"pam_sss.so", NULL } }, + #ifdef BUILD_LIBWBCLIENT + { "libwbclient.so", { LIBPFX"libwbclient.so", NULL } }, +-- +2.9.3 + diff --git a/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch b/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch new file mode 100644 index 0000000..99a2bd8 --- /dev/null +++ b/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch @@ -0,0 +1,151 @@ +From b1336bdfeacf904c8fdec04e06d8b90ef9ad15b3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 15 Mar 2017 10:57:09 +0100 +Subject: [PATCH 05/15] certmap: add placeholder for OpenSSL implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 30 +++++++++++++++++++++-------- + src/lib/certmap/sss_cert_content_crypto.c | 32 +++++++++++++++++++++++++++++++ + src/lib/certmap/sss_certmap_int.h | 8 +++++--- + 3 files changed, 59 insertions(+), 11 deletions(-) + create mode 100644 src/lib/certmap/sss_cert_content_crypto.c + +diff --git a/Makefile.am b/Makefile.am +index 8ca12c10d2713b6a72361d84b25486500c79f407..7947b7a5fbe3ca1034baac1c13c53300994b1bf8 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -278,9 +278,12 @@ if HAVE_CMOCKA + simple-access-tests \ + krb5_common_test \ + test_iobuf \ +- sss_certmap_test \ + $(NULL) + ++if HAVE_NSS ++non_interactive_cmocka_based_tests += sss_certmap_test ++endif #HAVE_NSS ++ + if HAVE_LIBRESOLV + non_interactive_cmocka_based_tests += test_resolv_fake + endif # HAVE_LIBRESOLV +@@ -1715,7 +1718,6 @@ sssd_check_socket_activated_responders_LDADD = \ + $(NULL) + endif + +-if HAVE_NSS + pkgconfig_DATA += src/lib/certmap/sss_certmap.pc + libsss_certmap_la_DEPENDENCIES = src/lib/certmap/sss_certmap.exports + libsss_certmap_la_SOURCES = \ +@@ -1726,26 +1728,38 @@ libsss_certmap_la_SOURCES = \ + src/lib/certmap/sss_certmap_ldap_mapping.c \ + src/util/util_ext.c \ + src/util/cert/cert_common.c \ +- src/util/crypto/nss/nss_base64.c \ +- src/util/cert/nss/cert.c \ +- src/util/crypto/nss/nss_util.c \ + $(NULL) + libsss_certmap_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(TALLOC_CFLAGS) \ +- $(NSS_CFLAGS) \ + $(NULL) + libsss_certmap_la_LIBADD = \ + $(TALLOC_LIBS) \ +- $(NSS_LIBS) \ + $(NULL) + libsss_certmap_la_LDFLAGS = \ + -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \ + -version-info 0:0:0 + ++if HAVE_NSS ++libsss_certmap_la_SOURCES += \ ++ src/util/crypto/nss/nss_base64.c \ ++ src/util/cert/nss/cert.c \ ++ src/util/crypto/nss/nss_util.c \ ++ $(NULL) ++libsss_certmap_la_CFLAGS += $(NSS_CFLAGS) ++libsss_certmap_la_LIBADD += $(NSS_LIBS) ++else ++libsss_certmap_la_SOURCES += \ ++ src/util/crypto/libcrypto/crypto_base64.c \ ++ src/util/cert/libcrypto/cert.c \ ++ $(NULL) ++ ++libsss_certmap_la_CFLAGS += $(CRYPTO_CFLAGS) ++libsss_certmap_la_LIBADD += $(CRYPTO_LIBS) ++endif ++ + dist_noinst_DATA += src/lib/certmap/sss_certmap.exports + dist_noinst_HEADERS += src/lib/certmap/sss_certmap_int.h +-endif + + ################# + # Feature Tests # +diff --git a/src/lib/certmap/sss_cert_content_crypto.c b/src/lib/certmap/sss_cert_content_crypto.c +new file mode 100644 +index 0000000000000000000000000000000000000000..bddcf9bce986bd986aa0aa5f16a0744a97ab36d6 +--- /dev/null ++++ b/src/lib/certmap/sss_cert_content_crypto.c +@@ -0,0 +1,32 @@ ++/* ++ SSSD - certificate handling utils - OpenSSL version ++ The calls defined here should be useable outside of SSSD as well, e.g. in ++ libsss_certmap. ++ ++ Copyright (C) Sumit Bose 2017 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++ ++#include "lib/certmap/sss_certmap.h" ++#include "lib/certmap/sss_certmap_int.h" ++ ++int sss_cert_get_content(TALLOC_CTX *mem_ctx, ++ const uint8_t *der_blob, size_t der_size, ++ struct sss_cert_content **content) ++{ ++ return EINVAL; ++} +diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h +index 28f1c596cfb5e78077b6a8e9baefa88b4900a022..0b4cda73639be9b323ac3388f97be90bc1a771f2 100644 +--- a/src/lib/certmap/sss_certmap_int.h ++++ b/src/lib/certmap/sss_certmap_int.h +@@ -22,12 +22,14 @@ + along with this program. If not, see . + */ + +-#include +-#include +- + #ifndef __SSS_CERTMAP_INT_H__ + #define __SSS_CERTMAP_INT_H__ + ++#include ++#include ++#include ++#include ++ + #define CM_DEBUG(cm_ctx, format, ...) do { \ + if (cm_ctx != NULL && cm_ctx->debug != NULL) { \ + cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \ +-- +2.9.3 + diff --git a/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch b/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch new file mode 100644 index 0000000..f41c5e7 --- /dev/null +++ b/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch @@ -0,0 +1,173 @@ +From cae55d342a5f5c5ac22ac913b9251c2112b22c42 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 21 Sep 2015 12:32:48 +0200 +Subject: [PATCH 06/15] sysdb: add sysdb_attrs_copy() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + src/db/sysdb.c | 24 ++++++++++++++ + src/db/sysdb.h | 1 + + src/tests/sysdb-tests.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 112 insertions(+) + +diff --git a/src/db/sysdb.c b/src/db/sysdb.c +index 5160e3df3810a113d4ec1371350e51a074aaa146..98b7afbfab5141fa9b63a4aab31c620545b3c1f2 100644 +--- a/src/db/sysdb.c ++++ b/src/db/sysdb.c +@@ -752,6 +752,30 @@ done: + return ret; + } + ++errno_t sysdb_attrs_copy(struct sysdb_attrs *src, struct sysdb_attrs *dst) ++{ ++ int ret; ++ size_t c; ++ size_t d; ++ ++ if (src == NULL || dst == NULL) { ++ return EINVAL; ++ } ++ ++ for (c = 0; c < src->num; c++) { ++ for (d = 0; d < src->a[c].num_values; d++) { ++ ret = sysdb_attrs_add_val_safe(dst, src->a[c].name, ++ &src->a[c].values[d]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val failed.\n"); ++ return ret; ++ } ++ } ++ } ++ ++ return EOK; ++} ++ + int sysdb_attrs_users_from_str_list(struct sysdb_attrs *attrs, + const char *attr_name, + const char *domain, +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 83d0d794c737c094d1fd52e7cc7f2113b5d9a7a0..c677957bb639e40db2f985205160612094302e78 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -352,6 +352,7 @@ int sysdb_attrs_add_lc_name_alias_safe(struct sysdb_attrs *attrs, + int sysdb_attrs_copy_values(struct sysdb_attrs *src, + struct sysdb_attrs *dst, + const char *name); ++errno_t sysdb_attrs_copy(struct sysdb_attrs *src, struct sysdb_attrs *dst); + int sysdb_attrs_get_el(struct sysdb_attrs *attrs, const char *name, + struct ldb_message_element **el); + int sysdb_attrs_get_el_ext(struct sysdb_attrs *attrs, const char *name, +diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c +index 013b01a9a68d9de87d796d3aff41d98cef8cccc3..c343c734a27a335303974b6866a5d9e88d4c307e 100644 +--- a/src/tests/sysdb-tests.c ++++ b/src/tests/sysdb-tests.c +@@ -4997,6 +4997,92 @@ START_TEST(test_sysdb_attrs_add_string_safe) + } + END_TEST + ++START_TEST(test_sysdb_attrs_copy) ++{ ++ int ret; ++ struct sysdb_attrs *src; ++ struct sysdb_attrs *dst; ++ TALLOC_CTX *tmp_ctx; ++ const char *val; ++ const char **array; ++ ++ ret = sysdb_attrs_copy(NULL, NULL); ++ fail_unless(ret == EINVAL, "Wrong return code"); ++ ++ tmp_ctx = talloc_new(NULL); ++ fail_unless(tmp_ctx != NULL, "talloc_new failed"); ++ ++ src = sysdb_new_attrs(tmp_ctx); ++ fail_unless(src != NULL, "sysdb_new_attrs failed"); ++ ++ ret = sysdb_attrs_copy(src, NULL); ++ fail_unless(ret == EINVAL, "Wrong return code"); ++ ++ dst = sysdb_new_attrs(tmp_ctx); ++ fail_unless(dst != NULL, "sysdb_new_attrs failed"); ++ ++ ret = sysdb_attrs_copy(NULL, dst); ++ fail_unless(ret == EINVAL, "Wrong return code"); ++ ++ ret = sysdb_attrs_copy(src, dst); ++ fail_unless(ret == EOK, "sysdb_attrs_copy failed"); ++ fail_unless(dst->num == 0, "Wrong number of elements"); ++ ++ ret = sysdb_attrs_add_string(src, TEST_ATTR_NAME, TEST_ATTR_VALUE); ++ fail_unless(ret == EOK, "sysdb_attrs_add_val failed."); ++ ++ ret = sysdb_attrs_copy(src, dst); ++ fail_unless(ret == EOK, "sysdb_attrs_copy failed"); ++ fail_unless(dst->num == 1, "Wrong number of elements"); ++ ret = sysdb_attrs_get_string(dst, TEST_ATTR_NAME, &val); ++ fail_unless(ret == EOK, "sysdb_attrs_get_string failed.\n"); ++ fail_unless(strcmp(val, TEST_ATTR_VALUE) == 0, "Wrong attribute value."); ++ ++ /* Make sure the same entry is not copied twice */ ++ ret = sysdb_attrs_copy(src, dst); ++ fail_unless(ret == EOK, "sysdb_attrs_copy failed"); ++ fail_unless(dst->num == 1, "Wrong number of elements"); ++ ret = sysdb_attrs_get_string(dst, TEST_ATTR_NAME, &val); ++ fail_unless(ret == EOK, "sysdb_attrs_get_string failed.\n"); ++ fail_unless(strcmp(val, TEST_ATTR_VALUE) == 0, "Wrong attribute value."); ++ ++ /* Add new value to existing attribute */ ++ ret = sysdb_attrs_add_string(src, TEST_ATTR_NAME, TEST_ATTR_VALUE"_2nd"); ++ fail_unless(ret == EOK, "sysdb_attrs_add_val failed."); ++ ++ ret = sysdb_attrs_copy(src, dst); ++ fail_unless(ret == EOK, "sysdb_attrs_copy failed"); ++ fail_unless(dst->num == 1, "Wrong number of elements"); ++ ret = sysdb_attrs_get_string_array(dst, TEST_ATTR_NAME, tmp_ctx, &array); ++ fail_unless(ret == EOK, "sysdb_attrs_get_string_array failed.\n"); ++ fail_unless(strcmp(array[0], TEST_ATTR_VALUE) == 0, ++ "Wrong attribute value."); ++ fail_unless(strcmp(array[1], TEST_ATTR_VALUE"_2nd") == 0, ++ "Wrong attribute value."); ++ fail_unless(array[2] == NULL, "Wrong number of values."); ++ ++ /* Add new attribute */ ++ ret = sysdb_attrs_add_string(src, TEST_ATTR_NAME"_2nd", TEST_ATTR_VALUE); ++ fail_unless(ret == EOK, "sysdb_attrs_add_val failed."); ++ ++ ret = sysdb_attrs_copy(src, dst); ++ fail_unless(ret == EOK, "sysdb_attrs_copy failed"); ++ fail_unless(dst->num == 2, "Wrong number of elements"); ++ ret = sysdb_attrs_get_string_array(dst, TEST_ATTR_NAME, tmp_ctx, &array); ++ fail_unless(ret == EOK, "sysdb_attrs_get_string_array failed.\n"); ++ fail_unless(strcmp(array[0], TEST_ATTR_VALUE) == 0, ++ "Wrong attribute value."); ++ fail_unless(strcmp(array[1], TEST_ATTR_VALUE"_2nd") == 0, ++ "Wrong attribute value."); ++ fail_unless(array[2] == NULL, "Wrong number of values."); ++ ret = sysdb_attrs_get_string(dst, TEST_ATTR_NAME"_2nd", &val); ++ fail_unless(ret == EOK, "sysdb_attrs_get_string failed.\n"); ++ fail_unless(strcmp(val, TEST_ATTR_VALUE) == 0, "Wrong attribute value."); ++ ++ talloc_free(tmp_ctx); ++} ++END_TEST ++ + START_TEST (test_sysdb_search_return_ENOENT) + { + struct sysdb_test_ctx *test_ctx; +@@ -6995,6 +7081,7 @@ Suite *create_sysdb_suite(void) + tcase_add_test(tc_sysdb, test_sysdb_attrs_add_val); + tcase_add_test(tc_sysdb, test_sysdb_attrs_add_val_safe); + tcase_add_test(tc_sysdb, test_sysdb_attrs_add_string_safe); ++ tcase_add_test(tc_sysdb, test_sysdb_attrs_copy); + + /* ===== Test search return empty result ===== */ + tcase_add_test(tc_sysdb, test_sysdb_search_return_ENOENT); +-- +2.9.3 + diff --git a/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch b/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch new file mode 100644 index 0000000..9292f78 --- /dev/null +++ b/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch @@ -0,0 +1,316 @@ +From af96fbe97576133ca6077c47f39b812e7e289040 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Sun, 12 Mar 2017 18:31:03 +0100 +Subject: [PATCH 07/15] sdap_get_users_send(): new argument mapped_attrs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mapped_attrs can be a list of sysdb_attrs which are not available on +the server side but should be store with the cached user entry. This is +needed e.g. when the input to look up the user in LDAP is not an +attribute which is stored in LDAP but some data where LDAP attributes +are extracted from. The current use case is the certificate mapping +library which can create LDAP search filters based on content of the +certificate. To allow upcoming cache lookup to use the input directly it +is stored in the user object in the cache. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + src/db/sysdb.h | 3 ++ + src/db/sysdb_ops.c | 61 ++++++++++++++++++++++++++++++ + src/providers/ldap/ldap_id.c | 4 +- + src/providers/ldap/sdap_async.h | 3 +- + src/providers/ldap/sdap_async_enum.c | 2 +- + src/providers/ldap/sdap_async_initgroups.c | 2 +- + src/providers/ldap/sdap_async_private.h | 1 + + src/providers/ldap/sdap_async_users.c | 41 +++++++++++++++++++- + src/providers/ldap/sdap_users.h | 1 + + 9 files changed, 111 insertions(+), 7 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index c677957bb639e40db2f985205160612094302e78..098f47f91187aac75c58c02f0af738c344765762 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -1246,6 +1246,9 @@ errno_t sysdb_search_user_by_cert(TALLOC_CTX *mem_ctx, + errno_t sysdb_remove_cert(struct sss_domain_info *domain, + const char *cert); + ++errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain, ++ struct sysdb_attrs *mapped_attr); ++ + /* === Functions related to GPOs === */ + + #define SYSDB_GPO_CONTAINER "cn=gpos,cn=ad,cn=custom" +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 242d3ce3bb795691e329790a07c3493672e8f523..6c2254df2b75d3d3419528523103ad9cddb40c9d 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -4685,6 +4685,67 @@ errno_t sysdb_search_user_by_cert(TALLOC_CTX *mem_ctx, + return sysdb_search_object_by_cert(mem_ctx, domain, cert, user_attrs, res); + } + ++errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain, ++ struct sysdb_attrs *mapped_attr) ++{ ++ int ret; ++ char *val; ++ char *filter; ++ const char *attrs[] = {SYSDB_NAME, NULL}; ++ struct ldb_result *res = NULL; ++ size_t c; ++ bool all_ok = true; ++ ++ if (mapped_attr->num != 1 || mapped_attr->a[0].num_values != 1) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unsupported number of attributes.\n"); ++ return EINVAL; ++ } ++ ++ ret = bin_to_ldap_filter_value(NULL, mapped_attr->a[0].values[0].data, ++ mapped_attr->a[0].values[0].length, &val); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n"); ++ return ret; ++ } ++ ++ filter = talloc_asprintf(NULL, "(&("SYSDB_UC")(%s=%s))", ++ mapped_attr->a[0].name, val); ++ talloc_free(val); ++ if (filter == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = sysdb_search_object_attr(NULL, domain, filter, attrs, false, &res); ++ talloc_free(filter); ++ if (ret == ENOENT || res == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "Mapped data not found.\n"); ++ talloc_free(res); ++ return EOK; ++ } else if (ret != EOK) { ++ talloc_free(res); ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_object_attr failed.\n"); ++ return ret; ++ } ++ ++ for (c = 0; c < res->count; c++) { ++ DEBUG(SSSDBG_TRACE_ALL, "Removing mapped data from [%s].\n", ++ ldb_dn_get_linearized(res->msgs[c]->dn)); ++ /* The timestamp cache is skipped on purpose here. */ ++ ret = sysdb_set_cache_entry_attr(domain->sysdb->ldb, res->msgs[c]->dn, ++ mapped_attr, SYSDB_MOD_DEL); ++ if (ret != EOK) { ++ all_ok = false; ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to remove mapped data from [%s], skipping.\n", ++ ldb_dn_get_linearized(res->msgs[c]->dn)); ++ } ++ } ++ talloc_free(res); ++ ++ return (all_ok ? EOK : EIO); ++} ++ + errno_t sysdb_remove_cert(struct sss_domain_info *domain, + const char *cert) + { +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index e9455b538daa2d65d944dbb68022a2773623d7b7..898ddb18689d55fcc3fdf021b38df0e574003eb2 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -442,7 +442,7 @@ static void users_get_search(struct tevent_req *req) + state->attrs, state->filter, + dp_opt_get_int(state->ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), +- lookup_type); ++ lookup_type, NULL); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; +@@ -507,7 +507,7 @@ static void users_get_done(struct tevent_req *subreq) + ret = sdap_fallback_local_user(state, state->shortname, uid, &usr_attrs); + if (ret == EOK) { + ret = sdap_save_user(state, state->ctx->opts, state->domain, +- usr_attrs[0], NULL, 0); ++ usr_attrs[0], NULL, NULL, 0); + } + } + } +diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h +index 2ebde6b83646408e446c91cb324809cb767b2617..6e5800b42ba4a045fa7985b09a80b6b86b8c6055 100644 +--- a/src/providers/ldap/sdap_async.h ++++ b/src/providers/ldap/sdap_async.h +@@ -90,7 +90,8 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, + const char **attrs, + const char *filter, + int timeout, +- enum sdap_entry_lookup_type lookup_type); ++ enum sdap_entry_lookup_type lookup_type, ++ struct sysdb_attrs *mapped_attrs); + int sdap_get_users_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp); + +diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c +index 387e53155b567ce106cc68009c7cb99e27d24a17..3f65059e18d5c8b548da0babec867d27c3a64198 100644 +--- a/src/providers/ldap/sdap_async_enum.c ++++ b/src/providers/ldap/sdap_async_enum.c +@@ -635,7 +635,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, + state->attrs, state->filter, + dp_opt_get_int(state->ctx->opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT), +- SDAP_LOOKUP_ENUMERATE); ++ SDAP_LOOKUP_ENUMERATE, NULL); + if (!subreq) { + ret = ENOMEM; + goto fail; +diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c +index 8c7a65bf36abf341e077cf9eac18a234d3a07c07..79af7a3eda3fe8533933535c98c2b4b4698dfda2 100644 +--- a/src/providers/ldap/sdap_async_initgroups.c ++++ b/src/providers/ldap/sdap_async_initgroups.c +@@ -2991,7 +2991,7 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) + DEBUG(SSSDBG_TRACE_ALL, "Storing the user\n"); + + ret = sdap_save_user(state, state->opts, state->dom, state->orig_user, +- NULL, 0); ++ NULL, NULL, 0); + if (ret) { + goto fail; + } +diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h +index 266bc03115e2bdd6a283f5f7da565fd00d3a77be..72507442a9ffd5c0e24ccbd95d75d3ebf9bf0940 100644 +--- a/src/providers/ldap/sdap_async_private.h ++++ b/src/providers/ldap/sdap_async_private.h +@@ -94,6 +94,7 @@ int sdap_save_users(TALLOC_CTX *memctx, + struct sdap_options *opts, + struct sysdb_attrs **users, + int num_users, ++ struct sysdb_attrs *mapped_attrs, + char **_usn_value); + + int sdap_initgr_common_store(struct sysdb_ctx *sysdb, +diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c +index 87d91d8247c37a4c6a1d83b7189399056528fc90..3d957ab584865f74499bc732395388a78965fe5f 100644 +--- a/src/providers/ldap/sdap_async_users.c ++++ b/src/providers/ldap/sdap_async_users.c +@@ -117,6 +117,7 @@ int sdap_save_user(TALLOC_CTX *memctx, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, ++ struct sysdb_attrs *mapped_attrs, + char **_usn_value, + time_t now) + { +@@ -511,6 +512,11 @@ int sdap_save_user(TALLOC_CTX *memctx, + user_attrs, missing, cache_timeout, now); + if (ret) goto done; + ++ if (mapped_attrs != NULL) { ++ ret = sysdb_set_user_attr(dom, user_name, mapped_attrs, SYSDB_MOD_ADD); ++ if (ret) return ret; ++ } ++ + if (_usn_value) { + *_usn_value = talloc_steal(memctx, usn_value); + } +@@ -537,6 +543,7 @@ int sdap_save_users(TALLOC_CTX *memctx, + struct sdap_options *opts, + struct sysdb_attrs **users, + int num_users, ++ struct sysdb_attrs *mapped_attrs, + char **_usn_value) + { + TALLOC_CTX *tmpctx; +@@ -565,11 +572,20 @@ int sdap_save_users(TALLOC_CTX *memctx, + } + in_transaction = true; + ++ if (mapped_attrs != NULL) { ++ ret = sysdb_remove_mapped_data(dom, mapped_attrs); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_remove_mapped_data failed, " ++ "some cached entries might contain invalid mapping data.\n"); ++ } ++ } ++ + now = time(NULL); + for (i = 0; i < num_users; i++) { + usn_value = NULL; + +- ret = sdap_save_user(tmpctx, opts, dom, users[i], &usn_value, now); ++ ret = sdap_save_user(tmpctx, opts, dom, users[i], mapped_attrs, ++ &usn_value, now); + + /* Do not fail completely on errors. + * Just report the failure to save and go on */ +@@ -868,6 +884,7 @@ struct sdap_get_users_state { + + char *higher_usn; + struct sysdb_attrs **users; ++ struct sysdb_attrs *mapped_attrs; + size_t count; + }; + +@@ -883,7 +900,8 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, + const char **attrs, + const char *filter, + int timeout, +- enum sdap_entry_lookup_type lookup_type) ++ enum sdap_entry_lookup_type lookup_type, ++ struct sysdb_attrs *mapped_attrs) + { + errno_t ret; + struct tevent_req *req; +@@ -900,6 +918,23 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, + state->filter = filter; + PROBE(SDAP_SEARCH_USER_SEND, state->filter); + ++ if (mapped_attrs == NULL) { ++ state->mapped_attrs = NULL; ++ } else { ++ state->mapped_attrs = sysdb_new_attrs(state); ++ if (state->mapped_attrs == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_attrs_copy(mapped_attrs, state->mapped_attrs); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_copy failed.\n"); ++ goto done; ++ } ++ } ++ + subreq = sdap_search_user_send(state, ev, dom, opts, search_bases, + sh, attrs, filter, timeout, lookup_type); + if (subreq == NULL) { +@@ -938,9 +973,11 @@ static void sdap_get_users_done(struct tevent_req *subreq) + } + + PROBE(SDAP_SEARCH_USER_SAVE_BEGIN, state->filter); ++ + ret = sdap_save_users(state, state->sysdb, + state->dom, state->opts, + state->users, state->count, ++ state->mapped_attrs, + &state->higher_usn); + PROBE(SDAP_SEARCH_USER_SAVE_END, state->filter); + if (ret) { +diff --git a/src/providers/ldap/sdap_users.h b/src/providers/ldap/sdap_users.h +index 78dafb31a2a07e7289055daec77c5dc5da1bdeef..a6d088a6d7114db75b0f0ea22ef85c57da6fab0f 100644 +--- a/src/providers/ldap/sdap_users.h ++++ b/src/providers/ldap/sdap_users.h +@@ -34,6 +34,7 @@ int sdap_save_user(TALLOC_CTX *memctx, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, ++ struct sysdb_attrs *mapped_attrs, + char **_usn_value, + time_t now); + +-- +2.9.3 + diff --git a/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch b/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch new file mode 100644 index 0000000..108254d --- /dev/null +++ b/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch @@ -0,0 +1,178 @@ +From a3cc501e36f5cf1e4a8187d723b53111f5481b36 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 30 Nov 2015 12:14:55 +0100 +Subject: [PATCH 08/15] LDAP: always store the certificate from the request +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Store the certificate used to lookup a user as mapped attribute in the +cached user object. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + src/db/sysdb.h | 1 + + src/db/sysdb_ops.c | 4 ++-- + src/providers/ldap/ldap_id.c | 19 ++++++++++++++++++- + src/tests/cmocka/test_nss_srv.c | 2 +- + src/tests/cmocka/test_pam_srv.c | 6 +++--- + src/tests/sysdb-tests.c | 4 ++-- + 6 files changed, 27 insertions(+), 9 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 098f47f91187aac75c58c02f0af738c344765762..3db22b3689bf6ffd9a48e29c229916e3fac9ca1b 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -139,6 +139,7 @@ + + #define SYSDB_AUTH_TYPE "authType" + #define SYSDB_USER_CERT "userCertificate" ++#define SYSDB_USER_MAPPED_CERT "userMappedCertificate" + #define SYSDB_USER_EMAIL "mail" + + #define SYSDB_SUBDOMAIN_REALM "realmName" +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 6c2254df2b75d3d3419528523103ad9cddb40c9d..8ae25764478e522255b177f9e8de1d3ca1ad43fd 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -4660,7 +4660,7 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx, + int ret; + char *user_filter; + +- ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_CERT, ++ ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT, + &user_filter); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n"); +@@ -4749,7 +4749,7 @@ errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain, + errno_t sysdb_remove_cert(struct sss_domain_info *domain, + const char *cert) + { +- struct ldb_message_element el = { 0, SYSDB_USER_CERT, 0, NULL }; ++ struct ldb_message_element el = { 0, SYSDB_USER_MAPPED_CERT, 0, NULL }; + struct sysdb_attrs del_attrs = { 1, &el }; + const char *attrs[] = {SYSDB_NAME, NULL}; + struct ldb_result *res = NULL; +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index 898ddb18689d55fcc3fdf021b38df0e574003eb2..a8b4bc2cfc6e9d4e0d74b0e3e036afbcbf7eb26e 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -60,6 +60,7 @@ struct users_get_state { + int dp_error; + int sdap_ret; + bool noexist_delete; ++ struct sysdb_attrs *extra_attrs; + }; + + static int users_get_retry(struct tevent_req *req); +@@ -99,6 +100,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + state->conn = conn; + state->dp_error = DP_ERR_FATAL; + state->noexist_delete = noexist_delete; ++ state->extra_attrs = NULL; + + state->op = sdap_id_op_create(state, state->conn->conn_cache); + if (!state->op) { +@@ -251,6 +253,21 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + "sss_cert_derb64_to_ldap_filter failed.\n"); + goto done; + } ++ ++ state->extra_attrs = sysdb_new_attrs(state); ++ if (state->extra_attrs == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_attrs_add_base64_blob(state->extra_attrs, ++ SYSDB_USER_MAPPED_CERT, filter_value); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n"); ++ goto done; ++ } ++ + break; + default: + ret = EINVAL; +@@ -442,7 +459,7 @@ static void users_get_search(struct tevent_req *req) + state->attrs, state->filter, + dp_opt_get_int(state->ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), +- lookup_type, NULL); ++ lookup_type, state->extra_attrs); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 72bbaf9bf35ebb3fc4208afaa3c7af95922afcb0..76b9c6fb05673130de0957e93291919c263a28f3 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -3508,7 +3508,7 @@ static void test_nss_getnamebycert(void **state) + der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size); + assert_non_null(der); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index ae2e555f7024027d1c0063031f8882bf81a31905..847419658bb983e6548722d6fa6fb22c63ee86b8 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -1598,7 +1598,7 @@ static int test_lookup_by_cert_cb(void *pvt) + der = sss_base64_decode(pam_test_ctx, pvt, &der_size); + assert_non_null(der); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + +@@ -1630,7 +1630,7 @@ static int test_lookup_by_cert_double_cb(void *pvt) + der = sss_base64_decode(pam_test_ctx, pvt, &der_size); + assert_non_null(der); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + +@@ -1658,7 +1658,7 @@ static int test_lookup_by_cert_wrong_user_cb(void *pvt) + der = sss_base64_decode(pam_test_ctx, pvt, &der_size); + assert_non_null(der); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + +diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c +index c343c734a27a335303974b6866a5d9e88d4c307e..5bdd631fbfa1b4463fb169e5f07b65fb2c784096 100644 +--- a/src/tests/sysdb-tests.c ++++ b/src/tests/sysdb-tests.c +@@ -5721,7 +5721,7 @@ START_TEST(test_sysdb_search_user_by_cert) + val.data = sss_base64_decode(test_ctx, TEST_USER_CERT_DERB64, &val.length); + fail_unless(val.data != NULL, "sss_base64_decode failed."); + +- ret = sysdb_attrs_add_val(data->attrs, SYSDB_USER_CERT, &val); ++ ret = sysdb_attrs_add_val(data->attrs, SYSDB_USER_MAPPED_CERT, &val); + fail_unless(ret == EOK, "sysdb_attrs_add_val failed with [%d][%s].", + ret, strerror(ret)); + +@@ -5750,7 +5750,7 @@ START_TEST(test_sysdb_search_user_by_cert) + data2 = test_data_new_user(test_ctx, 2345671); + fail_if(data2 == NULL); + +- ret = sysdb_attrs_add_val(data2->attrs, SYSDB_USER_CERT, &val); ++ ret = sysdb_attrs_add_val(data2->attrs, SYSDB_USER_MAPPED_CERT, &val); + fail_unless(ret == EOK, "sysdb_attrs_add_val failed with [%d][%s].", + ret, strerror(ret)); + +-- +2.9.3 + diff --git a/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch b/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch new file mode 100644 index 0000000..d53da7d --- /dev/null +++ b/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch @@ -0,0 +1,235 @@ +From 2e12cbdc8e2676b045a972045e9dae75b232dc76 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 2 Feb 2017 16:34:32 +0100 +Subject: [PATCH 09/15] sss_cert_derb64_to_ldap_filter: add sss_certmap support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use certificate mapping library if available to lookup a user by +certificate in LDAP. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 1 + + src/db/sysdb_ops.c | 2 +- + src/db/sysdb_views.c | 4 +- + src/providers/ipa/ipa_views.c | 2 +- + src/providers/ldap/ldap_id.c | 2 +- + src/tests/cmocka/test_cert_utils.c | 4 +- + src/util/cert.h | 3 ++ + src/util/cert/cert_common.c | 76 ++++++++++++++++++++++++++++++++------ + 8 files changed, 76 insertions(+), 18 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 7947b7a5fbe3ca1034baac1c13c53300994b1bf8..f262cc24832358910dbb92ccd46f93c9eda8a295 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -952,6 +952,7 @@ libsss_cert_la_LIBADD = \ + $(TALLOC_LIBS) \ + libsss_crypt.la \ + libsss_debug.la \ ++ libsss_certmap.la \ + $(NULL) + libsss_cert_la_LDFLAGS = \ + -avoid-version \ +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 8ae25764478e522255b177f9e8de1d3ca1ad43fd..919f22370ff87eff2bf0bb569ca90f1ee699a61e 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -4661,7 +4661,7 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx, + char *user_filter; + + ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT, +- &user_filter); ++ NULL, NULL, &user_filter); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n"); + return ret; +diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c +index 9dc48f5b6c414bbc7c64bcd1fe73553f388588bd..1c416dd14049237e9f35d52f154035e3ff861469 100644 +--- a/src/db/sysdb_views.c ++++ b/src/db/sysdb_views.c +@@ -862,8 +862,8 @@ errno_t sysdb_search_override_by_cert(TALLOC_CTX *mem_ctx, + goto done; + } + +- ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, cert, SYSDB_USER_CERT, +- &cert_filter); ++ ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, cert, SYSDB_USER_CERT, NULL, ++ NULL, &cert_filter); + + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n"); +diff --git a/src/providers/ipa/ipa_views.c b/src/providers/ipa/ipa_views.c +index 29f589ec1fd05f59175dcc4592e6395941e6e034..5b6fcbc9b7c6f2ea7dbeecb01a5a3fd11b8a6854 100644 +--- a/src/providers/ipa/ipa_views.c ++++ b/src/providers/ipa/ipa_views.c +@@ -156,7 +156,7 @@ static errno_t dp_id_data_to_override_filter(TALLOC_CTX *mem_ctx, + if ((ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_BY_CERT) { + ret = sss_cert_derb64_to_ldap_filter(mem_ctx, ar->filter_value, + ipa_opts->override_map[IPA_AT_OVERRIDE_USER_CERT].name, +- &cert_filter); ++ NULL, NULL, &cert_filter); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_cert_derb64_to_ldap_filter failed.\n"); +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index a8b4bc2cfc6e9d4e0d74b0e3e036afbcbf7eb26e..8e60769d09383ac8ebe33e5f64fd4fd9788e82cd 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -247,7 +247,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + } + + ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name, +- &user_filter); ++ NULL, NULL, &user_filter); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_cert_derb64_to_ldap_filter failed.\n"); +diff --git a/src/tests/cmocka/test_cert_utils.c b/src/tests/cmocka/test_cert_utils.c +index 35e8cb7513968079861048a7e8b0631229f202c0..5830131754e4cf318273151b586ef36d6a349829 100644 +--- a/src/tests/cmocka/test_cert_utils.c ++++ b/src/tests/cmocka/test_cert_utils.c +@@ -297,11 +297,11 @@ void test_sss_cert_derb64_to_ldap_filter(void **state) + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + +- ret = sss_cert_derb64_to_ldap_filter(ts, NULL, NULL, NULL); ++ ret = sss_cert_derb64_to_ldap_filter(ts, NULL, NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_cert_derb64_to_ldap_filter(ts, "AAECAwQFBgcICQ==", "attrName", +- &filter); ++ NULL, NULL, &filter); + assert_int_equal(ret, EOK); + assert_string_equal(filter, + "(attrName=\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09)"); +diff --git a/src/util/cert.h b/src/util/cert.h +index bb64d0d7a0a48207df60f6e6e554da5e16a16b03..4598aa8df0cd860fed71d9cd2e4beec7f1910578 100644 +--- a/src/util/cert.h ++++ b/src/util/cert.h +@@ -21,6 +21,7 @@ + #include + + #include "util/util.h" ++#include "lib/certmap/sss_certmap.h" + + #ifndef __CERT_H__ + #define __CERT_H__ +@@ -39,6 +40,8 @@ errno_t sss_cert_pem_to_derb64(TALLOC_CTX *mem_ctx, const char *pem, + + errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64, + const char *attr_name, ++ struct sss_certmap_ctx *certmap_ctx, ++ struct sss_domain_info *dom, + char **ldap_filter); + + errno_t bin_to_ldap_filter_value(TALLOC_CTX *mem_ctx, +diff --git a/src/util/cert/cert_common.c b/src/util/cert/cert_common.c +index a29696ed3cd9f2168f47323fac97d44e9b49f921..766877089429ff1c01000a3986316c74583e3fa4 100644 +--- a/src/util/cert/cert_common.c ++++ b/src/util/cert/cert_common.c +@@ -72,12 +72,17 @@ errno_t sss_cert_pem_to_derb64(TALLOC_CTX *mem_ctx, const char *pem, + + errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64, + const char *attr_name, ++ struct sss_certmap_ctx *certmap_ctx, ++ struct sss_domain_info *dom, + char **ldap_filter) + { + int ret; + unsigned char *der; + size_t der_size; + char *val; ++ char *filter = NULL; ++ char **domains = NULL; ++ size_t c; + + if (derb64 == NULL || attr_name == NULL) { + return EINVAL; +@@ -89,18 +94,67 @@ errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64, + return EINVAL; + } + +- ret = bin_to_ldap_filter_value(mem_ctx, der, der_size, &val); +- talloc_free(der); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n"); +- return ret; +- } ++ if (certmap_ctx == NULL) { ++ ret = bin_to_ldap_filter_value(mem_ctx, der, der_size, &val); ++ talloc_free(der); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n"); ++ return ret; ++ } + +- *ldap_filter = talloc_asprintf(mem_ctx, "(%s=%s)", attr_name, val); +- talloc_free(val); +- if (*ldap_filter == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); +- return ENOMEM; ++ *ldap_filter = talloc_asprintf(mem_ctx, "(%s=%s)", attr_name, val); ++ talloc_free(val); ++ if (*ldap_filter == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ return ENOMEM; ++ } ++ } else { ++ ret = sss_certmap_get_search_filter(certmap_ctx, der, der_size, ++ &filter, &domains); ++ talloc_free(der); ++ if (ret != 0) { ++ if (ret == ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Certificate does not match matching-rules.\n"); ++ } else { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_certmap_get_search_filter failed.\n"); ++ } ++ } else { ++ if (domains == NULL) { ++ if (IS_SUBDOMAIN(dom)) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Rule applies only to local domain.\n"); ++ ret = ENOENT; ++ } ++ } else { ++ for (c = 0; domains[c] != NULL; c++) { ++ if (strcasecmp(dom->name, domains[c]) == 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Rule applies to current domain [%s].\n", ++ dom->name); ++ ret = EOK; ++ break; ++ } ++ } ++ if (domains[c] == NULL) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Rule does not apply to current domain [%s].\n", ++ dom->name); ++ ret = ENOENT; ++ } ++ } ++ } ++ ++ if (ret == EOK) { ++ *ldap_filter = talloc_strdup(mem_ctx, filter); ++ if (*ldap_filter == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ } ++ } ++ sss_certmap_free_filter_and_domains(filter, domains); ++ return ret; + } + + return EOK; +-- +2.9.3 + diff --git a/SOURCES/0010-sysdb-add-certmap-related-calls.patch b/SOURCES/0010-sysdb-add-certmap-related-calls.patch new file mode 100644 index 0000000..5b33d5f --- /dev/null +++ b/SOURCES/0010-sysdb-add-certmap-related-calls.patch @@ -0,0 +1,846 @@ +From cfb6a115568ab24fe5df365d1436419b504111ec Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 6 Feb 2017 10:27:22 +0100 +Subject: [PATCH 10/15] sysdb: add certmap related calls +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add sysdb calls to write and read data for the certificate mapping +library to the cache. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 17 ++ + src/db/sysdb.h | 27 +++ + src/db/sysdb_certmap.c | 425 ++++++++++++++++++++++++++++++++++ + src/tests/cmocka/test_sysdb_certmap.c | 260 +++++++++++++++++++++ + 4 files changed, 729 insertions(+) + create mode 100644 src/db/sysdb_certmap.c + create mode 100644 src/tests/cmocka/test_sysdb_certmap.c + +diff --git a/Makefile.am b/Makefile.am +index f262cc24832358910dbb92ccd46f93c9eda8a295..bd0ca0d303e1742ad26c7648cd24e2c0135af34e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -254,6 +254,7 @@ if HAVE_CMOCKA + test_sysdb_ts_cache \ + test_sysdb_views \ + test_sysdb_subdomains \ ++ test_sysdb_certmap \ + test_sysdb_sudo \ + test_sysdb_utils \ + test_wbc_calls \ +@@ -974,6 +975,7 @@ libsss_util_la_SOURCES = \ + src/db/sysdb_ranges.c \ + src/db/sysdb_idmap.c \ + src/db/sysdb_gpo.c \ ++ src/db/sysdb_certmap.c \ + src/monitor/monitor_sbus.c \ + src/providers/dp_auth_util.c \ + src/providers/dp_pam_data_util.c \ +@@ -2773,6 +2775,21 @@ test_sysdb_subdomains_LDADD = \ + libsss_test_common.la \ + $(NULL) + ++test_sysdb_certmap_SOURCES = \ ++ src/tests/cmocka/test_sysdb_certmap.c \ ++ $(NULL) ++test_sysdb_certmap_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++test_sysdb_certmap_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(LDB_LIBS) \ ++ $(POPT_LIBS) \ ++ $(TALLOC_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ $(NULL) ++ + test_sysdb_sudo_SOURCES = \ + src/tests/cmocka/test_sysdb_sudo.c \ + $(NULL) +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 3db22b3689bf6ffd9a48e29c229916e3fac9ca1b..0cbb2c5c02355e9e9a4e73b075f92d16e4855045 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -39,6 +39,7 @@ + #define SYSDB_NETGROUP_CONTAINER "cn=Netgroups" + #define SYSDB_RANGE_CONTAINER "cn=ranges" + #define SYSDB_VIEW_CONTAINER "cn=views" ++#define SYSDB_CERTMAP_CONTAINER "cn=certmap" + #define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER","SYSDB_DOM_BASE + #define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER","SYSDB_DOM_BASE + #define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER","SYSDB_DOM_BASE +@@ -46,6 +47,7 @@ + #define SYSDB_TMPL_RANGE_BASE SYSDB_RANGE_CONTAINER","SYSDB_BASE + #define SYSDB_TMPL_VIEW_BASE SYSDB_VIEW_CONTAINER","SYSDB_BASE + #define SYSDB_TMPL_VIEW_SEARCH_BASE "cn=%s,"SYSDB_TMPL_VIEW_BASE ++#define SYSDB_TMPL_CERTMAP_BASE SYSDB_CERTMAP_CONTAINER","SYSDB_BASE + + #define SYSDB_SUBDOMAIN_CLASS "subdomain" + #define SYSDB_USER_CLASS "user" +@@ -58,6 +60,7 @@ + #define SYSDB_ID_RANGE_CLASS "idRange" + #define SYSDB_DOMAIN_ID_RANGE_CLASS "domainIDRange" + #define SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS "TrustedADDomainRange" ++#define SYSDB_CERTMAP_CLASS "certificateMappingRule" + + #define SYSDB_DN "dn" + #define SYSDB_NAME "name" +@@ -158,6 +161,12 @@ + #define SYSDB_DOMAIN_ID "domainID" + #define SYSDB_ID_RANGE_TYPE "idRangeType" + ++#define SYSDB_CERTMAP_PRIORITY "priority" ++#define SYSDB_CERTMAP_MATCHING_RULE "matchingRule" ++#define SYSDB_CERTMAP_MAPPING_RULE "mappingRule" ++#define SYSDB_CERTMAP_DOMAINS "domains" ++#define SYSDB_CERTMAP_USER_NAME_HINT "userNameHint" ++ + #define ORIGINALAD_PREFIX "originalAD" + #define OVERRIDE_PREFIX "override" + #define SYSDB_DEFAULT_OVERRIDE_NAME "defaultOverrideName" +@@ -264,6 +273,7 @@ + #define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE + #define SYSDB_TMPL_RANGE SYSDB_NAME"=%s,"SYSDB_TMPL_RANGE_BASE + #define SYSDB_TMPL_OVERRIDE SYSDB_OVERRIDE_ANCHOR_UUID"=%s,"SYSDB_TMPL_VIEW_SEARCH_BASE ++#define SYSDB_TMPL_CERTMAP SYSDB_NAME"=%s,"SYSDB_TMPL_CERTMAP_BASE + + #define SYSDB_MOD_ADD LDB_FLAG_MOD_ADD + #define SYSDB_MOD_DEL LDB_FLAG_MOD_DELETE +@@ -320,6 +330,15 @@ struct range_info { + char *range_type; + }; + ++struct certmap_info { ++ char *name; ++ uint32_t priority; ++ char *match_rule; ++ char *map_rule; ++ const char **domains; ++}; ++ ++ + /* These attributes are stored in the timestamp cache */ + extern const char *sysdb_ts_cache_attrs[]; + +@@ -619,6 +638,14 @@ uint64_t sss_view_ldb_msg_find_attr_as_uint64(struct sss_domain_info *dom, + const char *attr_name, + uint64_t default_value); + ++errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb, ++ struct certmap_info **certmaps, ++ bool user_name_hint); ++ ++errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, ++ struct certmap_info ***certmaps, ++ bool *user_name_hint); ++ + /* Sysdb initialization. + * call this function *only* once to initialize the database and get + * the sysdb ctx */ +diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..4917796b11c3967b4d147ebee7c7e83f09b872ce +--- /dev/null ++++ b/src/db/sysdb_certmap.c +@@ -0,0 +1,425 @@ ++/* ++ SSSD ++ ++ System Database - certificate mapping rules related calls ++ ++ Copyright (C) 2017 Sumit Bose ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++ ++#include "util/util.h" ++#include "db/sysdb_private.h" ++ ++static errno_t sysdb_create_certmap_container(struct sysdb_ctx *sysdb, ++ bool user_name_hint) ++{ ++ struct ldb_message *msg = NULL; ++ errno_t ret; ++ ++ msg = ldb_msg_new(sysdb); ++ if (msg == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ msg->dn = ldb_dn_new(msg, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE); ++ if (msg->dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ret = ldb_msg_add_string(msg, "cn", "certmap"); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ ret = ldb_msg_add_string(msg, SYSDB_CERTMAP_USER_NAME_HINT, ++ user_name_hint ? "TRUE" : "FALSE"); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ /* do a synchronous add */ ++ ret = ldb_add(sysdb->ldb, msg); ++ if (ret != LDB_SUCCESS) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Failed to add certmap container (%d, [%s])!\n", ++ ret, ldb_errstring(sysdb->ldb)); ++ ret = EIO; ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(msg); ++ ++ return ret; ++} ++ ++static errno_t sysdb_certmap_add(struct sysdb_ctx *sysdb, ++ struct certmap_info *certmap) ++{ ++ struct ldb_message *msg; ++ struct ldb_message_element *el; ++ int ret; ++ TALLOC_CTX *tmp_ctx; ++ size_t c; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed"); ++ return ENOMEM; ++ } ++ ++ msg = ldb_msg_new(tmp_ctx); ++ if (msg == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ msg->dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, ++ SYSDB_TMPL_CERTMAP, certmap->name); ++ if (msg->dn == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_CERTMAP_CLASS); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n"); ++ goto done; ++ } ++ ++ ret = sysdb_add_string(msg, SYSDB_NAME, certmap->name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n"); ++ goto done; ++ } ++ ++ if (certmap->map_rule != NULL) { ++ ret = sysdb_add_string(msg, SYSDB_CERTMAP_MAPPING_RULE, ++ certmap->map_rule); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n"); ++ goto done; ++ } ++ } ++ ++ if (certmap->match_rule != NULL) { ++ ret = sysdb_add_string(msg, SYSDB_CERTMAP_MATCHING_RULE, ++ certmap->match_rule); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n"); ++ goto done; ++ } ++ } ++ ++ if (certmap->domains != NULL) { ++ for (c = 0; certmap->domains[c] != NULL; c++); ++ el = talloc_zero(tmp_ctx, struct ldb_message_element); ++ if (el == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ el->name = talloc_strdup(el, SYSDB_CERTMAP_DOMAINS); ++ if(el->name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ el->num_values = c; ++ el->values = talloc_zero_array(el, struct ldb_val, c + 1); ++ if (el->values == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (c = 0; certmap->domains[c] != NULL; c++) { ++ el->values[c].data = (uint8_t *) talloc_strdup(el->values, ++ certmap->domains[c]); ++ if (el->values[c].data == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ el->values[c].length = strlen(certmap->domains[c]); ++ } ++ ++ ret = ldb_msg_add(msg, el, LDB_FLAG_MOD_ADD); ++ if (ret != LDB_SUCCESS) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add failed.\n"); ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ } ++ ++ ret = sysdb_add_ulong(msg, SYSDB_CERTMAP_PRIORITY, ++ (unsigned long)certmap->priority); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_ulong failed.\n"); ++ goto done; ++ } ++ ++ ret = ldb_add(sysdb->ldb, msg); ++ if (ret != LDB_SUCCESS) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_add failed.\n"); ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, sss_strerror(ret)); ++ } ++ talloc_zfree(tmp_ctx); ++ return ret; ++} ++ ++errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb, ++ struct certmap_info **certmaps, ++ bool user_name_hint) ++{ ++ size_t c; ++ struct ldb_dn *container_dn = NULL; ++ bool in_transaction = false; ++ int ret; ++ int sret; ++ ++ if (certmaps == NULL) { ++ return EINVAL; ++ } ++ ++ container_dn = ldb_dn_new(sysdb, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE); ++ if (container_dn == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = sysdb_transaction_start(sysdb); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n"); ++ goto done; ++ } ++ in_transaction = true; ++ ++ ret = sysdb_delete_recursive(sysdb, container_dn, true); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n"); ++ goto done; ++ } ++ ret = sysdb_create_certmap_container(sysdb, user_name_hint); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_create_certmap_container failed.\n"); ++ goto done; ++ } ++ ++ for (c = 0; certmaps[c] != NULL; c++) { ++ ret = sysdb_certmap_add(sysdb, certmaps[c]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_certmap_add failed.\n"); ++ goto done; ++ } ++ } ++ ++ ret = sysdb_transaction_commit(sysdb); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_transaction_commit failed.\n"); ++ goto done; ++ } ++ in_transaction = false; ++ ++done: ++ if (in_transaction) { ++ sret = sysdb_transaction_cancel(sysdb); ++ if (sret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction.\n"); ++ } ++ } ++ ++ talloc_free(container_dn); ++ ++ return ret; ++} ++ ++errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, ++ struct certmap_info ***certmaps, bool *user_name_hint) ++{ ++ size_t c; ++ size_t d; ++ struct ldb_dn *container_dn = NULL; ++ int ret; ++ struct certmap_info **maps; ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct ldb_result *res; ++ const char *tmp_str; ++ uint64_t tmp_uint; ++ struct ldb_message_element *tmp_el; ++ const char *attrs[] = {SYSDB_NAME, ++ SYSDB_CERTMAP_PRIORITY, ++ SYSDB_CERTMAP_MATCHING_RULE, ++ SYSDB_CERTMAP_MAPPING_RULE, ++ SYSDB_CERTMAP_DOMAINS, ++ NULL}; ++ const char *config_attrs[] = {SYSDB_CERTMAP_USER_NAME_HINT, ++ NULL}; ++ size_t num_values; ++ bool hint = false; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ container_dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE); ++ if (container_dn == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, container_dn, LDB_SCOPE_BASE, ++ config_attrs, SYSDB_CERTMAP_USER_NAME_HINT"=*"); ++ if (ret != LDB_SUCCESS || res->count != 1) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to read certmap config, skipping.\n"); ++ } else { ++ hint = ldb_msg_find_attr_as_bool(res->msgs[0], ++ SYSDB_CERTMAP_USER_NAME_HINT, false); ++ } ++ ++ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, ++ container_dn, LDB_SCOPE_SUBTREE, ++ attrs, "objectclass=%s", SYSDB_CERTMAP_CLASS); ++ if (ret != LDB_SUCCESS) { ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_search failed.\n"); ++ ret = EIO; ++ goto done; ++ } ++ ++ if (res->count == 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, "No certificate maps found.\n"); ++ ret = ENOENT; ++ goto done; ++ } ++ ++ maps = talloc_zero_array(tmp_ctx, struct certmap_info *, res->count + 1); ++ if (maps == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (c = 0; c < res->count; c++) { ++ maps[c] = talloc_zero(maps, struct certmap_info); ++ if (maps[c] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_NAME, NULL); ++ if (tmp_str == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n", ++ ldb_dn_get_linearized(res->msgs[c]->dn)); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ maps[c]->name = talloc_strdup(maps, tmp_str); ++ if (maps[c]->name == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], ++ SYSDB_CERTMAP_MAPPING_RULE, NULL); ++ if (tmp_str != NULL) { ++ maps[c]->map_rule = talloc_strdup(maps, tmp_str); ++ if (maps[c]->map_rule == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], ++ SYSDB_CERTMAP_MATCHING_RULE, NULL); ++ if (tmp_str != NULL) { ++ maps[c]->match_rule = talloc_strdup(maps, tmp_str); ++ if (maps[c]->match_rule == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ tmp_uint = ldb_msg_find_attr_as_uint64(res->msgs[c], ++ SYSDB_CERTMAP_PRIORITY, ++ (uint64_t) -1); ++ if (tmp_uint != (uint64_t) -1) { ++ if (tmp_uint >= UINT32_MAX) { ++ DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n", ++ (unsigned long) tmp_uint); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ maps[c]->priority = (uint32_t) tmp_uint; ++ } ++ ++ tmp_el = ldb_msg_find_element(res->msgs[c], SYSDB_CERTMAP_DOMAINS); ++ if (tmp_el != NULL) { ++ num_values = tmp_el->num_values; ++ } else { ++ num_values = 0; ++ } ++ ++ maps[c]->domains = talloc_zero_array(maps[c], const char *, ++ num_values + 1); ++ if (maps[c]->domains == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (d = 0; d < num_values; d++) { ++ maps[c]->domains[d] = talloc_strndup(maps[c]->domains, ++ (char *) tmp_el->values[d].data, ++ tmp_el->values[d].length); ++ if (maps[c]->domains[d] == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ ++ *certmaps = talloc_steal(mem_ctx, maps); ++ *user_name_hint = hint; ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} +diff --git a/src/tests/cmocka/test_sysdb_certmap.c b/src/tests/cmocka/test_sysdb_certmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..fb07165561779226935f436c308c85abfc305635 +--- /dev/null ++++ b/src/tests/cmocka/test_sysdb_certmap.c +@@ -0,0 +1,260 @@ ++/* ++ SSSD ++ ++ sysdb_certmap - Tests for sysdb certmap realted calls ++ ++ Authors: ++ Jakub Hrozek ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests/cmocka/common_mock.h" ++#include "tests/common.h" ++ ++#define TESTS_PATH "certmap_" BASE_FILE_STEM ++#define TEST_CONF_DB "test_sysdb_certmap.ldb" ++#define TEST_ID_PROVIDER "ldap" ++#define TEST_DOM_NAME "certmap_test" ++ ++struct certmap_test_ctx { ++ struct sss_test_ctx *tctx; ++}; ++ ++static int test_sysdb_setup(void **state) ++{ ++ struct certmap_test_ctx *test_ctx; ++ struct sss_test_conf_param params[] = { ++ { NULL, NULL }, /* Sentinel */ ++ }; ++ ++ assert_true(leak_check_setup()); ++ ++ test_ctx = talloc_zero(global_talloc_context, ++ struct certmap_test_ctx); ++ assert_non_null(test_ctx); ++ check_leaks_push(test_ctx); ++ ++ test_dom_suite_setup(TESTS_PATH); ++ ++ test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, ++ TEST_CONF_DB, TEST_DOM_NAME, ++ TEST_ID_PROVIDER, params); ++ assert_non_null(test_ctx->tctx); ++ ++ *state = test_ctx; ++ return 0; ++} ++ ++static int test_sysdb_teardown(void **state) ++{ ++ struct certmap_test_ctx *test_ctx = ++ talloc_get_type(*state, struct certmap_test_ctx); ++ ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); ++ talloc_free(test_ctx->tctx); ++ assert_true(check_leaks_pop(test_ctx)); ++ talloc_free(test_ctx); ++ assert_true(leak_check_teardown()); ++ return 0; ++} ++ ++static void test_sysdb_get_certmap_not_exists(void **state) ++{ ++ int ret; ++ struct certmap_info **certmap; ++ bool user_name_hint; ++ struct certmap_test_ctx *ctctx = talloc_get_type(*state, ++ struct certmap_test_ctx); ++ ++ ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, ++ &user_name_hint); ++ assert_int_equal(ret, ENOENT); ++ ++} ++ ++static void check_certmap(struct certmap_info *m, struct certmap_info *r, ++ size_t exp_domains) ++{ ++ size_t d; ++ ++ assert_non_null(r); ++ assert_non_null(m); ++ assert_string_equal(m->name, r->name); ++ ++ if (r->map_rule == NULL) { ++ assert_null(m->map_rule); ++ } else { ++ assert_string_equal(m->map_rule, r->map_rule); ++ } ++ ++ if (r->match_rule == NULL) { ++ assert_null(m->match_rule); ++ } else { ++ assert_string_equal(m->match_rule, r->match_rule); ++ } ++ ++ assert_int_equal(m->priority, r->priority); ++ assert_non_null(m->domains); ++ if (r->domains == NULL) { ++ assert_null(m->domains[0]); ++ } else { ++ for (d = 0; r->domains[d]; d++) { ++ assert_non_null(m->domains[d]); ++ assert_true(string_in_list(m->domains[d], discard_const(r->domains), ++ true)); ++ } ++ ++ assert_int_equal(d, exp_domains); ++ } ++ ++} ++ ++static void test_sysdb_update_certmap(void **state) ++{ ++ int ret; ++ const char *domains[] = { "dom1.test", "dom2.test", "dom3.test", NULL }; ++ struct certmap_info map_a = { discard_const("map_a"), 11, discard_const("abc"), discard_const("def"), NULL }; ++ struct certmap_info map_b = { discard_const("map_b"), 22, discard_const("abc"), NULL, domains }; ++ struct certmap_info *certmap_empty[] = { NULL }; ++ struct certmap_info *certmap_a[] = { &map_a, NULL }; ++ struct certmap_info *certmap_b[] = { &map_b, NULL }; ++ struct certmap_info *certmap_ab[] = { &map_a, &map_b, NULL }; ++ struct certmap_info **certmap; ++ struct certmap_test_ctx *ctctx = talloc_get_type(*state, ++ struct certmap_test_ctx); ++ bool user_name_hint; ++ ++ ret = sysdb_update_certmap(ctctx->tctx->sysdb, NULL, false); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_empty, false); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, ++ &user_name_hint); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_a, false); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, ++ &user_name_hint); ++ assert_int_equal(ret, EOK); ++ assert_false(user_name_hint); ++ assert_non_null(certmap); ++ assert_non_null(certmap[0]); ++ assert_string_equal(certmap[0]->name, map_a.name); ++ assert_string_equal(certmap[0]->map_rule, map_a.map_rule); ++ assert_string_equal(certmap[0]->match_rule, map_a.match_rule); ++ assert_int_equal(certmap[0]->priority, map_a.priority); ++ assert_non_null(certmap[0]->domains); ++ assert_null(certmap[0]->domains[0]); ++ assert_null(certmap[1]); ++ check_certmap(certmap[0], &map_a, 0); ++ talloc_free(certmap); ++ ++ ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_b, true); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, ++ &user_name_hint); ++ assert_int_equal(ret, EOK); ++ assert_true(user_name_hint); ++ assert_non_null(certmap); ++ assert_non_null(certmap[0]); ++ ++ check_certmap(certmap[0], &map_b, 3); ++ assert_null(certmap[1]); ++ talloc_free(certmap); ++ ++ ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_ab, false); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, ++ &user_name_hint); ++ assert_int_equal(ret, EOK); ++ assert_false(user_name_hint); ++ assert_non_null(certmap); ++ assert_non_null(certmap[0]); ++ assert_non_null(certmap[1]); ++ assert_null(certmap[2]); ++ if (strcmp(certmap[0]->name, "map_a") == 0) { ++ check_certmap(certmap[0], &map_a, 0); ++ check_certmap(certmap[1], &map_b, 3); ++ } else { ++ check_certmap(certmap[0], &map_b, 3); ++ check_certmap(certmap[1], &map_a, 0); ++ } ++ talloc_free(certmap); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ int rv; ++ int no_cleanup = 0; ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, ++ _("Do not delete the test database after a test run"), NULL }, ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_sysdb_get_certmap_not_exists, ++ test_sysdb_setup, ++ test_sysdb_teardown), ++ cmocka_unit_test_setup_teardown(test_sysdb_update_certmap, ++ test_sysdb_setup, ++ test_sysdb_teardown), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++ tests_set_cwd(); ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE); ++ test_dom_suite_setup(TESTS_PATH); ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ ++ if (rv == 0 && no_cleanup == 0) { ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE); ++ } ++ return rv; ++} +-- +2.9.3 + diff --git a/SOURCES/0011-IPA-add-certmap-support.patch b/SOURCES/0011-IPA-add-certmap-support.patch new file mode 100644 index 0000000..d2b8ff6 --- /dev/null +++ b/SOURCES/0011-IPA-add-certmap-support.patch @@ -0,0 +1,484 @@ +From d51754859a83e7fedf0cac90ad8bf5de09f35463 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 6 Feb 2017 10:28:46 +0100 +Subject: [PATCH 11/15] IPA: add certmap support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Read certificate mapping data from the IPA server and configure the +certificate mapping library accordingly. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + src/providers/ipa/ipa_config.h | 2 + + src/providers/ipa/ipa_subdomains.c | 354 ++++++++++++++++++++++++++++++ + src/providers/ipa/ipa_subdomains_server.c | 4 + + src/providers/ldap/ldap_id.c | 4 +- + src/providers/ldap/sdap.h | 4 + + 5 files changed, 367 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ipa/ipa_config.h b/src/providers/ipa/ipa_config.h +index 2f1e147d7edab0aca2a16269c6a73bc607b21bd5..60f2d5d7b71227a1d86889ceaf6f0f9ac868480d 100644 +--- a/src/providers/ipa/ipa_config.h ++++ b/src/providers/ipa/ipa_config.h +@@ -37,6 +37,8 @@ + #define IPA_CONFIG_SEARCH_BASE_TEMPLATE "cn=etc,%s" + #define IPA_CONFIG_FILTER "(&(cn=ipaConfig)(objectClass=ipaGuiConfig))" + ++#define IPA_OC_CONFIG "ipaConfig" ++ + struct tevent_req * ipa_get_config_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_handle *sh, +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index b2e96b204213a52014edcc6042ffa1ff8152b8bf..7537550606ef09c0b87a80932c75aa4f93c0efab 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -56,6 +56,24 @@ + + #define IPA_SUBDOMAIN_DISABLED_PERIOD 3600 + ++#define IPA_OC_CERTMAP_CONFIG_OBJECT "ipaCertMapConfigObject" ++#define IPA_CERTMAP_PROMPT_USERNAME "ipaCertMapPromptUserName" ++ ++#define IPA_OC_CERTMAP_RULE "ipaCertMapRule" ++#define IPA_CERTMAP_MAPRULE "ipaCertMapMapRule" ++#define IPA_CERTMAP_MATCHRULE "ipaCertMapMatchRule" ++#define IPA_CERTMAP_PRIORITY "ipaCertMapPriority" ++#define IPA_ENABLED_FLAG "ipaEnabledFlag" ++#define IPA_TRUE_VALUE "TRUE" ++#define IPA_ASSOCIATED_DOMAIN "associatedDomain" ++ ++#define OBJECTCLASS "objectClass" ++ ++#define CERTMAP_FILTER "(|(&("OBJECTCLASS"="IPA_OC_CERTMAP_RULE")" \ ++ "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))" \ ++ "("OBJECTCLASS"="IPA_OC_CERTMAP_CONFIG_OBJECT"))" ++ ++ + struct ipa_subdomains_ctx { + struct be_ctx *be_ctx; + struct ipa_id_ctx *ipa_id_ctx; +@@ -286,6 +304,193 @@ done: + return ret; + } + ++struct priv_sss_debug { ++ int level; ++}; ++ ++void ext_debug(void *private, const char *file, long line, const char *function, ++ const char *format, ...) ++{ ++ va_list ap; ++ struct priv_sss_debug *data = private; ++ int level = SSSDBG_OP_FAILURE; ++ ++ if (data != NULL) { ++ level = data->level; ++ } ++ ++ if (DEBUG_IS_SET(level)) { ++ va_start(ap, format); ++ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED, ++ format, ap); ++ va_end(ap); ++ } ++} ++ ++static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ struct sdap_options *sdap_opts, ++ size_t count, ++ struct sysdb_attrs **reply, ++ struct certmap_info ***_certmap_list) ++{ ++ struct certmap_info **certmap_list = NULL; ++ struct certmap_info *m; ++ const char *value; ++ const char **values; ++ size_t c; ++ size_t lc = 0; ++ int ret; ++ struct sss_certmap_ctx *certmap_ctx = NULL; ++ const char **ocs = NULL; ++ bool user_name_hint = false; ++ ++ certmap_list = talloc_zero_array(mem_ctx, struct certmap_info *, count + 1); ++ if (certmap_list == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n"); ++ return ENOMEM; ++ } ++ ++ for (c = 0; c < count; c++) { ++ ret = sysdb_attrs_get_string_array(reply[c], SYSDB_OBJECTCLASS, mem_ctx, ++ &ocs); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Missing objectclasses for config objects.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (string_in_list(IPA_OC_CERTMAP_CONFIG_OBJECT, discard_const(ocs), ++ false)) { ++ ret = sysdb_attrs_get_bool(reply[c], IPA_CERTMAP_PROMPT_USERNAME, ++ &user_name_hint); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to read user name hint option, skipping.\n"); ++ } ++ continue; ++ } ++ ++ m = talloc_zero(certmap_list, struct certmap_info); ++ if (m == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_attrs_get_string(reply[c], IPA_CN, &value); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); ++ goto done; ++ } ++ ++ m->name = talloc_strdup(m, value); ++ if (m->name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_attrs_get_string(reply[c], IPA_CERTMAP_MATCHRULE, &value); ++ if (ret == EOK) { ++ m->match_rule = talloc_strdup(m, value); ++ if (m->match_rule == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } else if (ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); ++ goto done; ++ } ++ ++ ret = sysdb_attrs_get_string(reply[c], IPA_CERTMAP_MAPRULE, &value); ++ if (ret == EOK) { ++ m->map_rule = talloc_strdup(m, value); ++ if (m->map_rule == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } else if (ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); ++ goto done; ++ } ++ ++ ret = sysdb_attrs_get_string_array(reply[c], IPA_ASSOCIATED_DOMAIN, m, ++ &values); ++ if (ret == EOK) { ++ m->domains = values; ++ } else if (ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); ++ goto done; ++ } ++ ++ ret = sysdb_attrs_get_uint32_t(reply[c], IPA_CERTMAP_PRIORITY, ++ &m->priority); ++ if (ret != EOK && ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); ++ goto done; ++ } else if (ret == ENOENT) { ++ m->priority = SSS_CERTMAP_MIN_PRIO; ++ } ++ ++ certmap_list[lc++] = m; ++ } ++ ++ certmap_list[lc] = NULL; ++ ++ ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &certmap_ctx); ++ if (ret != 0) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n"); ++ goto done; ++ } ++ ++ for (c = 0; certmap_list[c] != NULL; c++) { ++ DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n", ++ certmap_list[c]->name, ++ certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule); ++ ++ ret = sss_certmap_add_rule(certmap_ctx, certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule, ++ certmap_list[c]->domains); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_certmap_add_rule failed for rule [%s], skipping. " ++ "Please check for typos and if rule syntax is supported.\n", ++ certmap_list[c]->name); ++ goto done; ++ } ++ } ++ ++ ret = sysdb_update_certmap(domain->sysdb, certmap_list, user_name_hint); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed"); ++ goto done; ++ } ++ ++ sss_certmap_free_ctx(sdap_opts->certmap_ctx); ++ sdap_opts->certmap_ctx = talloc_steal(sdap_opts, certmap_ctx); ++ ++ if (_certmap_list != NULL) { ++ *_certmap_list = certmap_list; ++ } ++ ret = EOK; ++ ++done: ++ talloc_free(ocs); ++ if (ret != EOK) { ++ sss_certmap_free_ctx(certmap_ctx); ++ talloc_free(certmap_list); ++ } ++ ++ return ret; ++} ++ + static errno_t ipa_subdom_enumerates(struct sss_domain_info *parent, + struct sysdb_attrs *attrs, + bool *_enumerates) +@@ -801,6 +1006,125 @@ static errno_t ipa_subdomains_ranges_recv(struct tevent_req *req) + return EOK; + } + ++#define IPA_CERTMAP_SEARCH_BASE_TEMPLATE "cn=certmap,%s" ++ ++struct ipa_subdomains_certmap_state { ++ struct sss_domain_info *domain; ++ struct sdap_options *sdap_opts; ++}; ++ ++static void ipa_subdomains_certmap_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++ipa_subdomains_certmap_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ipa_subdomains_ctx *sd_ctx, ++ struct sdap_handle *sh) ++{ ++ struct ipa_subdomains_certmap_state *state; ++ struct tevent_req *subreq; ++ struct tevent_req *req; ++ errno_t ret; ++ char *ldap_basedn; ++ char *search_base; ++ const char *attrs[] = { OBJECTCLASS, IPA_CN, ++ IPA_CERTMAP_MAPRULE, IPA_CERTMAP_MATCHRULE, ++ IPA_CERTMAP_PRIORITY, IPA_ASSOCIATED_DOMAIN, ++ IPA_CERTMAP_PROMPT_USERNAME, ++ NULL }; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct ipa_subdomains_certmap_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->domain = sd_ctx->be_ctx->domain; ++ state->sdap_opts = sd_ctx->sdap_id_ctx->opts; ++ ++ ret = domain_to_basedn(state, state->domain->name, &ldap_basedn); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n"); ++ goto immediately; ++ } ++ ++ search_base = talloc_asprintf(state, IPA_CERTMAP_SEARCH_BASE_TEMPLATE, ++ ldap_basedn); ++ if (search_base == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ ret = ENOMEM; ++ goto immediately; ++ } ++ ++ subreq = sdap_get_generic_send(state, ev, sd_ctx->sdap_id_ctx->opts, sh, ++ search_base, LDAP_SCOPE_SUBTREE, ++ CERTMAP_FILTER, ++ attrs, NULL, 0, 0, false); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediately; ++ } ++ ++ tevent_req_set_callback(subreq, ipa_subdomains_certmap_done, req); ++ ++ return req; ++ ++immediately: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static void ipa_subdomains_certmap_done(struct tevent_req *subreq) ++{ ++ struct ipa_subdomains_certmap_state *state; ++ struct tevent_req *req; ++ struct sysdb_attrs **reply; ++ size_t reply_count; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ipa_subdomains_certmap_state); ++ ++ ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get data from LDAP [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = ipa_certmap_parse_results(state, state->domain, ++ state->sdap_opts, ++ reply_count, reply, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to parse certmap results [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static errno_t ipa_subdomains_certmap_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ return EOK; ++} ++ + struct ipa_subdomains_master_state { + struct sss_domain_info *domain; + struct ipa_options *ipa_options; +@@ -1365,6 +1689,7 @@ struct ipa_subdomains_refresh_state { + static errno_t ipa_subdomains_refresh_retry(struct tevent_req *req); + static void ipa_subdomains_refresh_connect_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq); ++static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq); +@@ -1487,6 +1812,35 @@ static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq) + return; + } + ++ subreq = ipa_subdomains_certmap_send(state, state->ev, state->sd_ctx, ++ sdap_id_op_handle(state->sdap_op)); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ tevent_req_set_callback(subreq, ipa_subdomains_refresh_certmap_done, req); ++ return; ++} ++ ++static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq) ++{ ++ struct ipa_subdomains_refresh_state *state; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ipa_subdomains_refresh_state); ++ ++ ret = ipa_subdomains_certmap_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to read certificate mapping rules " ++ "[%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ + subreq = ipa_subdomains_master_send(state, state->ev, state->sd_ctx, + sdap_id_op_handle(state->sdap_op)); + if (subreq == NULL) { +diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c +index 1af8676c5a9c49121d0f0118a46796c6637f04f9..ae3baf036e4278fb67d86b42742fb7e80b46724e 100644 +--- a/src/providers/ipa/ipa_subdomains_server.c ++++ b/src/providers/ipa/ipa_subdomains_server.c +@@ -362,6 +362,10 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, + ad_id_ctx->sdap_id_ctx->opts->idmap_ctx = + id_ctx->sdap_id_ctx->opts->idmap_ctx; + ++ /* Set up the certificate mapping context */ ++ ad_id_ctx->sdap_id_ctx->opts->certmap_ctx = ++ id_ctx->sdap_id_ctx->opts->certmap_ctx; ++ + *_ad_id_ctx = ad_id_ctx; + return EOK; + } +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index 8e60769d09383ac8ebe33e5f64fd4fd9788e82cd..0bee0ca8d71abece6749fdb8393b9ceacb64417d 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -247,7 +247,9 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + } + + ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name, +- NULL, NULL, &user_filter); ++ ctx->opts->certmap_ctx, ++ state->domain, ++ &user_filter); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_cert_derb64_to_ldap_filter failed.\n"); +diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h +index 6079a8bf62d0bdf23c8d462dc0f19c705e391a6e..afdc01948eefe9dda943c8c7ad01a42dd76a1da8 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -25,6 +25,7 @@ + #include "providers/backend.h" + #include + #include "util/sss_ldap.h" ++#include "lib/certmap/sss_certmap.h" + + struct sdap_msg { + struct sdap_msg *next; +@@ -478,6 +479,9 @@ struct sdap_options { + + bool support_matching_rule; + enum dc_functional_level dc_functional_level; ++ ++ /* Certificate mapping support */ ++ struct sss_certmap_ctx *certmap_ctx; + }; + + struct sdap_server_opts { +-- +2.9.3 + diff --git a/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch b/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch new file mode 100644 index 0000000..f879ce1 --- /dev/null +++ b/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch @@ -0,0 +1,736 @@ +From f2a81a22124e93a026ec0f06b77eab50998ecba5 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 15 Mar 2017 14:21:26 +0100 +Subject: [PATCH 12/15] nss-idmap: add sss_nss_getlistbycert() + +This patch adds a getlistbycert() call to libsss_nss_idmap to make it on +par with InfoPipe. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +--- + Makefile.am | 2 +- + src/python/pysss_nss_idmap.c | 103 ++++++++++++++++++- + src/responder/nss/nss_cmd.c | 7 ++ + src/responder/nss/nss_protocol.h | 6 ++ + src/responder/nss/nss_protocol_sid.c | 63 ++++++++++++ + src/sss_client/idmap/sss_nss_idmap.c | 110 +++++++++++++++++++- + src/sss_client/idmap/sss_nss_idmap.exports | 6 ++ + src/sss_client/idmap/sss_nss_idmap.h | 17 +++- + src/sss_client/sss_cli.h | 5 + + src/tests/cmocka/test_nss_srv.c | 158 +++++++++++++++++++++++++++++ + 10 files changed, 471 insertions(+), 6 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index bd0ca0d303e1742ad26c7648cd24e2c0135af34e..7516338bc6fd95045d20db8155a0c82fd7003358 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1128,7 +1128,7 @@ libsss_nss_idmap_la_LIBADD = \ + $(CLIENT_LIBS) + libsss_nss_idmap_la_LDFLAGS = \ + -Wl,--version-script,$(srcdir)/src/sss_client/idmap/sss_nss_idmap.exports \ +- -version-info 2:0:2 ++ -version-info 3:0:3 + + dist_noinst_DATA += src/sss_client/idmap/sss_nss_idmap.exports + +diff --git a/src/python/pysss_nss_idmap.c b/src/python/pysss_nss_idmap.c +index c57cc10a86a7a9a22a791c1eae027a1aafa8f780..2e5851c7a6e48629fd93e428aada499fcbe36ebb 100644 +--- a/src/python/pysss_nss_idmap.c ++++ b/src/python/pysss_nss_idmap.c +@@ -36,9 +36,37 @@ enum lookup_type { + SIDBYID, + NAMEBYSID, + IDBYSID, +- NAMEBYCERT ++ NAMEBYCERT, ++ LISTBYCERT + }; + ++static int add_dict_to_list(PyObject *py_list, PyObject *res_type, ++ PyObject *res, PyObject *id_type) ++{ ++ int ret; ++ PyObject *py_dict; ++ ++ py_dict = PyDict_New(); ++ if (py_dict == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = PyDict_SetItem(py_dict, res_type, res); ++ if (ret != 0) { ++ Py_XDECREF(py_dict); ++ return ret; ++ } ++ ++ ret = PyDict_SetItem(py_dict, PyBytes_FromString(SSS_TYPE_KEY), id_type); ++ if (ret != 0) { ++ Py_XDECREF(py_dict); ++ return ret; ++ } ++ ++ ret = PyList_Append(py_list, py_dict); ++ ++ return ret; ++} + static int add_dict(PyObject *py_result, PyObject *key, PyObject *res_type, + PyObject *res, PyObject *id_type) + { +@@ -191,6 +219,57 @@ static int do_getnamebycert(PyObject *py_result, PyObject *py_cert) + return ret; + } + ++static int do_getlistbycert(PyObject *py_result, PyObject *py_cert) ++{ ++ int ret; ++ const char *cert; ++ char **names = NULL; ++ enum sss_id_type *id_types = NULL; ++ size_t c; ++ ++ cert = py_string_or_unicode_as_string(py_cert); ++ if (cert == NULL) { ++ return EINVAL; ++ } ++ ++ ret = sss_nss_getlistbycert(cert, &names, &id_types); ++ if (ret == 0) { ++ ++ PyObject *py_list; ++ ++ py_list = PyList_New(0); ++ if (py_list == NULL) { ++ return ENOMEM; ++ } ++ ++ for (c = 0; names[c] != NULL; c++) { ++ ret = add_dict_to_list(py_list, ++ PyBytes_FromString(SSS_NAME_KEY), ++ PyUnicode_FromString(names[c]), ++ PYNUMBER_FROMLONG(id_types[c])); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ ret = PyDict_SetItem(py_result, py_cert, py_list); ++ if (ret != 0) { ++ goto done; ++ } ++ } ++ ++done: ++ free(id_types); ++ if (names != NULL) { ++ for (c = 0; names[c] != NULL; c++) { ++ free(names[c]); ++ } ++ free(names); ++ } ++ ++ return ret; ++} ++ ++ + static int do_getidbysid(PyObject *py_result, PyObject *py_sid) + { + const char *sid; +@@ -231,6 +310,9 @@ static int do_lookup(enum lookup_type type, PyObject *py_result, + case NAMEBYCERT: + return do_getnamebycert(py_result, py_inp); + break; ++ case LISTBYCERT: ++ return do_getlistbycert(py_result, py_inp); ++ break; + default: + return ENOSYS; + } +@@ -368,7 +450,7 @@ static PyObject * py_getidbysid(PyObject *module, PyObject *args) + } + + PyDoc_STRVAR(getnamebycert_doc, +-"getnamebycert(sid or list/tuple of certificates) -> dict(sid => dict(results))\n\ ++"getnamebycert(certificate or list/tuple of certificates) -> dict(certificate => dict(results))\n\ + \n\ + Returns a dictionary with a dictonary of results for each given certificates.\n\ + The result dictonary contain the name and the type of the object which can be\n\ +@@ -382,6 +464,21 @@ static PyObject * py_getnamebycert(PyObject *module, PyObject *args) + return check_args(NAMEBYCERT, args); + } + ++PyDoc_STRVAR(getlistbycert_doc, ++"getnamebycert(certificate or list/tuple of certificates) -> dict(certificate => dict(results))\n\ ++\n\ ++Returns a dictionary with a dictonary of results for each given certificates.\n\ ++The result dictonary contain the name and the type of the object which can be\n\ ++accessed with the key constants NAME_KEY and TYPE_KEY, respectively.\n\ ++\n\ ++NOTE: getlistbycert currently works only with id_provider set as \"ad\" or \"ipa\"" ++); ++ ++static PyObject * py_getlistbycert(PyObject *module, PyObject *args) ++{ ++ return check_args(LISTBYCERT, args); ++} ++ + static PyMethodDef methods[] = { + { sss_py_const_p(char, "getsidbyname"), (PyCFunction) py_getsidbyname, + METH_VARARGS, getsidbyname_doc }, +@@ -393,6 +490,8 @@ static PyMethodDef methods[] = { + METH_VARARGS, getidbysid_doc }, + { sss_py_const_p(char, "getnamebycert"), (PyCFunction) py_getnamebycert, + METH_VARARGS, getnamebycert_doc }, ++ { sss_py_const_p(char, "getlistbycert"), (PyCFunction) py_getlistbycert, ++ METH_VARARGS, getlistbycert_doc }, + { NULL,NULL, 0, NULL } + }; + +diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c +index 08b3d32f2662efc1cc803f6e9e5f2d064f7d3033..1931bf62a686c7f30852dac547866609cf54a81b 100644 +--- a/src/responder/nss/nss_cmd.c ++++ b/src/responder/nss/nss_cmd.c +@@ -932,6 +932,12 @@ static errno_t nss_cmd_getnamebycert(struct cli_ctx *cli_ctx) + nss_protocol_fill_single_name); + } + ++static errno_t nss_cmd_getlistbycert(struct cli_ctx *cli_ctx) ++{ ++ return nss_getby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT, ++ nss_protocol_fill_name_list); ++} ++ + struct sss_cmd_table *get_nss_cmds(void) + { + static struct sss_cmd_table nss_cmds[] = { +@@ -961,6 +967,7 @@ struct sss_cmd_table *get_nss_cmds(void) + { SSS_NSS_GETIDBYSID, nss_cmd_getidbysid }, + { SSS_NSS_GETORIGBYNAME, nss_cmd_getorigbyname }, + { SSS_NSS_GETNAMEBYCERT, nss_cmd_getnamebycert }, ++ { SSS_NSS_GETLISTBYCERT, nss_cmd_getlistbycert }, + { SSS_CLI_NULL, NULL } + }; + +diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h +index c94e7b911eb3c0f97b8c06b1766573311cde41ae..e4c0e52c0e642e885ef2c8423ea564beff7242cf 100644 +--- a/src/responder/nss/nss_protocol.h ++++ b/src/responder/nss/nss_protocol.h +@@ -175,6 +175,12 @@ nss_protocol_fill_single_name(struct nss_ctx *nss_ctx, + struct cache_req_result *result); + + errno_t ++nss_protocol_fill_name_list(struct nss_ctx *nss_ctx, ++ struct nss_cmd_ctx *cmd_ctx, ++ struct sss_packet *packet, ++ struct cache_req_result *result); ++ ++errno_t + nss_protocol_fill_id(struct nss_ctx *nss_ctx, + struct nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, +diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c +index 0b97e65f75412d40832d861568d8e2f9de5e1732..a6a4e27d039c67ef98f6d5900d5e3fcadb3ee717 100644 +--- a/src/responder/nss/nss_protocol_sid.c ++++ b/src/responder/nss/nss_protocol_sid.c +@@ -498,3 +498,66 @@ nss_protocol_fill_id(struct nss_ctx *nss_ctx, + + return EOK; + } ++ ++errno_t ++nss_protocol_fill_name_list(struct nss_ctx *nss_ctx, ++ struct nss_cmd_ctx *cmd_ctx, ++ struct sss_packet *packet, ++ struct cache_req_result *result) ++{ ++ enum sss_id_type *id_types; ++ size_t rp = 0; ++ size_t body_len; ++ uint8_t *body; ++ errno_t ret; ++ struct sized_string *sz_names; ++ size_t len; ++ size_t c; ++ const char *tmp_str; ++ ++ sz_names = talloc_array(cmd_ctx, struct sized_string, result->count); ++ if (sz_names == NULL) { ++ return ENOMEM; ++ } ++ ++ id_types = talloc_array(cmd_ctx, enum sss_id_type, result->count); ++ if (id_types == NULL) { ++ return ENOMEM; ++ } ++ ++ len = 0; ++ for (c = 0; c < result->count; c++) { ++ ret = nss_get_id_type(cmd_ctx, result, &(id_types[c])); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ tmp_str = nss_get_name_from_msg(result->domain, result->msgs[c]); ++ if (tmp_str == NULL) { ++ return EINVAL; ++ } ++ to_sized_string(&(sz_names[c]), tmp_str); ++ ++ len += sz_names[c].len; ++ } ++ ++ len += (2 + result->count) * sizeof(uint32_t); ++ ++ ret = sss_packet_grow(packet, len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); ++ return ret; ++ } ++ ++ sss_packet_get_body(packet, &body, &body_len); ++ ++ SAFEALIGN_SET_UINT32(&body[rp], result->count, &rp); /* Num results. */ ++ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */ ++ for (c = 0; c < result->count; c++) { ++ SAFEALIGN_SET_UINT32(&body[rp], id_types[c], &rp); ++ SAFEALIGN_SET_STRING(&body[rp], sz_names[c].str, sz_names[c].len, ++ &rp); ++ } ++ ++ return EOK; ++} +diff --git a/src/sss_client/idmap/sss_nss_idmap.c b/src/sss_client/idmap/sss_nss_idmap.c +index fa5a499e3606f7e45a406de4d63002ba35365cb1..6f3af267a1e763e7dce77e3862be377ae2bfe984 100644 +--- a/src/sss_client/idmap/sss_nss_idmap.c ++++ b/src/sss_client/idmap/sss_nss_idmap.c +@@ -31,6 +31,7 @@ + #include "util/strtonum.h" + + #define DATA_START (3 * sizeof(uint32_t)) ++#define LIST_START (2 * sizeof(uint32_t)) + union input { + const char *str; + uint32_t id; +@@ -38,10 +39,12 @@ union input { + + struct output { + enum sss_id_type type; ++ enum sss_id_type *types; + union { + char *str; + uint32_t id; + struct sss_nss_kv *kv_list; ++ char **names; + } d; + }; + +@@ -72,6 +75,63 @@ void sss_nss_free_kv(struct sss_nss_kv *kv_list) + } + } + ++void sss_nss_free_list(char **l) ++{ ++ size_t c; ++ ++ if (l != NULL) { ++ for (c = 0; l[c] != NULL; c++) { ++ free(l[c]); ++ } ++ free(l); ++ } ++} ++ ++static int buf_to_name_type_list(uint8_t *buf, size_t buf_len, uint32_t num, ++ char ***names, enum sss_id_type **types) ++{ ++ int ret; ++ size_t c; ++ char **n = NULL; ++ enum sss_id_type *t = NULL; ++ size_t rp = 0; ++ ++ n = calloc(num + 1, sizeof(char *)); ++ if (n == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ t = calloc(num + 1, sizeof(enum sss_id_type)); ++ if (t == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (c = 0; c < num; c++) { ++ SAFEALIGN_COPY_UINT32(&(t[c]), buf + rp, &rp); ++ n[c] = strdup((char *) buf + rp); ++ if (n[c] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ rp += strlen(n[c]) + 1; ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ sss_nss_free_list(n); ++ free(t); ++ } else { ++ *names = n; ++ *types = t; ++ } ++ ++ return ret; ++} ++ + static int buf_to_kv_list(uint8_t *buf, size_t buf_len, + struct sss_nss_kv **kv_list) + { +@@ -153,13 +213,14 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd , + size_t data_len; + uint32_t c; + struct sss_nss_kv *kv_list; ++ char **names; ++ enum sss_id_type *types; + + switch (cmd) { + case SSS_NSS_GETSIDBYNAME: + case SSS_NSS_GETNAMEBYSID: + case SSS_NSS_GETIDBYSID: + case SSS_NSS_GETORIGBYNAME: +- case SSS_NSS_GETNAMEBYCERT: + ret = sss_strnlen(inp.str, 2048, &inp_len); + if (ret != EOK) { + return EINVAL; +@@ -169,6 +230,17 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd , + rd.data = inp.str; + + break; ++ case SSS_NSS_GETNAMEBYCERT: ++ case SSS_NSS_GETLISTBYCERT: ++ ret = sss_strnlen(inp.str, 10 * 1024 , &inp_len); ++ if (ret != EOK) { ++ return EINVAL; ++ } ++ ++ rd.len = inp_len + 1; ++ rd.data = inp.str; ++ ++ break; + case SSS_NSS_GETSIDBYID: + rd.len = sizeof(uint32_t); + rd.data = &inp.id; +@@ -195,7 +267,7 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd , + if (num_results == 0) { + ret = ENOENT; + goto done; +- } else if (num_results > 1) { ++ } else if (num_results > 1 && cmd != SSS_NSS_GETLISTBYCERT) { + ret = EBADMSG; + goto done; + } +@@ -237,6 +309,18 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd , + out->d.id = c; + + break; ++ case SSS_NSS_GETLISTBYCERT: ++ ret = buf_to_name_type_list(repbuf + LIST_START, replen - LIST_START, ++ num_results, ++ &names, &types); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ out->types = types; ++ out->d.names = names; ++ ++ break; + case SSS_NSS_GETORIGBYNAME: + ret = buf_to_kv_list(repbuf + DATA_START, data_len, &kv_list); + if (ret != EOK) { +@@ -392,3 +476,25 @@ int sss_nss_getnamebycert(const char *cert, char **fq_name, + + return ret; + } ++ ++int sss_nss_getlistbycert(const char *cert, char ***fq_name, ++ enum sss_id_type **type) ++{ ++ int ret; ++ union input inp; ++ struct output out; ++ ++ if (fq_name == NULL || cert == NULL || *cert == '\0') { ++ return EINVAL; ++ } ++ ++ inp.str = cert; ++ ++ ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETLISTBYCERT, &out); ++ if (ret == EOK) { ++ *fq_name = out.d.names; ++ *type = out.types; ++ } ++ ++ return ret; ++} +diff --git a/src/sss_client/idmap/sss_nss_idmap.exports b/src/sss_client/idmap/sss_nss_idmap.exports +index bd5d80212017d38334c3cdeefa47d6029f42aebb..49dac6fc9351b0ca98cd46e83b85ec8ef0075a0d 100644 +--- a/src/sss_client/idmap/sss_nss_idmap.exports ++++ b/src/sss_client/idmap/sss_nss_idmap.exports +@@ -25,3 +25,9 @@ SSS_NSS_IDMAP_0.2.0 { + global: + sss_nss_getnamebycert; + } SSS_NSS_IDMAP_0.1.0; ++ ++SSS_NSS_IDMAP_0.3.0 { ++ # public functions ++ global: ++ sss_nss_getlistbycert; ++} SSS_NSS_IDMAP_0.2.0; +diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h +index 8a6299194e7b91e084b26c0c96e2f93875a832e7..cbf19479ff9ec6e0d6e07e1f7e48a1571e147740 100644 +--- a/src/sss_client/idmap/sss_nss_idmap.h ++++ b/src/sss_client/idmap/sss_nss_idmap.h +@@ -130,7 +130,7 @@ int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list, + * @param[in] cert base64 encoded certificate + * @param[out] fq_name Fully qualified name of a user or a group, + * must be freed by the caller +- * @param[out] type Type of the object related to the SID ++ * @param[out] type Type of the object related to the cert + * + * @return + * - see #sss_nss_getsidbyname +@@ -139,6 +139,21 @@ int sss_nss_getnamebycert(const char *cert, char **fq_name, + enum sss_id_type *type); + + /** ++ * @brief Return a list of fully qualified names for the given base64 encoded ++ * X.509 certificate in DER format ++ * ++ * @param[in] cert base64 encoded certificate ++ * @param[out] fq_name List of fully qualified name of users or groups, ++ * must be freed by the caller ++ * @param[out] type List of types of the objects related to the cert ++ * ++ * @return ++ * - see #sss_nss_getsidbyname ++ */ ++int sss_nss_getlistbycert(const char *cert, char ***fq_name, ++ enum sss_id_type **type); ++ ++/** + * @brief Free key-value list returned by sss_nss_getorigbyname() + * + * @param[in] kv_list Key-value list returned by sss_nss_getorigbyname(). +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index 8091e11515184dc9b7f32eed535055d9eee3143f..59fee7a4eceb2c185e156e812af7f2f4c6b2a0dd 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -260,6 +260,11 @@ SSS_NSS_GETNAMEBYCERT = 0x0116, /**< Takes the zero terminated string + of a X509 certificate and returns the zero + terminated fully qualified name of the + related object. */ ++SSS_NSS_GETLISTBYCERT = 0x0117, /**< Takes the zero terminated string ++ of the base64 encoded DER representation ++ of a X509 certificate and returns a list ++ of zero terminated fully qualified names ++ of the related objects. */ + }; + + /** +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 76b9c6fb05673130de0957e93291919c263a28f3..50714715cc80338640f2a77ecbe17bd5e0d6e911 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -3454,6 +3454,16 @@ struct passwd testbycert = { + .pw_passwd = discard_const("*"), + }; + ++struct passwd testbycert2 = { ++ .pw_name = discard_const("testcertuser2"), ++ .pw_uid = 23457, ++ .pw_gid = 6890, ++ .pw_dir = discard_const("/home/testcertuser2"), ++ .pw_gecos = discard_const("test cert user2"), ++ .pw_shell = discard_const("/bin/sh"), ++ .pw_passwd = discard_const("*"), ++}; ++ + #define TEST_TOKEN_CERT \ + "MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \ + "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \ +@@ -3495,6 +3505,57 @@ static int test_nss_getnamebycert_check(uint32_t status, uint8_t *body, size_t b + return EOK; + } + ++static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t blen) ++{ ++ size_t rp = 0; ++ uint32_t id_type; ++ uint32_t num; ++ uint32_t reserved; ++ const char *name; ++ int found = 0; ++ const char *fq_name1 = "testcertuser@"TEST_DOM_NAME ; ++ const char *fq_name2 = "testcertuser2@"TEST_DOM_NAME; ++ ++ assert_int_equal(status, EOK); ++ ++ /* num_results and reserved */ ++ SAFEALIGN_COPY_UINT32(&num, body + rp, &rp); ++ assert_in_range(num, 1, 2); ++ SAFEALIGN_COPY_UINT32(&reserved, body + rp, &rp); ++ assert_int_equal(reserved, 0); ++ ++ SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp); ++ assert_int_equal(id_type, SSS_ID_TYPE_UID); ++ ++ name = (const char *)body + rp; ++ if (num == 1) { ++ assert_string_equal(name, fq_name1); ++ return EOK; ++ } ++ ++ rp += strlen(name) + 1; ++ if (strcmp(name, fq_name1) == 0) { ++ found = 1; ++ } else if (strcmp(name, fq_name2) == 0) { ++ found = 2; ++ } ++ assert_in_range(found, 1, 2); ++ ++ SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp); ++ assert_int_equal(id_type, SSS_ID_TYPE_UID); ++ ++ name = (const char *)body + rp; ++ if (found == 1) { ++ assert_string_equal(name, fq_name2); ++ } else { ++ assert_string_equal(name, fq_name1); ++ } ++ ++ ++ return EOK; ++} ++ ++ + static void test_nss_getnamebycert(void **state) + { + errno_t ret; +@@ -3572,6 +3633,99 @@ void test_nss_getnamebycert_neg(void **state) + assert_int_equal(nss_test_ctx->ncache_hits, 1); + } + ++static void test_nss_getlistbycert(void **state) ++{ ++ errno_t ret; ++ struct sysdb_attrs *attrs; ++ unsigned char *der = NULL; ++ size_t der_size; ++ ++ attrs = sysdb_new_attrs(nss_test_ctx); ++ assert_non_null(attrs); ++ ++ der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size); ++ assert_non_null(der); ++ ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ talloc_free(der); ++ assert_int_equal(ret, EOK); ++ ++ /* Prime the cache with a valid user */ ++ ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom, ++ &testbycert, attrs, 0); ++ assert_int_equal(ret, EOK); ++ talloc_free(attrs); ++ ++ mock_input_cert(TEST_TOKEN_CERT); ++ will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETLISTBYCERT); ++ mock_fill_bysid(); ++ ++ /* Query for that user, call a callback when command finishes */ ++ /* Should go straight to back end, without contacting DP. */ ++ /* If there is only a single user mapped the result will look like the */ ++ /* result of getnamebycert. */ ++ set_cmd_cb(test_nss_getlistbycert_check); ++ ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT, ++ nss_test_ctx->nss_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(nss_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++static void test_nss_getlistbycert_multi(void **state) ++{ ++ errno_t ret; ++ struct sysdb_attrs *attrs; ++ unsigned char *der = NULL; ++ size_t der_size; ++ ++ der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size); ++ assert_non_null(der); ++ ++ attrs = sysdb_new_attrs(nss_test_ctx); ++ assert_non_null(attrs); ++ ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ assert_int_equal(ret, EOK); ++ ++ /* Prime the cache with two valid user */ ++ ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom, ++ &testbycert, attrs, 0); ++ assert_int_equal(ret, EOK); ++ talloc_free(attrs); ++ ++ /* Looks like attrs is modified during store_user() makes sure we start ++ * with fresh data. */ ++ attrs = sysdb_new_attrs(nss_test_ctx); ++ assert_non_null(attrs); ++ ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ talloc_free(der); ++ assert_int_equal(ret, EOK); ++ ++ ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom, ++ &testbycert2, attrs, 0); ++ assert_int_equal(ret, EOK); ++ talloc_free(attrs); ++ ++ mock_input_cert(TEST_TOKEN_CERT); ++ will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETLISTBYCERT); ++ mock_fill_bysid(); ++ ++ /* Query for that user, call a callback when command finishes */ ++ /* Should go straight to back end, without contacting DP */ ++ set_cmd_cb(test_nss_getlistbycert_check); ++ ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT, ++ nss_test_ctx->nss_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(nss_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ + struct passwd sid_user = { + .pw_name = discard_const("testusersid"), + .pw_uid = 1234, +@@ -3818,6 +3972,10 @@ int main(int argc, const char *argv[]) + nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getnamebycert, + nss_test_setup, nss_test_teardown), ++ cmocka_unit_test_setup_teardown(test_nss_getlistbycert, ++ nss_test_setup, nss_test_teardown), ++ cmocka_unit_test_setup_teardown(test_nss_getlistbycert_multi, ++ nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getsidbyname, + nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getsidbyupn, +-- +2.9.3 + diff --git a/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch b/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch new file mode 100644 index 0000000..d0dea12 --- /dev/null +++ b/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch @@ -0,0 +1,70 @@ +From e7c9ff18f41d9951aff3c99dca7db1871e53cfaf Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 28 Feb 2017 14:19:53 +0100 +Subject: [PATCH 13/15] nss: allow larger buffer for certificate based requests + +To make sure larger certificates can be processed as well the maximal +buffer size is increased for requests by certificate. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +--- + src/responder/common/responder_packet.c | 21 ++++++++++++++++++++- + src/responder/common/responder_packet.h | 1 + + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c +index 4f5e110837eb76609d31a77c62a00e00530ffc90..cc4d66995965cca4c86a80c31d2afd4c9ac3e0e4 100644 +--- a/src/responder/common/responder_packet.c ++++ b/src/responder/common/responder_packet.c +@@ -179,6 +179,8 @@ int sss_packet_recv(struct sss_packet *packet, int fd) + size_t rb; + size_t len; + void *buf; ++ size_t new_len; ++ int ret; + + buf = (uint8_t *)packet->buffer + packet->iop; + if (packet->iop > 4) len = sss_packet_get_len(packet) - packet->iop; +@@ -205,7 +207,24 @@ int sss_packet_recv(struct sss_packet *packet, int fd) + } + + if (sss_packet_get_len(packet) > packet->memsize) { +- return EINVAL; ++ /* Allow certificate based requests to use larger buffer but not ++ * larger than SSS_CERT_PACKET_MAX_RECV_SIZE. Due to the way ++ * sss_packet_grow() works the packet len must be set to '0' first and ++ * then grow to the expected size. */ ++ if ((sss_packet_get_cmd(packet) == SSS_NSS_GETNAMEBYCERT ++ || sss_packet_get_cmd(packet) == SSS_NSS_GETLISTBYCERT) ++ && packet->memsize < SSS_CERT_PACKET_MAX_RECV_SIZE ++ && (new_len = sss_packet_get_len(packet)) ++ < SSS_CERT_PACKET_MAX_RECV_SIZE) { ++ new_len = sss_packet_get_len(packet); ++ sss_packet_set_len(packet, 0); ++ ret = sss_packet_grow(packet, new_len); ++ if (ret != EOK) { ++ return ret; ++ } ++ } else { ++ return EINVAL; ++ } + } + + packet->iop += rb; +diff --git a/src/responder/common/responder_packet.h b/src/responder/common/responder_packet.h +index 3ad0eee28477e446c9e4996617beb55f32923d47..afceb4aaefa40fd86bdfde820c92c09b65cd8702 100644 +--- a/src/responder/common/responder_packet.h ++++ b/src/responder/common/responder_packet.h +@@ -25,6 +25,7 @@ + #include "sss_client/sss_cli.h" + + #define SSS_PACKET_MAX_RECV_SIZE 1024 ++#define SSS_CERT_PACKET_MAX_RECV_SIZE ( 10 * SSS_PACKET_MAX_RECV_SIZE ) + + struct sss_packet; + +-- +2.9.3 + diff --git a/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch b/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch new file mode 100644 index 0000000..287bfdf --- /dev/null +++ b/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch @@ -0,0 +1,44 @@ +From 8820b7bba01312419171c4949a9f1c5c8c061a55 Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Mon, 20 Mar 2017 11:51:05 -0400 +Subject: [PATCH 14/15] IPA: Add s2n request to string function + +Add a function to convert request_types to string allowing the +ability to print request type information for ipa_s2n functions during +IPA client operations. + +Reviewed-by: Sumit Bose +--- + src/providers/ipa/ipa_s2n_exop.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 07bbb2b4d252c8ca9ada4d890c36c903c9f75773..4fe20689fe4c0f2bb5217691dd05b37d2a1cc820 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -979,6 +979,22 @@ done: + return ret; + } + ++static const char *ipa_s2n_reqtype2str(enum request_types request_type) ++{ ++ switch (request_type) { ++ case REQ_SIMPLE: ++ return "REQ_SIMPLE"; ++ case REQ_FULL: ++ return "REQ_FULL"; ++ case REQ_FULL_WITH_MEMBERS: ++ return "REQ_FULL_WITH_MEMBERS"; ++ default: ++ break; ++ } ++ ++ return "Unknown request type"; ++} ++ + struct ipa_s2n_get_list_state { + struct tevent_context *ev; + struct ipa_id_ctx *ipa_ctx; +-- +2.9.3 + diff --git a/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch b/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch new file mode 100644 index 0000000..8031b4c --- /dev/null +++ b/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch @@ -0,0 +1,82 @@ +From 359fe83281f2f54d71da65879eafec5ae383f33f Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Thu, 16 Mar 2017 14:46:55 -0400 +Subject: [PATCH 15/15] IPA: Enhance debug logging for ipa s2n operations + +Add log messages to provide useful debug logging surrounding +IPA client extended operations to the IPA Server during AD trust +requests to retrieve information. Print more details about the +objects requested and received during the ipa_s2n operations. + +This will improve log analysis and troubleshooting efforts during AD +trust user and group resolution failures on IPA clients, such as missing +groups. + +Reviewed-by: Sumit Bose +--- + src/providers/ipa/ipa_s2n_exop.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 4fe20689fe4c0f2bb5217691dd05b37d2a1cc820..c99312274073858e5e03f3e82c069dafc839eb61 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -1156,6 +1156,13 @@ static errno_t ipa_s2n_get_list_step(struct tevent_req *req) + need_v1 = true; + } + ++ if (state->req_input.type == REQ_INP_NAME ++ && state->req_input.inp.name != NULL) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for group [%s].\n", ++ ipa_s2n_reqtype2str(state->request_type), ++ state->list[state->list_idx]); ++ } ++ + subreq = ipa_s2n_exop_send(state, state->ev, state->sh, need_v1, + state->exop_timeout, bv_req); + if (subreq == NULL) { +@@ -1194,6 +1201,9 @@ static void ipa_s2n_get_list_next(struct tevent_req *subreq) + goto fail; + } + ++ DEBUG(SSSDBG_TRACE_FUNC, "Received [%s] attributes from IPA server.\n", ++ state->attrs->a.name); ++ + if (is_default_view(state->ipa_ctx->view_name)) { + ret = ipa_s2n_get_list_save_step(req); + if (ret == EOK) { +@@ -1375,6 +1385,11 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx, + goto fail; + } + ++ DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for trust user [%s] " ++ "to IPA server\n", ++ ipa_s2n_reqtype2str(state->request_type), ++ req_input->inp.name); ++ + subreq = ipa_s2n_exop_send(state, state->ev, state->sh, is_v1, + state->exop_timeout, bv_req); + if (subreq == NULL) { +@@ -1661,6 +1676,19 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq) + state->attrs = attrs; + + if (attrs->response_type == RESP_USER_GROUPLIST) { ++ ++ if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) { ++ size_t c; ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Received [%zu] groups in group list " ++ "from IPA Server\n", attrs->ngroups); ++ ++ for (c = 0; c < attrs->ngroups; c++) { ++ DEBUG(SSSDBG_TRACE_FUNC, "[%s].\n", attrs->groups[c]); ++ } ++ } ++ ++ + ret = get_group_dn_list(state, state->dom, + attrs->ngroups, attrs->groups, + &group_dn_list, &missing_list); +-- +2.9.3 + diff --git a/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch b/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch new file mode 100644 index 0000000..5ded3ab --- /dev/null +++ b/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch @@ -0,0 +1,45 @@ +From 4ea851ed034efdb06d13b34797b9f849e3dcec97 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 15 Mar 2017 13:32:42 +0100 +Subject: [PATCH 16/36] UTIL: iobuf: Make input parameter for the readonly + operation const +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/util/sss_iobuf.c | 2 +- + src/util/sss_iobuf.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c +index 7c72ea94d7a005dfd9671793b3ad470a6de7967a..900418b750a3455ebc2c3bb1893db726692260b8 100644 +--- a/src/util/sss_iobuf.c ++++ b/src/util/sss_iobuf.c +@@ -49,7 +49,7 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, + } + + struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, +- uint8_t *data, ++ const uint8_t *data, + size_t size) + { + struct sss_iobuf *iobuf; +diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h +index eae357a40f2948e63df189f2842edee68691a542..900faaa212230f72f52e344c085167e80ae2b465 100644 +--- a/src/util/sss_iobuf.h ++++ b/src/util/sss_iobuf.h +@@ -47,7 +47,7 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, + * @return The newly created buffer on success or NULL on an error. + */ + struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, +- uint8_t *data, ++ const uint8_t *data, + size_t size); + + /* +-- +2.9.3 + diff --git a/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch b/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch new file mode 100644 index 0000000..c51e496 --- /dev/null +++ b/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch @@ -0,0 +1,32 @@ +From c402799ea8b24d2e382d0ad7a06ee92861852972 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 15 Mar 2017 13:42:03 +0100 +Subject: [PATCH 17/36] UTIL: Fix a typo in the tcurl test tool +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/tests/tcurl_test_tool.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 35ea979780df47c92ed92bc5bba713faa8909b90..38cea432885c97ca3827c8f158bf7e3ebfc67b31 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -204,8 +204,8 @@ int main(int argc, const char *argv[]) + urls[i], + headers, + inbufs[i], +- 10); +- if (ctx == NULL) { ++ 5); ++ if (req == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n"); + talloc_zfree(tool_ctx); + return 1; +-- +2.9.3 + diff --git a/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch b/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch new file mode 100644 index 0000000..bb11f98 --- /dev/null +++ b/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch @@ -0,0 +1,36 @@ +From 4aecf8a2d3962d962da1e2f98b0bb3b84a8ae536 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 23 Feb 2017 20:55:05 +0100 +Subject: [PATCH 18/36] UTIL: Add SAFEALIGN_COPY_UINT8_CHECK +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This macro will be used later in the KCM code + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/util/util_safealign.h | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/util/util_safealign.h b/src/util/util_safealign.h +index 0d9a579cdbfafc30bf2d0a6ad2651c71428ebd93..57f04a17d4a38300b959c1593d756b351ebd89e8 100644 +--- a/src/util/util_safealign.h ++++ b/src/util/util_safealign.h +@@ -130,6 +130,12 @@ safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter) + safealign_memcpy(dest, src, srclen, pctr); \ + } while(0) + ++#define SAFEALIGN_COPY_UINT8_CHECK(dest, src, len, pctr) do { \ ++ if ((*(pctr) + sizeof(uint8_t)) > (len) || \ ++ SIZE_T_OVERFLOW(*(pctr), sizeof(uint8_t))) { return EINVAL; } \ ++ safealign_memcpy(dest, src, sizeof(uint8_t), pctr); \ ++} while(0) ++ + /* Aliases for backward compatibility. */ + #define SAFEALIGN_SET_VALUE SAFEALIGN_SETMEM_VALUE + #define SAFEALIGN_SET_INT64 SAFEALIGN_SETMEM_INT64 +-- +2.9.3 + diff --git a/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch b/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch new file mode 100644 index 0000000..84752b5 --- /dev/null +++ b/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch @@ -0,0 +1,34 @@ +From 62acd53dc2880b23cfb221ce40537abfb6e011bb Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 20 Sep 2016 22:00:27 +0200 +Subject: [PATCH 19/36] UTIL: Add utility macro cli_creds_get_gid() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The KCM responder checks the owneship of the ccache based on both UID +and GID of the peer. In order to reuse the already existing creds +structure, let's just add a new macro that returns the GID from the +creds structure. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/util/util_creds.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/util/util_creds.h b/src/util/util_creds.h +index 65468fa12b8c6921859574c40f5759c936a10e86..936b9965d1ccd2b437d93b38d789b6f8389f47a6 100644 +--- a/src/util/util_creds.h ++++ b/src/util/util_creds.h +@@ -71,6 +71,7 @@ struct cli_creds { + }; + + #define cli_creds_get_uid(x) x->ucred.uid ++#define cli_creds_get_gid(x) x->ucred.gid + + #else /* not HAVE_UCRED */ + struct cli_creds { +-- +2.9.3 + diff --git a/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch b/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch new file mode 100644 index 0000000..97c4775 --- /dev/null +++ b/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch @@ -0,0 +1,194 @@ +From cef2ade5294bd9dc06f7eca26287c2e90e2c2ee1 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 23 Feb 2017 21:57:13 +0100 +Subject: [PATCH 20/36] UTIL: Add type-specific getsetters to sss_iobuf +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The KCM responder receives its input as unstructured data. To make the +parsing easier, this commit adds several type-specific getsetters to the +iobuf module. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/util/sss_iobuf.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++ + src/util/sss_iobuf.h | 33 ++++++++++++++++ + 2 files changed, 141 insertions(+) + +diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c +index 900418b750a3455ebc2c3bb1893db726692260b8..fc288d2df2bfaaba393dd490d4da8976de804cb5 100644 +--- a/src/util/sss_iobuf.c ++++ b/src/util/sss_iobuf.c +@@ -184,6 +184,25 @@ errno_t sss_iobuf_read(struct sss_iobuf *iobuf, + return EOK; + } + ++errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf, ++ size_t len, ++ uint8_t *_buf) ++{ ++ size_t read; ++ errno_t ret; ++ ++ ret = sss_iobuf_read(iobuf, len, _buf, &read); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ if (read != len) { ++ return ENOBUFS; ++ } ++ ++ return EOK; ++} ++ + errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf, + uint8_t *buf, + size_t len) +@@ -203,3 +222,92 @@ errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf, + + return EOK; + } ++ ++errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf, ++ uint32_t *_val) ++{ ++ SAFEALIGN_COPY_UINT32_CHECK(_val, iobuf_ptr(iobuf), ++ iobuf->capacity, &iobuf->dp); ++ return EOK; ++} ++ ++errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf, ++ int32_t *_val) ++{ ++ SAFEALIGN_COPY_INT32_CHECK(_val, iobuf_ptr(iobuf), ++ iobuf->capacity, &iobuf->dp); ++ return EOK; ++} ++ ++errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf, ++ uint32_t val) ++{ ++ errno_t ret; ++ ++ ret = ensure_bytes(iobuf, sizeof(uint32_t)); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ SAFEALIGN_SETMEM_UINT32(iobuf_ptr(iobuf), val, &iobuf->dp); ++ return EOK; ++} ++ ++errno_t sss_iobuf_write_int32(struct sss_iobuf *iobuf, ++ int32_t val) ++{ ++ errno_t ret; ++ ++ ret = ensure_bytes(iobuf, sizeof(int32_t)); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ SAFEALIGN_SETMEM_INT32(iobuf_ptr(iobuf), val, &iobuf->dp); ++ return EOK; ++} ++ ++errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf, ++ const char **_out) ++{ ++ uint8_t *end; ++ size_t len; ++ ++ if (iobuf == NULL) { ++ return EINVAL; ++ } ++ ++ if (_out == NULL) { ++ return EINVAL; ++ } ++ ++ *_out = NULL; ++ ++ end = memchr(iobuf_ptr(iobuf), '\0', sss_iobuf_get_size(iobuf)); ++ if (end == NULL) { ++ return EINVAL; ++ } ++ ++ len = end + 1 - iobuf_ptr(iobuf); ++ if (sss_iobuf_get_size(iobuf) < len) { ++ return EINVAL; ++ } ++ ++ *_out = (const char *) iobuf_ptr(iobuf); ++ iobuf->dp += len; ++ return EOK; ++} ++ ++errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf, ++ const char *str) ++{ ++ if (iobuf == NULL || str == NULL) { ++ return EINVAL; ++ } ++ ++ SAFEALIGN_MEMCPY_CHECK(iobuf_ptr(iobuf), ++ str, strlen(str)+1, ++ sss_iobuf_get_size(iobuf), ++ &iobuf->dp); ++ return EOK; ++} +diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h +index 900faaa212230f72f52e344c085167e80ae2b465..cc3dfd1e98eeb49b979ac321bd0253bffa8a6dff 100644 +--- a/src/util/sss_iobuf.h ++++ b/src/util/sss_iobuf.h +@@ -96,6 +96,22 @@ errno_t sss_iobuf_read(struct sss_iobuf *iobuf, + size_t *_read); + + /* ++ * @brief Read an exact number of bytes from an IO buffer ++ * ++ * Read exactly len bytes from an IO buffer. If the buffer contains fewer ++ * bytes than len, ENOBUFS is returned. ++ * ++ * @param[in] iobuf The IO buffer to read from ++ * @param[in] len The maximum number of bytes to read ++ * @param[out] _buf The buffer to read data into from iobuf ++ * ++ * @return EOK on success, errno otherwise ++ */ ++errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf, ++ size_t len, ++ uint8_t *_buf); ++ ++/* + * @brief Write into an IO buffer + * + * Attempts to write len bytes into the iobuf. If the capacity is exceeded, +@@ -115,4 +131,21 @@ errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf, + uint8_t *buf, + size_t len); + ++errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf, ++ uint32_t *_val); ++ ++errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf, ++ uint32_t val); ++ ++errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf, ++ int32_t *_val); ++ ++errno_t sss_iobuf_write_int32(struct sss_iobuf *iobuf, ++ int32_t val); ++ ++errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf, ++ const char **_out); ++ ++errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf, ++ const char *str); + #endif /* __SSS_IOBUF_H_ */ +-- +2.9.3 + diff --git a/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch b/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch new file mode 100644 index 0000000..c088f68 --- /dev/null +++ b/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch @@ -0,0 +1,262 @@ +From aa309f35905951c6fdd12d286bb3aeeb61a62088 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 20 Sep 2016 22:03:30 +0200 +Subject: [PATCH 21/36] UTIL: krb5 principal (un)marshalling +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The KCM responder needs to read the contents of the principal blob that +the Kerberos library sends. Since libkrb5 doesn't export any API to do +so, we need to implement marshalling and unmarshalling of the principal +ourselves. + +In future, when the KCM server also supports renewals, we will also need +to unmarshall the credentials, but until that is not really needed, the +credentials will be stored as a blob. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/util/sss_krb5.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/util/sss_krb5.h | 9 +++ + 2 files changed, 204 insertions(+) + +diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c +index 4808a7703d07bb4eba91f14a7a515aadaec1774b..d461cf881566af37f31524c16f6a5f1511a5dc89 100644 +--- a/src/util/sss_krb5.c ++++ b/src/util/sss_krb5.c +@@ -24,6 +24,7 @@ + + #include "config.h" + ++#include "util/sss_iobuf.h" + #include "util/util.h" + #include "util/sss_krb5.h" + +@@ -1128,3 +1129,197 @@ done: + + return res; + } ++ ++static errno_t iobuf_read_uint32be(struct sss_iobuf *iobuf, ++ uint32_t *_val) ++{ ++ uint32_t beval; ++ errno_t ret; ++ ++ ret = sss_iobuf_read_uint32(iobuf, &beval); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ *_val = be32toh(beval); ++ return EOK; ++} ++ ++static errno_t iobuf_write_uint32be(struct sss_iobuf *iobuf, ++ uint32_t val) ++{ ++ uint32_t beval; ++ ++ beval = htobe32(val); ++ return sss_iobuf_write_uint32(iobuf, beval); ++} ++ ++static errno_t iobuf_get_len_bytes(TALLOC_CTX *mem_ctx, ++ struct sss_iobuf *iobuf, ++ uint32_t *_nbytes, ++ uint8_t **_bytes) ++{ ++ errno_t ret; ++ uint32_t nbytes; ++ uint8_t *bytes = NULL; ++ ++ ret = iobuf_read_uint32be(iobuf, &nbytes); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ bytes = talloc_zero_size(mem_ctx, nbytes); ++ if (bytes == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sss_iobuf_read_len(iobuf, nbytes, bytes); ++ if (ret != EOK) { ++ talloc_free(bytes); ++ return ret; ++ } ++ ++ *_bytes = bytes; ++ *_nbytes = nbytes; ++ return EOK; ++} ++ ++static errno_t get_krb5_data(TALLOC_CTX *mem_ctx, ++ struct sss_iobuf *iobuf, ++ krb5_data *k5data) ++{ ++ errno_t ret; ++ uint32_t nbytes; ++ uint8_t *bytes = NULL; ++ ++ ret = iobuf_get_len_bytes(mem_ctx, iobuf, &nbytes, &bytes); ++ if (ret != EOK) { ++ talloc_free(bytes); ++ return ret; ++ } ++ ++ k5data->data = (char *) bytes; /* FIXME - the cast is ugly */ ++ k5data->length = nbytes; ++ return EOK; ++} ++ ++static errno_t set_krb5_data(struct sss_iobuf *iobuf, ++ krb5_data *k5data) ++{ ++ errno_t ret; ++ ++ ret = iobuf_write_uint32be(iobuf, k5data->length); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ if (k5data->length > 0) { ++ ret = sss_iobuf_write_len(iobuf, ++ (uint8_t *) k5data->data, ++ k5data->length); ++ if (ret != EOK) { ++ return ret; ++ } ++ } ++ ++ return EOK; ++} ++ ++/* FIXME - it would be nice if Kerberos exported these APIs.. */ ++krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx, ++ struct sss_iobuf *iobuf, ++ krb5_principal *_princ) ++{ ++ krb5_principal princ = NULL; ++ krb5_error_code ret; ++ uint32_t ncomps; ++ ++ if (iobuf == NULL || _princ == NULL) { ++ return EINVAL; ++ } ++ ++ princ = talloc_zero(mem_ctx, struct krb5_principal_data); ++ if (princ == NULL) { ++ return ENOMEM; ++ } ++ ++ princ->magic = KV5M_PRINCIPAL; ++ ++ ret = iobuf_read_uint32be(iobuf, (uint32_t *) &princ->type); ++ if (ret != EOK) { ++ goto fail; ++ } ++ ++ ret = iobuf_read_uint32be(iobuf, &ncomps); ++ if (ret != EOK) { ++ goto fail; ++ } ++ ++ if (ncomps > sss_iobuf_get_capacity(iobuf)) { ++ /* Sanity check to avoid large allocations */ ++ ret = EINVAL; ++ goto fail; ++ } ++ ++ if (ncomps != 0) { ++ princ->data = talloc_zero_array(princ, krb5_data, ncomps); ++ if (princ->data == NULL) { ++ ret = ENOMEM; ++ goto fail; ++ } ++ ++ princ->length = ncomps; ++ } ++ ++ ret = get_krb5_data(princ, iobuf, &princ->realm); ++ if (ret != EOK) { ++ goto fail; ++ } ++ ++ for (size_t i = 0; i < ncomps; i++) { ++ ret = get_krb5_data(princ->data, iobuf, &princ->data[i]); ++ if (ret != EOK) { ++ goto fail; ++ } ++ } ++ ++ *_princ = princ; ++ return 0; ++ ++fail: ++ talloc_free(princ); ++ return ret; ++} ++ ++krb5_error_code sss_krb5_marshal_princ(krb5_principal princ, ++ struct sss_iobuf *iobuf) ++{ ++ krb5_error_code ret; ++ ++ if (iobuf == NULL || princ == NULL) { ++ return EINVAL; ++ } ++ ++ ret = iobuf_write_uint32be(iobuf, princ->type); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = iobuf_write_uint32be(iobuf, princ->length); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = set_krb5_data(iobuf, &princ->realm); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ for (int i = 0; i < princ->length; i++) { ++ ret = set_krb5_data(iobuf, &princ->data[i]); ++ if (ret != EOK) { ++ return ret; ++ } ++ } ++ return EOK; ++} +diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h +index ac0f6082c75a8878f72346733e592b7575d44089..0d9043be98749b1a21a1b74c68f07298fa27f230 100644 +--- a/src/util/sss_krb5.h ++++ b/src/util/sss_krb5.h +@@ -32,6 +32,7 @@ + #include + #endif + ++#include "util/sss_iobuf.h" + #include "util/util.h" + + #define KRB5_CHILD_LOG_FILE "krb5_child" +@@ -186,4 +187,12 @@ krb5_error_code sss_krb5_kt_have_content(krb5_context context, + krb5_keytab keytab); + + bool sss_krb5_realm_has_proxy(const char *realm); ++ ++krb5_error_code sss_krb5_marshal_princ(krb5_principal princ, ++ struct sss_iobuf *iobuf); ++ ++krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx, ++ struct sss_iobuf *iobuf, ++ krb5_principal *_princ); ++ + #endif /* __SSS_KRB5_H__ */ +-- +2.9.3 + diff --git a/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch b/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch new file mode 100644 index 0000000..c2ba5a6 --- /dev/null +++ b/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch @@ -0,0 +1,1013 @@ +From 8cb263f039da9e616e907d25701593dca22b11ed Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 1 Aug 2016 12:52:07 +0200 +Subject: [PATCH 22/36] KCM: Initial responder build and packaging +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds the initial build of the Kerberos Cache Manager responder (KCM). + +This is a deamon that is capable of holding and storing Kerberos +ccaches. When KCM is used, the kerberos libraries (invoked through e.g. +kinit) are referred to as a 'client' and the KCM deamon is referred to +as 'server'. + +At the moment, only the Heimdal implementation of Kerberos implements the +KCM server: + https://www.h5l.org/manual/HEAD/info/heimdal/Credential-cache-server-_002d-KCM.html +This patch adds a KCM server to SSSD. + +In MIT, only the 'client-side' support was added: + http://k5wiki.kerberos.org/wiki/Projects/KCM_client +This page also describes the protocol between the client and the server. + +The client is capable of talking to the server over either UNIX sockets +(Linux, most Unixes) or Mach RPC (macOS). Our server only implements the +UNIX sockets way and should be socket-activated by systemd, although can +in theory be also ran explicitly. + +The KCM server only builds if the configuration option "--with-kcm" is +enabled. It is packaged in a new subpackage sssd-kcm in order to allow +distributions to enable the KCM credential caches by installing this +subpackage only, without the rest of the SSSD. The sssd-kcm subpackage +also includes a krb5.conf.d snippet that allows the admin to just uncomment +the KCM defaults and instructs them to start the socket. + +The server can be configured in sssd.conf in the "[kcm]" section. +By default, the server only listens on the same socket path the Heimdal +server uses, which is "/var/run/.heim_org.h5l.kcm-socket". This is, +however, configurable. + +The file src/responder/kcm/kcm.h is more or less directly imported from +the MIT Kerberos tree, with an additional sentinel code and some +comments. Not all KCM operations are implemented, only those that also +the MIT client implements. That said, this KCM server should also be +usable with a Heimdal client, although no special testing was with this +hybrid. + +The patch also adds several error codes that will be used in later +patches. + +Related to: + https://pagure.io/SSSD/sssd/issue/2887 + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + Makefile.am | 53 ++++++++ + configure.ac | 10 +- + contrib/kcm_default_ccache | 12 ++ + contrib/sssd.spec.in | 41 ++++++ + src/conf_macros.m4 | 16 +++ + src/confdb/confdb.h | 3 + + src/config/cfg_rules.ini | 19 +++ + src/external/libcurl.m4 | 6 +- + src/responder/kcm/kcm.c | 254 +++++++++++++++++++++++++++++++++++ + src/responder/kcm/kcm.h | 97 +++++++++++++ + src/responder/kcm/kcmsrv_cmd.c | 65 +++++++++ + src/responder/kcm/kcmsrv_pvt.h | 58 ++++++++ + src/sysv/systemd/sssd-kcm.service.in | 9 ++ + src/sysv/systemd/sssd-kcm.socket.in | 10 ++ + src/util/util_errors.c | 5 + + src/util/util_errors.h | 5 + + 16 files changed, 658 insertions(+), 5 deletions(-) + create mode 100644 contrib/kcm_default_ccache + create mode 100644 src/responder/kcm/kcm.c + create mode 100644 src/responder/kcm/kcm.h + create mode 100644 src/responder/kcm/kcmsrv_cmd.c + create mode 100644 src/responder/kcm/kcmsrv_pvt.h + create mode 100644 src/sysv/systemd/sssd-kcm.service.in + create mode 100644 src/sysv/systemd/sssd-kcm.socket.in + +diff --git a/Makefile.am b/Makefile.am +index 7516338bc6fd95045d20db8155a0c82fd7003358..4248536e90370c1aab59549a9c18408ef314e6d4 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -87,6 +87,7 @@ sudolibdir = @sudolibpath@ + polkitdir = @polkitdir@ + pamconfdir = $(sysconfdir)/pam.d + systemtap_tapdir = @tapset_dir@ ++krb5sysincludedir = $(sysconfdir)/krb5.conf.d + + if HAVE_SYSTEMD_UNIT + ifp_exec_cmd = $(sssdlibexecdir)/sssd_ifp --uid 0 --gid 0 --debug-to-files --dbus-activated +@@ -186,6 +187,11 @@ endif + if BUILD_SECRETS + sssdlibexec_PROGRAMS += sssd_secrets + endif ++if BUILD_KCM ++sssdlibexec_PROGRAMS += sssd_kcm ++dist_krb5sysinclude_DATA = contrib/kcm_default_ccache ++endif ++ + + if BUILD_PAC_RESPONDER + sssdlibexec_PROGRAMS += sssd_pac +@@ -703,6 +709,8 @@ dist_noinst_HEADERS = \ + src/responder/secrets/secsrv_private.h \ + src/responder/secrets/secsrv_local.h \ + src/responder/secrets/secsrv_proxy.h \ ++ src/responder/kcm/kcm.h \ ++ src/responder/kcm/kcmsrv_pvt.h \ + src/sbus/sbus_client.h \ + src/sbus/sssd_dbus.h \ + src/sbus/sssd_dbus_meta.h \ +@@ -1476,6 +1484,24 @@ sssd_secrets_LDADD = \ + $(NULL) + endif + ++if BUILD_KCM ++sssd_kcm_SOURCES = \ ++ src/responder/kcm/kcm.c \ ++ src/responder/kcm/kcmsrv_cmd.c \ ++ src/util/sss_sockets.c \ ++ $(SSSD_RESPONDER_OBJ) \ ++ $(NULL) ++sssd_kcm_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(KRB5_CFLAGS) \ ++ $(NULL) ++sssd_kcm_LDADD = \ ++ $(KRB5_LIBS) \ ++ $(SSSD_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ $(NULL) ++endif ++ + sssd_be_SOURCES = \ + src/providers/data_provider_be.c \ + src/providers/data_provider_req.c \ +@@ -4259,6 +4285,12 @@ if BUILD_SUDO + src/sysv/systemd/sssd-sudo.service \ + $(NULL) + endif ++if BUILD_KCM ++ systemdunit_DATA += \ ++ src/sysv/systemd/sssd-kcm.socket \ ++ src/sysv/systemd/sssd-kcm.service \ ++ $(NULL) ++endif + if WITH_JOURNALD + systemdconf_DATA += \ + src/sysv/systemd/journal.conf +@@ -4350,6 +4382,12 @@ EXTRA_DIST += \ + src/sysv/systemd/sssd-sudo.service.in \ + $(NULL) + endif ++if BUILD_KCM ++EXTRA_DIST += \ ++ src/sysv/systemd/sssd-kcm.socket.in \ ++ src/sysv/systemd/sssd-kcm.service.in \ ++ $(NULL) ++endif + + src/sysv/systemd/sssd.service: src/sysv/systemd/sssd.service.in Makefile + @$(MKDIR_P) src/sysv/systemd/ +@@ -4433,6 +4471,16 @@ src/sysv/systemd/sssd-sudo.service: src/sysv/systemd/sssd-sudo.service.in Makefi + $(replace_script) + endif + ++if BUILD_KCM ++src/sysv/systemd/sssd-kcm.socket: src/sysv/systemd/sssd-kcm.socket.in Makefile ++ @$(MKDIR_P) src/sysv/systemd/ ++ $(replace_script) ++ ++src/sysv/systemd/sssd-kcm.service: src/sysv/systemd/sssd-kcm.service.in Makefile ++ @$(MKDIR_P) src/sysv/systemd/ ++ $(replace_script) ++endif ++ + SSSD_USER_DIRS = \ + $(DESTDIR)$(dbpath) \ + $(DESTDIR)$(keytabdir) \ +@@ -4596,6 +4644,9 @@ install-data-hook: + if BUILD_SAMBA + mv $(DESTDIR)/$(winbindplugindir)/winbind_idmap_sss.so $(DESTDIR)/$(winbindplugindir)/sss.so + endif ++if BUILD_KCM ++ $(MKDIR_P) $(DESTDIR)/$(krb5sysincludedir) ++endif + + uninstall-hook: + if [ -f $(abs_builddir)/src/config/.files2 ]; then \ +@@ -4670,6 +4721,8 @@ endif + rm -f $(builddir)/src/sysv/systemd/sssd-sudo.service + rm -f $(builddir)/src/sysv/systemd/sssd-secrets.socket + rm -f $(builddir)/src/sysv/systemd/sssd-secrets.service ++ rm -f $(builddir)/src/sysv/systemd/sssd-kcm.socket ++ rm -f $(builddir)/src/sysv/systemd/sssd-kcm.service + rm -f $(builddir)/src/sysv/systemd/journal.conf + + CLEANFILES += *.X */*.X */*/*.X +diff --git a/configure.ac b/configure.ac +index dd1012015a5fea9f25e5b5199b4868fbc0bc14c4..c363d48a806cc1998e85779a92b6b59b0e2a5c9c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -155,6 +155,7 @@ WITH_SSSD_USER + SSSD_RUNSTATEDIR + WITH_SECRETS + WITH_SECRETS_DB_PATH ++WITH_KCM + + m4_include([src/external/pkg.m4]) + m4_include([src/external/libpopt.m4]) +@@ -193,13 +194,20 @@ m4_include([src/external/libresolv.m4]) + m4_include([src/external/intgcheck.m4]) + m4_include([src/external/systemtap.m4]) + m4_include([src/external/service.m4]) +-m4_include([src/external/libcurl.m4]) + + if test x$with_secrets = xyes; then + m4_include([src/external/libhttp_parser.m4]) + m4_include([src/external/libjansson.m4]) + fi + ++if test x$with_kcm = xyes; then ++ m4_include([src/external/libcurl.m4]) ++fi ++# This variable is defined by external/libcurl.m4, but conditionals ++# must be always evaluated ++AM_CONDITIONAL([BUILD_WITH_LIBCURL], ++ [test x"$have_curlopt_unix_sockpath" = xyes]) ++ + WITH_UNICODE_LIB + if test x$unicode_lib = xlibunistring; then + m4_include([src/external/libunistring.m4]) +diff --git a/contrib/kcm_default_ccache b/contrib/kcm_default_ccache +new file mode 100644 +index 0000000000000000000000000000000000000000..ac88fca86b60b19f772912b5d9d14595a96d101d +--- /dev/null ++++ b/contrib/kcm_default_ccache +@@ -0,0 +1,12 @@ ++# This file should normally be installed by your distribution into a ++# directory that is included from the Kerberos configuration file (/etc/krb5.conf) ++# On Fedora/RHEL/CentOS, this is /etc/krb5.conf.d/ ++# ++# To enable the KCM credential cache, uncomment the following lines and ++# enable the KCM socket and the service: ++# systemctl enable sssd-kcm.socket ++# systemctl start sssd-kcm.socket ++# systemctl enable sssd-kcm.service ++ ++#[libdefaults] ++# default_ccache_name = KCM: +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 28ebe07a26a3112210b092b7831e7f6aae061c8d..5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -112,6 +112,13 @@ + %global enable_systemtap_opt --enable-systemtap + %endif + ++%if (0%{?fedora} >= 23 || 0%{?rhel} >= 7) ++ %global with_kcm 1 ++ %global with_kcm_option --with-kcm ++%else ++ %global with_kcm_option --without-kcm ++%endif ++ + Name: @PACKAGE_NAME@ + Version: @PACKAGE_VERSION@ + Release: 0@PRERELEASE_VERSION@%{?dist} +@@ -677,6 +684,18 @@ Requires: libsss_certmap = %{version}-%{release} + %description -n libsss_certmap-devel + Library to map certificates to users based on rules + ++%if (0%{?with_kcm} == 1) ++%package kcm ++Summary: An implementation of a Kerberos KCM server ++Group: Applications/System ++License: GPLv3+ ++Requires: sssd-common = %{version}-%{release} ++ ++%description kcm ++An implementation of a Kerberos KCM server. Use this package if you want to ++use the KCM: Kerberos credentials cache. ++%endif ++ + %prep + %setup -q -n %{name}-%{version} + +@@ -706,6 +725,7 @@ autoreconf -ivf + %{?with_python3_option} \ + %{?enable_polkit_rules_option} \ + %{?enable_systemtap_opt} \ ++ %{?with_kcm_option} \ + %{?experimental} + + make %{?_smp_mflags} all +@@ -1178,6 +1198,15 @@ done + %{_libdir}/libsss_certmap.so + %{_libdir}/pkgconfig/sss_certmap.pc + ++%if (0%{?with_kcm} == 1) ++%files kcm ++%{_libexecdir}/%{servicename}/sssd_kcm ++%dir %{_sysconfdir}/krb5.conf.d ++%config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache ++%{_unitdir}/sssd-kcm.socket ++%{_unitdir}/sssd-kcm.service ++%endif ++ + %pre common + getent group sssd >/dev/null || groupadd -r sssd + getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd +@@ -1274,6 +1303,18 @@ fi + + %postun -n libsss_simpleifp -p /sbin/ldconfig + ++%if (0%{?with_kcm} == 1) ++%post kcm ++%systemd_post sssd-kcm.socket ++ ++%preun kcm ++%systemd_preun sssd-kcm.socket ++ ++%postun kcm ++%systemd_postun_with_restart sssd-kcm.socket ++%systemd_postun_with_restart sssd-kcm.service ++%endif ++ + %changelog + * Mon Mar 15 2010 Stephen Gallagher - @PACKAGE_VERSION@-0@PRERELEASE_VERSION@ + - Automated build of the SSSD +diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 +index 749e7694f4dd7086468e461194ef274be2094236..420997229cb3c244afd8fb21b074e43a21de0eda 100644 +--- a/src/conf_macros.m4 ++++ b/src/conf_macros.m4 +@@ -887,6 +887,22 @@ AC_DEFUN([WITH_SECRETS], + AM_CONDITIONAL([BUILD_SECRETS], [test x"$with_secrets" = xyes]) + ]) + ++AC_DEFUN([WITH_KCM], ++ [ AC_ARG_WITH([kcm], ++ [AC_HELP_STRING([--with-kcm], ++ [Whether to build with KCM server support [yes]] ++ ) ++ ], ++ [with_kcm=$withval], ++ with_kcm=yes ++ ) ++ ++ if test x"$with_kcm" = xyes; then ++ AC_DEFINE(BUILD_KCM, 1, [whether to build with KCM server support]) ++ fi ++ AM_CONDITIONAL([BUILD_KCM], [test x"$with_kcm" = xyes]) ++ ]) ++ + AC_DEFUN([WITH_SECRETS_DB_PATH], + [ AC_ARG_WITH([secrets-db-path], + [AC_HELP_STRING([--with-secrets-db-path=PATH], +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index c05b1cee45ece748bf8e2b1e1ecf3dc28979e48b..c443e869a7a6782265b42c4ad122867c4e3dd8e0 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -231,6 +231,9 @@ + #define CONFDB_SEC_MAX_SECRETS "max_secrets" + #define CONFDB_SEC_MAX_PAYLOAD_SIZE "max_payload_size" + ++/* KCM Service */ ++#define CONFDB_KCM_CONF_ENTRY "config/kcm" ++#define CONFDB_KCM_SOCKET "socket_path" + + struct confdb_ctx; + struct config_file_ctx; +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index c287328828cae2f0ad8a5a105f1c2b3e05353021..5e789c51658c51c0af1338d23d6c0f30f40bf119 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -9,6 +9,7 @@ section = ssh + section = pac + section = ifp + section = secrets ++section = kcm + section_re = ^secrets/users/[0-9]\+$ + section_re = ^domain/.*$ + +@@ -262,6 +263,24 @@ option = forward_headers + option = username + option = password + ++# KCM responder ++[rule/allowed_kcm_options] ++validator = ini_allowed_options ++section_re = ^kcm$ ++ ++option = timeout ++option = debug ++option = debug_level ++option = debug_timestamps ++option = debug_microseconds ++option = debug_to_files ++option = command ++option = reconnection_retries ++option = fd_limit ++option = client_idle_timeout ++option = description ++option = socket_path ++ + [rule/allowed_domain_options] + validator = ini_allowed_options + section_re = ^domain/.*$ +diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4 +index 3bc303ca4e1dea8a04117e32b8c4466b80d885b1..b420b04ad806bd1251f086b773ffe480d39f8bd3 100644 +--- a/src/external/libcurl.m4 ++++ b/src/external/libcurl.m4 +@@ -9,8 +9,8 @@ AS_IF([test x$enable_libcurl = xyes], + [PKG_CHECK_MODULES([CURL], + [libcurl], + [found_libcurl=yes], +- [AC_MSG_WARN([ +-The libcurl development library was not found. Some features will be disabled.]) ++ [AC_MSG_ERROR([ ++The libcurl development library was not found.]) + ])]) + + AS_IF([test x"$found_libcurl" = xyes], +@@ -32,7 +32,5 @@ AS_IF([test x"$found_libcurl" = xyes], + AC_SUBST(CURL_LIBS) + AC_SUBST(CURL_CFLAGS) + +-AM_CONDITIONAL([BUILD_WITH_LIBCURL], +- [test x"$have_curlopt_unix_sockpath" = xyes]) + AM_COND_IF([BUILD_WITH_LIBCURL], + [AC_DEFINE_UNQUOTED(HAVE_LIBCURL, 1, [Build with libcurl support])]) +diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c +new file mode 100644 +index 0000000000000000000000000000000000000000..90a6999c5e39d48a1a2ea8168d171612a65077d5 +--- /dev/null ++++ b/src/responder/kcm/kcm.c +@@ -0,0 +1,254 @@ ++/* ++ SSSD ++ ++ KCM Server - the mainloop and server setup ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "responder/kcm/kcm.h" ++#include "responder/kcm/kcmsrv_pvt.h" ++#include "responder/common/responder.h" ++#include "util/util.h" ++ ++#define DEFAULT_KCM_FD_LIMIT 2048 ++ ++#ifndef SSS_KCM_SOCKET_NAME ++#define SSS_KCM_SOCKET_NAME DEFAULT_KCM_SOCKET_PATH ++#endif ++ ++static int kcm_responder_ctx_destructor(void *ptr) ++{ ++ struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx); ++ ++ /* mark that we are shutting down the responder, so it is propagated ++ * into underlying contexts that are freed right before rctx */ ++ DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n"); ++ rctx->shutting_down = true; ++ ++ return 0; ++} ++ ++static int kcm_get_config(struct kcm_ctx *kctx) ++{ ++ int ret; ++ char *sock_name; ++ ++ ret = confdb_get_int(kctx->rctx->cdb, ++ CONFDB_KCM_CONF_ENTRY, ++ CONFDB_SERVICE_FD_LIMIT, ++ DEFAULT_KCM_FD_LIMIT, ++ &kctx->fd_limit); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Failed to get file descriptors limit\n"); ++ goto done; ++ } ++ ++ ret = confdb_get_int(kctx->rctx->cdb, ++ kctx->rctx->confdb_service_path, ++ CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, ++ CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT, ++ &kctx->rctx->client_idle_timeout); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get the client idle timeout [%d]: %s\n", ++ ret, strerror(ret)); ++ goto done; ++ } ++ ++ /* Ensure that the client timeout is at least ten seconds */ ++ if (kctx->rctx->client_idle_timeout < 10) { ++ kctx->rctx->client_idle_timeout = 10; ++ } ++ ++ ret = confdb_get_string(kctx->rctx->cdb, ++ kctx->rctx, ++ kctx->rctx->confdb_service_path, ++ CONFDB_KCM_SOCKET, ++ SSS_KCM_SOCKET_NAME, ++ &sock_name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get the client idle timeout [%d]: %s\n", ++ ret, strerror(ret)); ++ goto done; ++ } ++ kctx->rctx->sock_name = sock_name; ++ ++ ret = EOK; ++ ++done: ++ return ret; ++} ++ ++static int kcm_data_destructor(void *ptr) ++{ ++ struct kcm_resp_ctx *kcm_data = talloc_get_type(ptr, struct kcm_resp_ctx); ++ ++ if (kcm_data != NULL) { ++ krb5_free_context(kcm_data->k5c); ++ } ++ return 0; ++} ++ ++static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx) ++{ ++ struct kcm_resp_ctx *kcm_data; ++ krb5_error_code kret; ++ ++ kcm_data = talloc_zero(mem_ctx, struct kcm_resp_ctx); ++ if (kcm_data == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm data\n"); ++ return NULL; ++ } ++ ++ kret = krb5_init_context(&kcm_data->k5c); ++ if (kret != EOK) { ++ talloc_free(kcm_data); ++ return NULL; ++ } ++ talloc_set_destructor((TALLOC_CTX*)kcm_data, kcm_data_destructor); ++ ++ return kcm_data; ++} ++ ++static int kcm_process_init(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct confdb_ctx *cdb) ++{ ++ struct resp_ctx *rctx; ++ struct kcm_ctx *kctx; ++ int ret; ++ ++ rctx = talloc_zero(mem_ctx, struct resp_ctx); ++ if (rctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n"); ++ return ENOMEM; ++ } ++ rctx->ev = ev; ++ rctx->cdb = cdb; ++ rctx->confdb_service_path = CONFDB_KCM_CONF_ENTRY; ++ rctx->shutting_down = false; ++ rctx->lfd = -1; ++ rctx->priv_lfd = -1; ++ ++ talloc_set_destructor((TALLOC_CTX*)rctx, kcm_responder_ctx_destructor); ++ ++ kctx = talloc_zero(rctx, struct kcm_ctx); ++ if (kctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm_ctx\n"); ++ ret = ENOMEM; ++ goto fail; ++ } ++ ++ kctx->rctx = rctx; ++ kctx->rctx->pvt_ctx = kctx; ++ ++ ret = kcm_get_config(kctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM config\n"); ++ goto fail; ++ } ++ ++ kctx->kcm_data = kcm_data_setup(kctx); ++ if (kctx->kcm_data == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "fatal error initializing responder data\n"); ++ ret = EIO; ++ goto fail; ++ } ++ ++ /* Set up file descriptor limits */ ++ responder_set_fd_limit(kctx->fd_limit); ++ ++ ret = activate_unix_sockets(rctx, kcm_connection_setup); ++ if (ret != EOK) goto fail; ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "KCM Initialization complete\n"); ++ ++ return EOK; ++ ++fail: ++ talloc_free(rctx); ++ return ret; ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ int opt; ++ poptContext pc; ++ struct main_context *main_ctx; ++ int ret; ++ uid_t uid; ++ gid_t gid; ++ ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_MAIN_OPTS ++ SSSD_SERVER_OPTS(uid, gid) ++ POPT_TABLEEND ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ umask(DFL_RSP_UMASK); ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ ++ poptFreeContext(pc); ++ ++ DEBUG_INIT(debug_level); ++ ++ /* set up things like debug, signals, daemonization, etc... */ ++ debug_log_file = "sssd_kcm"; ++ ++ ret = server_setup("sssd[kcm]", 0, uid, gid, CONFDB_KCM_CONF_ENTRY, ++ &main_ctx); ++ if (ret != EOK) return 2; ++ ++ ret = die_if_parent_died(); ++ if (ret != EOK) { ++ /* This is not fatal, don't return */ ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Could not set up to exit when parent process does\n"); ++ } ++ ++ ret = kcm_process_init(main_ctx, ++ main_ctx->event_ctx, ++ main_ctx->confdb_ctx); ++ if (ret != EOK) return 3; ++ ++ /* loop on main */ ++ server_loop(main_ctx); ++ ++ return 0; ++} +diff --git a/src/responder/kcm/kcm.h b/src/responder/kcm/kcm.h +new file mode 100644 +index 0000000000000000000000000000000000000000..1ea7e9bbca754dca2eeb72a08830fa2f95713b4f +--- /dev/null ++++ b/src/responder/kcm/kcm.h +@@ -0,0 +1,97 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* include/kcm.h - Kerberos cache manager protocol declarations */ ++/* ++ * Copyright (C) 2014 by the Massachusetts Institute of Technology. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ++ * OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef KCM_H ++#define KCM_H ++ ++#define KCM_PROTOCOL_VERSION_MAJOR 2 ++#define KCM_PROTOCOL_VERSION_MINOR 0 ++ ++#define KCM_UUID_LEN 16 ++ ++/* This should ideally be in RUNSTATEDIR, but Heimdal uses a hardcoded ++ * /var/run, and we need to use the same default path. */ ++#define DEFAULT_KCM_SOCKET_PATH "/var/run/.heim_org.h5l.kcm-socket" ++#define DEFAULT_KCM_MACH_SERVICE "org.h5l.kcm" ++ ++/* ++ * All requests begin with: ++ * major version (1 bytes) ++ * minor version (1 bytes) ++ * opcode (16-bit big-endian) ++ * ++ * All replies begin with a 32-bit big-endian reply code. ++ * ++ * Parameters are appended to the request or reply with no delimiters. Flags ++ * and time offsets are stored as 32-bit big-endian integers. Names are ++ * marshalled as zero-terminated strings. Principals and credentials are ++ * marshalled in the v4 FILE ccache format. UUIDs are 16 bytes. UUID lists ++ * are not delimited, so nothing can come after them. ++ */ ++ ++/* Opcodes without comments are currently unused in the MIT client ++ * implementation. */ ++typedef enum kcm_opcode { ++ KCM_OP_NOOP, ++ KCM_OP_GET_NAME, ++ KCM_OP_RESOLVE, ++ KCM_OP_GEN_NEW, /* 0x3 () -> (name) */ ++ KCM_OP_INITIALIZE, /* 0x4 (name, princ) -> () */ ++ KCM_OP_DESTROY, /* 0x4 (name) -> () */ ++ KCM_OP_STORE, /* 0x6 (name, cred) -> () */ ++ KCM_OP_RETRIEVE, ++ KCM_OP_GET_PRINCIPAL, /* 0x8 (name) -> (princ) */ ++ KCM_OP_GET_CRED_UUID_LIST, /* 0x9 (name) -> (uuid, ...) */ ++ KCM_OP_GET_CRED_BY_UUID, /* 0xa (name, uuid) -> (cred) */ ++ KCM_OP_REMOVE_CRED, /* (name, flags, credtag) -> () */ ++ KCM_OP_SET_FLAGS, ++ KCM_OP_CHOWN, ++ KCM_OP_CHMOD, ++ KCM_OP_GET_INITIAL_TICKET, ++ KCM_OP_GET_TICKET, ++ KCM_OP_MOVE_CACHE, ++ KCM_OP_GET_CACHE_UUID_LIST, /* 0x12 () -> (uuid, ...) */ ++ KCM_OP_GET_CACHE_BY_UUID, /* 0x13 (uuid) -> (name) */ ++ KCM_OP_GET_DEFAULT_CACHE, /* 0x14 () -> (name) */ ++ KCM_OP_SET_DEFAULT_CACHE, /* 0x15 (name) -> () */ ++ KCM_OP_GET_KDC_OFFSET, /* 0x16 (name) -> (offset) */ ++ KCM_OP_SET_KDC_OFFSET, /* 0x17 (name, offset) -> () */ ++ KCM_OP_ADD_NTLM_CRED, ++ KCM_OP_HAVE_NTLM_CRED, ++ KCM_OP_DEL_NTLM_CRED, ++ KCM_OP_DO_NTLM_AUTH, ++ KCM_OP_GET_NTLM_USER_LIST, ++ ++ KCM_OP_SENTINEL, /* SSSD addition, not in the MIT header */ ++} kcm_opcode; ++ ++#endif /* KCM_H */ +diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c +new file mode 100644 +index 0000000000000000000000000000000000000000..e9a03cbd41169c93e00b0630dc1e05e205881ec9 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_cmd.c +@@ -0,0 +1,65 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM server request and reply parsing and dispatching ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++#include "util/util.h" ++#include "responder/common/responder.h" ++ ++struct kcm_proto_ctx { ++ void *unused; ++}; ++ ++static void kcm_fd_handler(struct tevent_context *ev, ++ struct tevent_fd *fde, ++ uint16_t flags, void *ptr) ++{ ++ errno_t ret; ++ struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx); ++ ++ /* Always reset the idle timer on any activity */ ++ ret = reset_client_idle_timer(cctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Could not create idle timer for client. " ++ "This connection may not auto-terminate\n"); ++ /* Non-fatal, continue */ ++ } ++} ++ ++int kcm_connection_setup(struct cli_ctx *cctx) ++{ ++ struct kcm_proto_ctx *protocol_ctx; ++ ++ protocol_ctx = talloc_zero(cctx, struct kcm_proto_ctx); ++ if (protocol_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ cctx->protocol_ctx = protocol_ctx; ++ cctx->cfd_handler = kcm_fd_handler; ++ return EOK; ++} ++ ++/* Dummy, not used here but required to link to other responder files */ ++struct cli_protocol_version *register_cli_protocol_version(void) ++{ ++ return NULL; ++} +diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h +new file mode 100644 +index 0000000000000000000000000000000000000000..a7c9d062c17f09986d894064176c3a461d396ac0 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_pvt.h +@@ -0,0 +1,58 @@ ++/* ++ SSSD ++ ++ KCM Server - private header file ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef __KCMSRV_PVT_H__ ++#define __KCMSRV_PVT_H__ ++ ++#include "config.h" ++ ++#include ++#include "responder/common/responder.h" ++ ++/* KCM IO structure */ ++struct kcm_data { ++ uint8_t *data; ++ size_t length; ++}; ++ ++/* To avoid leaking the sssd-specific responder data to other ++ * modules, the ccache databases and other KCM specific data ++ * are kept separately ++ */ ++struct kcm_resp_ctx { ++ krb5_context k5c; ++}; ++ ++/* responder context that contains both the responder data, ++ * like the ccaches and the sssd-specific stuff like the ++ * generic responder ctx ++ */ ++struct kcm_ctx { ++ struct resp_ctx *rctx; ++ int fd_limit; ++ char *socket_path; ++ ++ struct kcm_resp_ctx *kcm_data; ++}; ++ ++int kcm_connection_setup(struct cli_ctx *cctx); ++ ++#endif /* __KCMSRV_PVT_H__ */ +diff --git a/src/sysv/systemd/sssd-kcm.service.in b/src/sysv/systemd/sssd-kcm.service.in +new file mode 100644 +index 0000000000000000000000000000000000000000..1e2bee12dc3bedd17d41b86f91c9b2b52d985c40 +--- /dev/null ++++ b/src/sysv/systemd/sssd-kcm.service.in +@@ -0,0 +1,9 @@ ++[Unit] ++Description=SSSD Kerberos Cache Manager ++Documentation=man:sssd-kcm(5) ++ ++[Install] ++Also=sssd-kcm.socket ++ ++[Service] ++ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 --debug-to-files +diff --git a/src/sysv/systemd/sssd-kcm.socket.in b/src/sysv/systemd/sssd-kcm.socket.in +new file mode 100644 +index 0000000000000000000000000000000000000000..80ec1c0c8f190e83de0b603df8e90aa49d2ec181 +--- /dev/null ++++ b/src/sysv/systemd/sssd-kcm.socket.in +@@ -0,0 +1,10 @@ ++[Unit] ++Description=SSSD Secrets Service responder socket ++Documentation=man:sssd-kcm(8) ++Requires=sssd-secrets.socket ++ ++[Socket] ++ListenStream=@localstatedir@/run/.heim_org.h5l.kcm-socket ++ ++[Install] ++WantedBy=sockets.target +diff --git a/src/util/util_errors.c b/src/util/util_errors.c +index 17388c997db5315c2491af1021e75aff07632488..23cfdf9c6116a2c8e569a041e8289b65a112fd08 100644 +--- a/src/util/util_errors.c ++++ b/src/util/util_errors.c +@@ -40,6 +40,7 @@ struct err_string error_to_str[] = { + { "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 */ ++ { "No matching credentials found" }, /* ERR_NO_MATCHING_CREDS */ + { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */ + { "Authentication Denied" }, /* ERR_AUTH_DENIED */ + { "Authentication Failed" }, /* ERR_AUTH_FAILED */ +@@ -104,6 +105,10 @@ struct err_string error_to_str[] = { + { "The secret payload size is too large" }, /* ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE */ + { "No authentication methode available" }, /* ERR_NO_AUTH_METHOD_AVAILABLE */ + { "Smartcard authentication not supported" }, /* ERR_SC_AUTH_NOT_SUPPORTED */ ++ { "Malformed input KCM packet" }, /* ERR_KCM_MALFORMED_IN_PKT */ ++ { "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */ ++ { "End of credential cache reached" }, /* ERR_KCM_CC_END */ ++ { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */ + { "ERR_LAST" } /* ERR_LAST */ + }; + +diff --git a/src/util/util_errors.h b/src/util/util_errors.h +index 7aacad26084a3a2af6333988f07db865f6a4d299..387d481616db1ed5e22b73fae82632a582fae946 100644 +--- a/src/util/util_errors.h ++++ b/src/util/util_errors.h +@@ -62,6 +62,7 @@ enum sssd_errors { + ERR_CREDS_EXPIRED_CCACHE, + ERR_CREDS_INVALID, + ERR_NO_CACHED_CREDS, ++ ERR_NO_MATCHING_CREDS, + ERR_CACHED_CREDS_EXPIRED, + ERR_AUTH_DENIED, + ERR_AUTH_FAILED, +@@ -126,6 +127,10 @@ enum sssd_errors { + ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE, + ERR_NO_AUTH_METHOD_AVAILABLE, + ERR_SC_AUTH_NOT_SUPPORTED, ++ ERR_KCM_MALFORMED_IN_PKT, ++ ERR_KCM_OP_NOT_IMPLEMENTED, ++ ERR_KCM_CC_END, ++ ERR_KCM_WRONG_CCNAME_FORMAT, + ERR_LAST /* ALWAYS LAST */ + }; + +-- +2.9.3 + diff --git a/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch b/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch new file mode 100644 index 0000000..5e59dc2 --- /dev/null +++ b/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch @@ -0,0 +1,578 @@ +From aface2604c53db717299ac3dfe798dfd0c540f99 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 23 Sep 2016 14:00:10 +0200 +Subject: [PATCH 23/36] KCM: request parsing and sending a reply +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implements parsing the KCM client request into per-client buffers and +sending a response for both the failure case and for success. + +The protocol is documented at: + http://k5wiki.kerberos.org/wiki/Projects/KCM_client + +Several places don't use the sss_iobuf structure, because they don't +parse variable-length data from the buffer and it's much more efficient +to just allocate the needed request and reply structure on the stack. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/responder/kcm/kcmsrv_cmd.c | 467 ++++++++++++++++++++++++++++++++++++++++- + src/responder/kcm/kcmsrv_pvt.h | 21 +- + 2 files changed, 474 insertions(+), 14 deletions(-) + +diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c +index e9a03cbd41169c93e00b0630dc1e05e205881ec9..cbf70353730d8a4e03d8f75c97395f4ef007e77f 100644 +--- a/src/responder/kcm/kcmsrv_cmd.c ++++ b/src/responder/kcm/kcmsrv_cmd.c +@@ -19,14 +19,430 @@ + along with this program. If not, see . + */ + ++#include ++ + #include "config.h" + #include "util/util.h" ++#include "util/sss_iobuf.h" + #include "responder/common/responder.h" ++#include "responder/kcm/kcmsrv_pvt.h" ++#include "responder/kcm/kcm.h" + +-struct kcm_proto_ctx { +- void *unused; ++/* The first four bytes of a message is always the size */ ++#define KCM_MSG_LEN_SIZE 4 ++ ++/* The return code is 32bits */ ++#define KCM_RETCODE_SIZE 4 ++ ++/* The maximum length of a request or reply as defined by the RPC ++ * protocol. This is the same constant size as MIT KRB5 uses ++ */ ++#define KCM_PACKET_MAX_SIZE 2048 ++ ++/* KCM operation, its raw input and raw output and result */ ++struct kcm_op_io { ++ struct kcm_op *op; ++ struct kcm_data request; ++ struct sss_iobuf *reply; ++}; ++ ++/** ++ * KCM IO-vector operations ++ */ ++struct kcm_iovec { ++ /* We don't use iovec b/c void pointers don't allow for ++ * pointer arithmetics and it's convenient to keep track ++ * of processed bytes ++ */ ++ uint8_t *kiov_base; ++ size_t kiov_len; ++ size_t nprocessed; ++}; ++ ++static errno_t kcm_iovec_op(int fd, struct kcm_iovec *kiov, bool do_read) ++{ ++ ssize_t len; ++ struct iovec iov[1]; ++ ++ iov[0].iov_base = kiov->kiov_base + kiov->nprocessed; ++ iov[0].iov_len = kiov->kiov_len - kiov->nprocessed; ++ if (iov[0].iov_len == 0) { ++ /* This iovec is full (read) or depleted (write), proceed to the next one */ ++ return EOK; ++ } ++ ++ if (do_read) { ++ len = readv(fd, iov, 1); ++ } else { ++ len = writev(fd, iov, 1); ++ } ++ ++ if (len == -1) { ++ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { ++ return EAGAIN; ++ } else { ++ return errno; ++ } ++ } ++ ++ if (len == 0) { ++ /* Read event on fd that doesn't yield data? error */ ++ return ENODATA; ++ } ++ ++ /* Decrease the amount of available free space in the iovec */ ++ kiov->nprocessed += len; ++ return EOK; ++} ++ ++static errno_t kcm_read_iovec(int fd, struct kcm_iovec *kiov) ++{ ++ return kcm_iovec_op(fd, kiov, true); ++} ++ ++static errno_t kcm_write_iovec(int fd, struct kcm_iovec *kiov) ++{ ++ return kcm_iovec_op(fd, kiov, false); ++} ++ ++/** ++ * Parsing KCM input ++ * ++ * The request is received as two IO vectors: ++ * ++ * first iovec: ++ * length 32-bit big-endian integer ++ * ++ * second iovec: ++ * major protocol number 8-bit big-endian integer ++ * minor protocol number 8-bit big-endian integer ++ * opcode 16-bit big-endian integer ++ * message payload buffer ++ */ ++struct kcm_reqbuf { ++ uint8_t lenbuf[KCM_MSG_LEN_SIZE]; ++ struct kcm_iovec v_len; ++ ++ /* Includes the major, minor versions etc */ ++ uint8_t msgbuf[KCM_PACKET_MAX_SIZE]; ++ struct kcm_iovec v_msg; ++}; ++ ++static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, ++ struct kcm_op_io *op_io) ++{ ++ size_t lc = 0; ++ size_t mc = 0; ++ uint16_t opcode = 0; ++ uint16_t opcode_be = 0; ++ uint32_t len_be = 0; ++ uint32_t msglen; ++ uint8_t proto_maj = 0; ++ uint8_t proto_min = 0; ++ ++ /* The first 4 bytes before the payload is message length */ ++ SAFEALIGN_COPY_UINT32_CHECK(&len_be, ++ reqbuf->v_len.kiov_base, ++ reqbuf->v_len.kiov_len, ++ &lc); ++ msglen = be32toh(len_be); ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Received message with length %"PRIu32"\n", msglen); ++ ++ if (msglen == 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Illegal zero-length message\n"); ++ return EBADMSG; ++ } ++ ++ if (msglen != reqbuf->v_msg.nprocessed) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Sender claims the message is %"PRIu32" bytes, " ++ "but received %zu\n", ++ msglen, reqbuf->v_msg.nprocessed); ++ return EBADMSG; ++ } ++ ++ /* First 16 bits are 8 bit major and 8bit major protocol version */ ++ SAFEALIGN_COPY_UINT8_CHECK(&proto_maj, ++ reqbuf->v_msg.kiov_base + mc, ++ reqbuf->v_msg.kiov_len, ++ &mc); ++ SAFEALIGN_COPY_UINT8_CHECK(&proto_min, ++ reqbuf->v_msg.kiov_base + mc, ++ reqbuf->v_msg.kiov_len, ++ &mc); ++ ++ if (proto_maj != KCM_PROTOCOL_VERSION_MAJOR) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Expected major version %d, got %"PRIu16"\n", ++ KCM_PROTOCOL_VERSION_MAJOR, (uint16_t) proto_maj); ++ return ERR_KCM_MALFORMED_IN_PKT; ++ } ++ ++ if (proto_min != KCM_PROTOCOL_VERSION_MINOR) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Expected minor version %d, got %"PRIu16"\n", ++ KCM_PROTOCOL_VERSION_MINOR, (uint16_t) proto_maj); ++ return ERR_KCM_MALFORMED_IN_PKT; ++ } ++ ++ SAFEALIGN_COPY_UINT16_CHECK(&opcode_be, ++ reqbuf->v_msg.kiov_base + mc, ++ reqbuf->v_msg.kiov_len, ++ &mc); ++ ++ opcode = be16toh(opcode_be); ++ DEBUG(SSSDBG_TRACE_LIBS, "Received operation code %"PRIu16"\n", opcode); ++ ++ return EOK; ++} ++ ++/** ++ * Constructing a reply for failure and success ++ * ++ * The reply consists of three IO vectors: ++ * 1) length iovec: ++ * length: 32-bit big-endian ++ * ++ * 2) return code iovec: ++ * retcode: 32-bit big-endian. Non-zero on failure in the KCM server, ++ * zero if the KCM operation ran (even if the operation itself ++ * failed) ++ * ++ * 3) reply iovec ++ * message: buffer, first 32-bits of the buffer is the return code of ++ * the KCM operation, the rest depends on the operation itself. ++ * The buffer's length is specified by the first integer in the ++ * reply (very intuitive, right?) ++ * ++ * The client always reads the length and return code iovectors. However, the ++ * client reads the reply iovec only if retcode is 0 in the return code iovector ++ * (see kcmio_unix_socket_read() in the MIT tree) ++ */ ++struct kcm_repbuf { ++ uint8_t lenbuf[KCM_MSG_LEN_SIZE]; ++ struct kcm_iovec v_len; ++ ++ uint8_t rcbuf[KCM_RETCODE_SIZE]; ++ struct kcm_iovec v_rc; ++ ++ uint8_t msgbuf[KCM_PACKET_MAX_SIZE]; ++ struct kcm_iovec v_msg; ++}; ++ ++static errno_t kcm_failbuf_construct(errno_t ret, ++ struct kcm_repbuf *repbuf) ++{ ++ size_t c; ++ ++ c = 0; ++ SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, 0, &c); ++ c = 0; ++ SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c); ++ ++ return EOK; ++} ++ ++/** ++ * Construct a reply buffer and send it to the KCM client ++ */ ++static void kcm_reply_error(struct cli_ctx *cctx, ++ errno_t retcode, ++ struct kcm_repbuf *repbuf) ++{ ++ errno_t ret; ++ krb5_error_code kerr; ++ ++ DEBUG(SSSDBG_OP_FAILURE, ++ "KCM operation returs failure [%d]: %s\n", ++ retcode, sss_strerror(retcode)); ++ kerr = sss2krb5_error(retcode); ++ ++ ret = kcm_failbuf_construct(kerr, repbuf); ++ if (ret != EOK) { ++ /* If we can't construct the reply buffer, just terminate the client */ ++ talloc_free(cctx); ++ return; ++ } ++ ++ TEVENT_FD_WRITEABLE(cctx->cfde); ++} ++ ++/** ++ * Request-reply dispatcher ++ */ ++struct kcm_req_ctx { ++ /* client context owns per-client buffers including this one */ ++ struct cli_ctx *cctx; ++ ++ /* raw IO buffers */ ++ struct kcm_reqbuf reqbuf; ++ struct kcm_repbuf repbuf; ++ ++ /* long-lived responder structures */ ++ struct kcm_ctx *kctx; ++ ++ struct kcm_op_io op_io; + }; + ++static errno_t kcm_recv_data(int fd, struct kcm_reqbuf *reqbuf) ++{ ++ errno_t ret; ++ ++ ret = kcm_read_iovec(fd, &reqbuf->v_len); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = kcm_read_iovec(fd, &reqbuf->v_msg); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ return EOK; ++} ++ ++static struct kcm_req_ctx *kcm_new_req(TALLOC_CTX *mem_ctx, ++ struct cli_ctx *cctx, ++ struct kcm_ctx *kctx) ++{ ++ struct kcm_req_ctx *req; ++ ++ req = talloc_zero(cctx, struct kcm_req_ctx); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ req->reqbuf.v_len.kiov_base = req->reqbuf.lenbuf; ++ req->reqbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE; ++ ++ req->reqbuf.v_msg.kiov_base = req->reqbuf.msgbuf; ++ req->reqbuf.v_msg.kiov_len = KCM_PACKET_MAX_SIZE; ++ ++ req->repbuf.v_len.kiov_base = req->repbuf.lenbuf; ++ req->repbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE; ++ ++ req->repbuf.v_rc.kiov_base = req->repbuf.rcbuf; ++ req->repbuf.v_rc.kiov_len = KCM_RETCODE_SIZE; ++ ++ req->repbuf.v_msg.kiov_base = req->repbuf.msgbuf; ++ /* Length of the msg iobuf will be adjusted later, so far use the full ++ * length so that constructing the reply can use that capacity ++ */ ++ req->repbuf.v_msg.kiov_len = KCM_PACKET_MAX_SIZE; ++ ++ req->cctx = cctx; ++ req->kctx = kctx; ++ ++ return req; ++} ++ ++static void kcm_recv(struct cli_ctx *cctx) ++{ ++ struct kcm_req_ctx *req; ++ struct kcm_ctx *kctx; ++ int ret; ++ ++ kctx = talloc_get_type(cctx->rctx->pvt_ctx, struct kcm_ctx); ++ req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx); ++ if (req == NULL) { ++ /* A new request comes in, setup data structures */ ++ req = kcm_new_req(cctx, cctx, kctx); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot set up client connection\n"); ++ talloc_free(cctx); ++ return; ++ } ++ ++ cctx->state_ctx = req; ++ } ++ ++ ret = kcm_recv_data(cctx->cfd, &req->reqbuf); ++ switch (ret) { ++ case ENODATA: ++ DEBUG(SSSDBG_TRACE_ALL, "Client closed connection.\n"); ++ talloc_free(cctx); ++ return; ++ case EAGAIN: ++ DEBUG(SSSDBG_TRACE_ALL, "Retry later\n"); ++ return; ++ case EOK: ++ /* all fine */ ++ break; ++ default: ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Failed to receive data (%d, %s), aborting client\n", ++ ret, sss_strerror(ret)); ++ talloc_free(cctx); ++ return; ++ } ++ ++ ret = kcm_input_parse(&req->reqbuf, &req->op_io); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Failed to parse data (%d, %s), aborting client\n", ++ ret, sss_strerror(ret)); ++ goto fail; ++ } ++ ++ /* do not read anymore, client is done sending */ ++ TEVENT_FD_NOT_READABLE(cctx->cfde); ++ ++ kcm_reply_error(cctx, ret, &req->repbuf); ++ return; ++ ++fail: ++ /* Fail with reply */ ++ kcm_reply_error(cctx, ret, &req->repbuf); ++} ++ ++static int kcm_send_data(struct cli_ctx *cctx) ++{ ++ struct kcm_req_ctx *req; ++ errno_t ret; ++ ++ req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx); ++ ++ ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ return EOK; ++} ++ ++static void kcm_send(struct cli_ctx *cctx) ++{ ++ errno_t ret; ++ ++ ret = kcm_send_data(cctx); ++ if (ret == EAGAIN) { ++ /* not all data was sent, loop again */ ++ return; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n"); ++ talloc_free(cctx); ++ return; ++ } ++ ++ /* ok all sent */ ++ TEVENT_FD_NOT_WRITEABLE(cctx->cfde); ++ TEVENT_FD_READABLE(cctx->cfde); ++ talloc_zfree(cctx->state_ctx); ++ return; ++} ++ + static void kcm_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *ptr) +@@ -39,25 +455,54 @@ static void kcm_fd_handler(struct tevent_context *ev, + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not create idle timer for client. " +- "This connection may not auto-terminate\n"); ++ "This connection may not auto-terminate\n"); + /* Non-fatal, continue */ + } ++ ++ if (flags & TEVENT_FD_READ) { ++ kcm_recv(cctx); ++ return; ++ } ++ if (flags & TEVENT_FD_WRITE) { ++ kcm_send(cctx); ++ return; ++ } + } + + int kcm_connection_setup(struct cli_ctx *cctx) + { +- struct kcm_proto_ctx *protocol_ctx; +- +- protocol_ctx = talloc_zero(cctx, struct kcm_proto_ctx); +- if (protocol_ctx == NULL) { +- return ENOMEM; +- } +- +- cctx->protocol_ctx = protocol_ctx; + cctx->cfd_handler = kcm_fd_handler; + return EOK; + } + ++krb5_error_code sss2krb5_error(errno_t err) ++{ ++ switch (err) { ++ case EOK: ++ return 0; ++ case ENOMEM: ++ return KRB5_CC_NOMEM; ++ case EACCES: ++ return KRB5_FCC_PERM; ++ case ERR_KCM_OP_NOT_IMPLEMENTED: ++ return KRB5_CC_NOSUPP; ++ case ERR_WRONG_NAME_FORMAT: ++ return KRB5_CC_BADNAME; ++ case ERR_NO_MATCHING_CREDS: ++ return KRB5_FCC_NOFILE; ++ case ERR_NO_CREDS: ++ return KRB5_CC_NOTFOUND; ++ case ERR_KCM_CC_END: ++ return KRB5_CC_END; ++ case ERR_KCM_MALFORMED_IN_PKT: ++ case EINVAL: ++ case EIO: ++ return KRB5_CC_IO; ++ } ++ ++ return KRB5_FCC_INTERNAL; ++} ++ + /* Dummy, not used here but required to link to other responder files */ + struct cli_protocol_version *register_cli_protocol_version(void) + { +diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h +index a7c9d062c17f09986d894064176c3a461d396ac0..fd1fd9fa32d59a323d465def68999f24f84e3923 100644 +--- a/src/responder/kcm/kcmsrv_pvt.h ++++ b/src/responder/kcm/kcmsrv_pvt.h +@@ -27,13 +27,20 @@ + #include + #include "responder/common/responder.h" + +-/* KCM IO structure */ ++/* ++ * KCM IO structure ++ * ++ * In theory we cold use sss_iobuf there, but since iobuf was ++ * made opaque, this allows it to allocate the structures on ++ * the stack in one go. ++ * */ + struct kcm_data { + uint8_t *data; + size_t length; + }; + +-/* To avoid leaking the sssd-specific responder data to other ++/* ++ * To avoid leaking the sssd-specific responder data to other + * modules, the ccache databases and other KCM specific data + * are kept separately + */ +@@ -41,7 +48,8 @@ struct kcm_resp_ctx { + krb5_context k5c; + }; + +-/* responder context that contains both the responder data, ++/* ++ * responder context that contains both the responder data, + * like the ccaches and the sssd-specific stuff like the + * generic responder ctx + */ +@@ -55,4 +63,11 @@ struct kcm_ctx { + + int kcm_connection_setup(struct cli_ctx *cctx); + ++/* ++ * Internally in SSSD-KCM we use SSSD-internal error codes so that we ++ * can always the same sss_strerror() functions to format the errors ++ * nicely, but the client expects libkrb5 error codes. ++ */ ++krb5_error_code sss2krb5_error(errno_t err); ++ + #endif /* __KCMSRV_PVT_H__ */ +-- +2.9.3 + diff --git a/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch b/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch new file mode 100644 index 0000000..a5e0fc2 --- /dev/null +++ b/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch @@ -0,0 +1,2153 @@ +From 79f6ccd2dc3f0c1369d5a93678c88ee76ec761e0 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 7 Mar 2017 13:49:21 +0100 +Subject: [PATCH 24/36] KCM: Implement an internal ccache storage and retrieval + API +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In order for the KCM server to work with ccaches stored in different +locations, implement a middle-man between the KCM server and the ccache +storage. + +This module has asynchronous API because we can't assume anything about +where the ccaches are stored. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + Makefile.am | 9 + + configure.ac | 1 + + contrib/sssd.spec.in | 1 + + src/external/libuuid.m4 | 17 + + src/responder/kcm/kcmsrv_ccache.c | 1423 +++++++++++++++++++++++++++++++++ + src/responder/kcm/kcmsrv_ccache.h | 306 +++++++ + src/responder/kcm/kcmsrv_ccache_be.h | 204 +++++ + src/responder/kcm/kcmsrv_ccache_pvt.h | 62 ++ + src/responder/kcm/kcmsrv_pvt.h | 1 + + 9 files changed, 2024 insertions(+) + create mode 100644 src/external/libuuid.m4 + create mode 100644 src/responder/kcm/kcmsrv_ccache.c + create mode 100644 src/responder/kcm/kcmsrv_ccache.h + create mode 100644 src/responder/kcm/kcmsrv_ccache_be.h + create mode 100644 src/responder/kcm/kcmsrv_ccache_pvt.h + +diff --git a/Makefile.am b/Makefile.am +index 4248536e90370c1aab59549a9c18408ef314e6d4..a2b9dc49e95fa2d025f5174d2902866fab180a78 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -711,6 +711,9 @@ dist_noinst_HEADERS = \ + src/responder/secrets/secsrv_proxy.h \ + src/responder/kcm/kcm.h \ + src/responder/kcm/kcmsrv_pvt.h \ ++ src/responder/kcm/kcmsrv_ccache.h \ ++ src/responder/kcm/kcmsrv_ccache_pvt.h \ ++ src/responder/kcm/kcmsrv_ccache_be.h \ + src/sbus/sbus_client.h \ + src/sbus/sssd_dbus.h \ + src/sbus/sssd_dbus_meta.h \ +@@ -1488,16 +1491,22 @@ if BUILD_KCM + sssd_kcm_SOURCES = \ + src/responder/kcm/kcm.c \ + src/responder/kcm/kcmsrv_cmd.c \ ++ src/responder/kcm/kcmsrv_ccache.c \ + src/util/sss_sockets.c \ ++ src/util/sss_krb5.c \ ++ src/util/sss_iobuf.c \ + $(SSSD_RESPONDER_OBJ) \ + $(NULL) + sssd_kcm_CFLAGS = \ + $(AM_CFLAGS) \ + $(KRB5_CFLAGS) \ ++ $(UUID_CFLAGS) \ + $(NULL) + sssd_kcm_LDADD = \ + $(KRB5_LIBS) \ + $(SSSD_LIBS) \ ++ $(UUID_LIBS) \ ++ $(SYSTEMD_DAEMON_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ + $(NULL) + endif +diff --git a/configure.ac b/configure.ac +index c363d48a806cc1998e85779a92b6b59b0e2a5c9c..cf5e2557ef0a1bd6374200aa33abea6c509d03aa 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -202,6 +202,7 @@ fi + + if test x$with_kcm = xyes; then + m4_include([src/external/libcurl.m4]) ++ m4_include([src/external/libuuid.m4]) + fi + # This variable is defined by external/libcurl.m4, but conditionals + # must be always evaluated +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056..52d33b4de281dc1d91a9027ac1c8c878e66fb396 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -222,6 +222,7 @@ BuildRequires: systemtap-sdt-devel + %endif + BuildRequires: http-parser-devel + BuildRequires: jansson-devel ++BuildRequires: libuuid-devel + + %description + Provides a set of daemons to manage access to remote directories and +diff --git a/src/external/libuuid.m4 b/src/external/libuuid.m4 +new file mode 100644 +index 0000000000000000000000000000000000000000..55411a2118bd787c9d50ba61f9cb791e1c76088d +--- /dev/null ++++ b/src/external/libuuid.m4 +@@ -0,0 +1,17 @@ ++AC_SUBST(UUID_LIBS) ++AC_SUBST(UUID_CFLAGS) ++ ++PKG_CHECK_MODULES([UUID], [uuid], [found_uuid=yes], [found_uuid=no]) ++ ++SSS_AC_EXPAND_LIB_DIR() ++AS_IF([test x"$found_uuid" = xyes], ++ [AC_CHECK_HEADERS([uuid/uuid.h], ++ [AC_CHECK_LIB([uuid], ++ [uuid_generate], ++ [UUID_LIBS="-L$sss_extra_libdir -luuid"], ++ [AC_MSG_ERROR([libuuid missing uuid_generate])], ++ [-L$sss_extra_libdir -luuid])], ++ [AC_MSG_ERROR([ ++You must have the header file uuid.h installed to build sssd ++with KCM responder. If you want to build sssd without KCM responder ++then specify --without-kcm when running configure.])])]) +diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c +new file mode 100644 +index 0000000000000000000000000000000000000000..2c565b8378e3ec297faf655d3c48d7ab902713d3 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache.c +@@ -0,0 +1,1423 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM ccache operations ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include "util/crypto/sss_crypto.h" ++#include "util/util.h" ++#include "util/sss_krb5.h" ++#include "responder/kcm/kcmsrv_ccache.h" ++#include "responder/kcm/kcmsrv_ccache_pvt.h" ++#include "responder/kcm/kcmsrv_ccache_be.h" ++ ++static int kcm_cc_destructor(struct kcm_ccache *cc) ++{ ++ if (cc == NULL) { ++ return 0; ++ } ++ ++ krb5_free_principal(NULL, cc->client); ++ return 0; ++} ++ ++errno_t kcm_cc_new(TALLOC_CTX *mem_ctx, ++ krb5_context k5c, ++ struct cli_creds *owner, ++ const char *name, ++ krb5_principal princ, ++ struct kcm_ccache **_cc) ++{ ++ struct kcm_ccache *cc; ++ krb5_error_code kret; ++ errno_t ret; ++ ++ cc = talloc_zero(mem_ctx, struct kcm_ccache); ++ if (cc == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = kcm_check_name(name, owner); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Name %s is malformed\n", name); ++ return ret; ++ } ++ ++ cc->name = talloc_strdup(cc, name); ++ if (cc->name == NULL) { ++ talloc_free(cc); ++ return ENOMEM; ++ } ++ ++ uuid_generate(cc->uuid); ++ ++ kret = krb5_copy_principal(k5c, princ, &cc->client); ++ if (kret != 0) { ++ const char *err_msg = sss_krb5_get_error_message(k5c, kret); ++ DEBUG(SSSDBG_OP_FAILURE, ++ "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg); ++ sss_krb5_free_error_message(k5c, err_msg); ++ talloc_free(cc); ++ return ERR_INTERNAL; ++ } ++ ++ cc->owner.uid = cli_creds_get_uid(owner); ++ cc->owner.gid = cli_creds_get_gid(owner); ++ cc->kdc_offset = INT32_MAX; ++ ++ talloc_set_destructor(cc, kcm_cc_destructor); ++ *_cc = cc; ++ return EOK; ++} ++ ++const char *kcm_cc_get_name(struct kcm_ccache *cc) ++{ ++ return cc ? cc->name : NULL; ++} ++ ++errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid) ++{ ++ if (cc == NULL) { ++ return EINVAL; ++ } ++ uuid_copy(_uuid, cc->uuid); ++ return EOK; ++} ++ ++krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc) ++{ ++ return cc ? cc->client : NULL; ++} ++ ++bool kcm_cc_access(struct kcm_ccache *cc, ++ struct cli_creds *client) ++{ ++ bool ok; ++ uid_t uid = cli_creds_get_uid(client); ++ gid_t gid = cli_creds_get_gid(client); ++ ++ if (cc == NULL) { ++ return false; ++ } ++ ++ if (uid == 0 && gid == 0) { ++ /* root can access any ccache */ ++ return true; ++ } ++ ++ ok = ((cc->owner.uid == uid) && (cc->owner.gid == gid)); ++ if (!ok) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Client %"SPRIuid":%"SPRIgid" has no access to ccache %s\n", ++ cli_creds_get_uid(client), ++ cli_creds_get_gid(client), ++ cc->name); ++ } ++ return ok; ++} ++ ++int32_t kcm_cc_get_offset(struct kcm_ccache *cc) ++{ ++ return cc ? cc->kdc_offset : INT32_MAX; ++} ++ ++errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc, ++ struct sss_iobuf *cred_blob) ++{ ++ struct kcm_cred *kcreds; ++ uuid_t uuid; ++ errno_t ret; ++ ++ if (cc == NULL || cred_blob == NULL) { ++ return EINVAL; ++ } ++ ++ uuid_generate(uuid); ++ kcreds = kcm_cred_new(cc, uuid, cred_blob); ++ if (kcreds == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = kcm_cc_store_creds(cc, kcreds); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ return EOK; ++} ++ ++struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc) ++{ ++ if (cc == NULL) { ++ return NULL; ++ } ++ ++ return cc->creds; ++} ++ ++struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd) ++{ ++ if (crd == NULL) { ++ return NULL; ++ } ++ ++ return crd->next; ++} ++ ++struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob) ++{ ++ struct kcm_cred *kcreds; ++ ++ kcreds = talloc_zero(mem_ctx, struct kcm_cred); ++ if (kcreds == NULL) { ++ return NULL; ++ } ++ ++ uuid_copy(kcreds->uuid, uuid); ++ kcreds->cred_blob = talloc_steal(kcreds, cred_blob); ++ return kcreds; ++} ++ ++/* Add a cred to ccache */ ++errno_t kcm_cc_store_creds(struct kcm_ccache *cc, ++ struct kcm_cred *crd) ++{ ++ DLIST_ADD(cc->creds, crd); ++ talloc_steal(cc, crd); ++ return EOK; ++} ++ ++errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid) ++{ ++ if (crd == NULL) { ++ return EINVAL; ++ } ++ uuid_copy(_uuid, crd->uuid); ++ return EOK; ++} ++ ++struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd) ++{ ++ return crd ? crd->cred_blob : NULL; ++} ++ ++struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ enum kcm_ccdb_be cc_be) ++{ ++ errno_t ret; ++ struct kcm_ccdb *ccdb = NULL; ++ ++ if (ev == NULL) { ++ return NULL; ++ } ++ ++ ccdb = talloc_zero(mem_ctx, struct kcm_ccdb); ++ if (ccdb == NULL) { ++ return NULL; ++ } ++ ccdb->ev = ev; ++ ++ switch (cc_be) { ++ case CCDB_BE_MEMORY: ++ DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n"); ++ /* Not implemented yet */ ++ break; ++ case CCDB_BE_SECRETS: ++ DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n"); ++ /* Not implemented yet */ ++ break; ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database\n"); ++ break; ++ } ++ ++ if (ccdb->ops == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Ccache database not initialized\n"); ++ talloc_free(ccdb); ++ return NULL; ++ } ++ ++ ret = ccdb->ops->init(ccdb); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize ccache database\n"); ++ talloc_free(ccdb); ++ return NULL; ++ } ++ ++ return ccdb; ++} ++ ++struct kcm_ccdb_nextid_state { ++ char *next_cc; ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++}; ++ ++static void kcm_ccdb_nextid_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_nextid_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_nextid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->client = client; ++ ++ if (ev == NULL || db == NULL || client == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = state->db->ops->nextid_send(mem_ctx, ev, state->db, client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_nextid_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_nextid_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_nextid_state *state = tevent_req_data(req, ++ struct kcm_ccdb_nextid_state); ++ errno_t ret; ++ unsigned int nextid; ++ ++ ret = state->db->ops->nextid_recv(subreq, &nextid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to generate next UID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->next_cc = talloc_asprintf(state, "%"SPRIuid":%u", ++ cli_creds_get_uid(state->client), ++ nextid); ++ if (state->next_cc == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n"); ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "generated %s\n", state->next_cc); ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_nextid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ char **_next_cc) ++{ ++ struct kcm_ccdb_nextid_state *state = tevent_req_data(req, ++ struct kcm_ccdb_nextid_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_next_cc = talloc_steal(mem_ctx, state->next_cc); ++ return EOK; ++} ++ ++struct kcm_ccdb_list_state { ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ ++ uuid_t *uuid_list; ++}; ++ ++static void kcm_ccdb_list_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_list_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_list_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->client = client; ++ ++ if (ev == NULL || db == NULL || client == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = state->db->ops->list_send(mem_ctx, ++ ev, ++ state->db, ++ client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_list_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_list_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_list_state *state = tevent_req_data(req, ++ struct kcm_ccdb_list_state); ++ errno_t ret; ++ ++ ret = state->db->ops->list_recv(subreq, state, &state->uuid_list); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to list all ccaches [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_list_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t **_uuid_list) ++{ ++ struct kcm_ccdb_list_state *state = tevent_req_data(req, ++ struct kcm_ccdb_list_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list); ++ return EOK; ++} ++ ++struct kcm_ccdb_get_default_state { ++ struct kcm_ccdb *db; ++ uuid_t uuid; ++}; ++ ++static void kcm_ccdb_get_default_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_get_default_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_get_default_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ ++ if (ev == NULL || db == NULL || client == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = db->ops->get_default_send(mem_ctx, ev, db, client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_get_default_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_get_default_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_get_default_state *state = tevent_req_data(req, ++ struct kcm_ccdb_get_default_state); ++ errno_t ret; ++ ++ ret = state->db->ops->get_default_recv(subreq, state->uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get the default ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_get_default_recv(struct tevent_req *req, ++ uuid_t *uuid) ++{ ++ struct kcm_ccdb_get_default_state *state = tevent_req_data(req, ++ struct kcm_ccdb_get_default_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ if (uuid != NULL) { ++ /* The caller might supply a NULL dfl to just check if there is ++ * some default ccache ++ */ ++ uuid_copy(*uuid, state->uuid); ++ } ++ ++ return EOK; ++} ++ ++struct kcm_ccdb_set_default_state { ++ struct tevent_context *ev; ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ uuid_t uuid; ++}; ++ ++static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq); ++static void kcm_ccdb_set_default_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_set_default_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_set_default_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->ev = ev; ++ state->client = client; ++ uuid_copy(state->uuid, uuid); ++ ++ if (ev == NULL || db == NULL || client == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ if (uuid_is_null(uuid)) { ++ /* NULL UUID means to just reset the default to 'no default' */ ++ subreq = state->db->ops->set_default_send(state, ++ state->ev, ++ state->db, ++ state->client, ++ state->uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req); ++ } else { ++ /* Otherwise we need to check if the client can access the UUID ++ * about to be set as default ++ */ ++ subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_set_default_uuid_resolved, req); ++ } ++ ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_set_default_state *state = tevent_req_data(req, ++ struct kcm_ccdb_set_default_state); ++ errno_t ret; ++ bool ok; ++ struct kcm_ccache *cc; ++ ++ ret = state->db->ops->getbyuuid_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get cache by UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n"); ++ tevent_req_error(req, ERR_KCM_CC_END); ++ return; ++ } ++ ++ ok = kcm_cc_access(cc, state->client); ++ if (!ok) { ++ tevent_req_error(req, EACCES); ++ return; ++ } ++ ++ subreq = state->db->ops->set_default_send(state, ++ state->ev, ++ state->db, ++ state->client, ++ state->uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req); ++} ++ ++static void kcm_ccdb_set_default_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_set_default_state *state = tevent_req_data(req, ++ struct kcm_ccdb_set_default_state); ++ errno_t ret; ++ ++ ret = state->db->ops->set_default_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to set the default ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_set_default_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++struct kcm_ccdb_getbyname_state { ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ ++ struct kcm_ccache *cc; ++}; ++ ++static void kcm_ccdb_getbyname_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_getbyname_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyname_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->client = client; ++ ++ if (ev == NULL || db == NULL || client == NULL || name == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = db->ops->getbyname_send(state, ev, db, client, name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_getbyname_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_getbyname_state *state = tevent_req_data(req, ++ struct kcm_ccdb_getbyname_state); ++ errno_t ret; ++ bool ok; ++ ++ ret = state->db->ops->getbyname_recv(subreq, state, &state->cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get cache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (state->cc == NULL) { ++ DEBUG(SSSDBG_TRACE_LIBS, "No cache found by name\n"); ++ tevent_req_done(req); ++ return; ++ } ++ ++ ok = kcm_cc_access(state->cc, state->client); ++ if (!ok) { ++ tevent_req_error(req, EACCES); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct kcm_ccdb_getbyname_state *state = tevent_req_data(req, ++ struct kcm_ccdb_getbyname_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++struct kcm_ccdb_getbyuuid_state { ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ ++ struct kcm_ccache *cc; ++}; ++ ++static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_getbyuuid_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyuuid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->client = client; ++ ++ if (ev == NULL || db == NULL || client == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_getbyuuid_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req, ++ struct kcm_ccdb_getbyuuid_state); ++ errno_t ret; ++ bool ok; ++ ++ ret = state->db->ops->getbyuuid_recv(subreq, state, &state->cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get cache by UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (state->cc == NULL) { ++ DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n"); ++ tevent_req_done(req); ++ return; ++ } ++ ++ ok = kcm_cc_access(state->cc, state->client); ++ if (!ok) { ++ tevent_req_error(req, EACCES); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req, ++ struct kcm_ccdb_getbyuuid_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++struct kcm_ccdb_name_by_uuid_state { ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ ++ const char *name; ++}; ++ ++static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_name_by_uuid_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, ++ &state, ++ struct kcm_ccdb_name_by_uuid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->client = client; ++ ++ if (ev == NULL || db == NULL || client == NULL || uuid_is_null(uuid)) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = db->ops->name_by_uuid_send(state, ev, db, client, uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_name_by_uuid_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req, ++ struct kcm_ccdb_name_by_uuid_state); ++ errno_t ret; ++ ++ ret = state->db->ops->name_by_uuid_recv(subreq, state, &state->name); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to resolve cache by UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ const char **_name) ++{ ++ struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req, ++ struct kcm_ccdb_name_by_uuid_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_name = talloc_steal(mem_ctx, state->name); ++ return EOK; ++} ++ ++struct kcm_ccdb_uuid_by_name_state { ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ ++ uuid_t uuid; ++}; ++ ++static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_uuid_by_name_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, ++ &state, ++ struct kcm_ccdb_uuid_by_name_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->client = client; ++ ++ if (ev == NULL || db == NULL || client == NULL || name == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = db->ops->uuid_by_name_send(state, ev, db, client, name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_uuid_by_name_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req, ++ struct kcm_ccdb_uuid_by_name_state); ++ errno_t ret; ++ ++ ret = state->db->ops->uuid_by_name_recv(subreq, state, state->uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to resolve cache by UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t _uuid) ++{ ++ struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req, ++ struct kcm_ccdb_uuid_by_name_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ uuid_copy(_uuid, state->uuid); ++ return EOK; ++} ++ ++struct kcm_ccdb_create_cc_state { ++ struct kcm_ccdb *db; ++}; ++ ++static void kcm_ccdb_create_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ struct kcm_ccache *cc) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_create_cc_state *state = NULL; ++ errno_t ret; ++ bool ok; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_create_cc_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ ++ if (ev == NULL || db == NULL || client == NULL || cc == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ ok = kcm_cc_access(cc, client); ++ if (!ok) { ++ ret = EACCES; ++ goto immediate; ++ } ++ ++ subreq = state->db->ops->create_send(mem_ctx, ++ ev, ++ state->db, ++ client, ++ cc); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_create_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_create_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_create_cc_state *state = tevent_req_data(req, ++ struct kcm_ccdb_create_cc_state); ++ errno_t ret; ++ ++ ret = state->db->ops->create_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to create ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx) ++{ ++ if (mod_ctx == NULL) { ++ return; ++ } ++ ++ mod_ctx->kdc_offset = INT32_MAX; ++} ++ ++void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx) ++{ ++ if (cc == NULL || mod_ctx == NULL) { ++ return; ++ } ++ ++ if (mod_ctx->kdc_offset != INT32_MAX) { ++ cc->kdc_offset = mod_ctx->kdc_offset; ++ } ++ ++} ++ ++struct kcm_ccdb_mod_cc_state { ++ struct kcm_ccdb *db; ++}; ++ ++static void kcm_ccdb_mod_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct kcm_mod_ctx *mod_cc) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_mod_cc_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_mod_cc_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ ++ if (ev == NULL || db == NULL || client == NULL || mod_cc == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = state->db->ops->mod_send(mem_ctx, ++ ev, ++ state->db, ++ client, ++ uuid, ++ mod_cc); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_mod_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_mod_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_mod_cc_state *state = tevent_req_data(req, ++ struct kcm_ccdb_mod_cc_state); ++ errno_t ret; ++ ++ ret = state->db->ops->mod_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to create ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++struct kcm_ccdb_store_cred_blob_state { ++ struct kcm_ccdb *db; ++}; ++ ++static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_store_cred_blob_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_store_cred_blob_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ ++ if (ev == NULL || db == NULL || client == NULL || cred_blob == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = state->db->ops->store_cred_send(mem_ctx, ++ ev, ++ state->db, ++ client, ++ uuid, ++ cred_blob); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_store_cred_blob_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_store_cred_blob_state *state = tevent_req_data(req, ++ struct kcm_ccdb_store_cred_blob_state); ++ errno_t ret; ++ ++ ret = state->db->ops->store_cred_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to create ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++struct kcm_ccdb_delete_cc_state { ++ struct tevent_context *ev; ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ uuid_t uuid; ++}; ++ ++static void kcm_ccdb_delete_done(struct tevent_req *subreq); ++static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq); ++static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_ccdb_delete_cc_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_delete_cc_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->db = db; ++ state->ev = ev; ++ state->client = client; ++ uuid_copy(state->uuid, uuid); ++ ++ if (ev == NULL || db == NULL || client == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = state->db->ops->delete_send(state, ++ state->ev, ++ state->db, ++ state->client, ++ state->uuid); ++ tevent_req_set_callback(subreq, kcm_ccdb_delete_done, req); ++ ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_ccdb_delete_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req, ++ struct kcm_ccdb_delete_cc_state); ++ errno_t ret; ++ ++ ret = state->db->ops->delete_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to delete ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ /* The delete operation must also check if the deleted ccache was ++ * the default and reset the default if it was ++ */ ++ subreq = state->db->ops->get_default_send(state, ++ state->ev, ++ state->db, ++ state->client); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_delete_get_default_done, req); ++} ++ ++static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req, ++ struct kcm_ccdb_delete_cc_state); ++ errno_t ret; ++ uuid_t dfl_uuid; ++ uuid_t null_uuid; ++ ++ ret = state->db->ops->get_default_recv(subreq, dfl_uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get the default ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (uuid_compare(dfl_uuid, state->uuid) != 0) { ++ /* The ccache about to be deleted was not the default, quit */ ++ tevent_req_done(req); ++ return; ++ } ++ ++ /* If we deleted the default ccache, reset the default ccache to 'none' */ ++ uuid_clear(null_uuid); ++ ++ subreq = state->db->ops->set_default_send(state, ++ state->ev, ++ state->db, ++ state->client, ++ null_uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_ccdb_delete_default_reset_done, req); ++} ++ ++static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req, ++ struct kcm_ccdb_delete_cc_state); ++ errno_t ret; ++ ++ ret = state->db->ops->set_default_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to NULL the default ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++void kcm_debug_uuid(uuid_t uuid) ++{ ++ char dbgbuf[UUID_STR_SIZE]; ++ ++ if (!(debug_level & SSSDBG_TRACE_ALL) || uuid == NULL) { ++ return; ++ } ++ ++ uuid_unparse(uuid, dbgbuf); ++ DEBUG(SSSDBG_TRACE_ALL, "UUID: %s\n", dbgbuf); ++} ++ ++errno_t kcm_check_name(const char *name, struct cli_creds *client) ++{ ++ char prefix[64]; ++ size_t prefix_len; ++ ++ prefix_len = snprintf(prefix, sizeof(prefix), ++ "%"SPRIuid, cli_creds_get_uid(client)); ++ ++ if (strncmp(name, prefix, prefix_len) != 0) { ++ return ERR_KCM_WRONG_CCNAME_FORMAT; ++ } ++ return EOK; ++} +diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h +new file mode 100644 +index 0000000000000000000000000000000000000000..130ae48ae30d5e1e2ab238a647a9b9dc76cc4945 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache.h +@@ -0,0 +1,306 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM ccache operations ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++#ifndef _KCMSRV_CCACHE_H_ ++#define _KCMSRV_CCACHE_H_ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "util/util.h" ++#include "util/sss_iobuf.h" ++#include "util/util_creds.h" ++ ++#define UUID_BYTES 16 ++#define UUID_STR_SIZE 37 ++ ++/* ++ * Credentials are opaque to the KCM server ++ * ++ * Each ccache has a unique UUID. ++ */ ++struct kcm_cred; ++ ++/* ++ * An opaque ccache type and its operations ++ * ++ * Contains zero or some KCM credentials. One credential in the cache ++ * is marked as the default one. The client can set and get the default ++ * cache (e.g. with kswitch) but one cache is always the default -- we ++ * fall back to the one created first. ++ * ++ * Each cache has a name and a UUID. Heimdal allows the name to be changed, ++ * we don't (yet, because the MIT client doesn't allow that either) ++ * ++ * Each ccache also stores a client principal. ++ */ ++struct kcm_ccache; ++ ++/* ++ * Create a new KCM ccache owned by mem_ctx on the ++ * memory level. ++ * ++ * When created, the ccache contains no credendials ++ */ ++errno_t kcm_cc_new(TALLOC_CTX *mem_ctx, ++ krb5_context k5c, ++ struct cli_creds *owner, ++ const char *name, ++ krb5_principal princ, ++ struct kcm_ccache **_cc); ++ ++/* ++ * Returns true if a client can access a ccache. ++ * ++ * Note that root can access any ccache */ ++bool kcm_cc_access(struct kcm_ccache *cc, ++ struct cli_creds *client); ++ ++/* ++ * Since the kcm_ccache structure is opaque, the kcmsrv_ccache ++ * layer contains a number of getsetters to read and write ++ * properties of the kcm_ccache structure ++ */ ++const char *kcm_cc_get_name(struct kcm_ccache *cc); ++errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid); ++krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc); ++int32_t kcm_cc_get_offset(struct kcm_ccache *cc); ++ ++/* Mainly useful for creating a cred structure from a persistent ++ * storage ++ */ ++struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob); ++ ++/* Add a cred to ccache */ ++errno_t kcm_cc_store_creds(struct kcm_ccache *cc, ++ struct kcm_cred *crd); ++ ++errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid); ++ ++/* ++ * At the moment, the credentials are stored without unmarshalling ++ * them, just as the clients sends the credentials. ++ */ ++struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd); ++errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc, ++ struct sss_iobuf *cred_blob); ++ /* ++ * The KCM server can call kcm_cred_get_creds to fetch the first ++ * credential, then iterate over the credentials with ++ * kcm_cc_next_cred until it returns NULL ++ */ ++struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc); ++struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd); ++ ++enum kcm_ccdb_be { ++ CCDB_BE_MEMORY, ++ CCDB_BE_SECRETS, ++}; ++ ++/* An opaque database that contains all the ccaches */ ++struct kcm_ccdb; ++ ++/* ++ * Initialize a ccache database of type cc_be ++ */ ++struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ enum kcm_ccdb_be cc_be); ++ ++/* ++ * In KCM, each ccache name is usually in the form of "UID: ++ * ++ * The is generated by the KCM ccache database. Use this function ++ * to retrieve the next number ++ */ ++struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client); ++errno_t kcm_ccdb_nextid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ char **_nextid); ++ ++/* ++ * List all ccaches that belong to a given client ++ * ++ * The cc_list the recv function returns is NULL-terminated. ++ * ++ * NOTE: Contrary to how Heimdal behaves, root CAN NOT list all ccaches ++ * of all users. This is a deliberate decision to treat root as any other ++ * user, except it can access a ccache of another user by name, just not ++ * list them. ++ * ++ * If a client has no ccaches, the function returns OK, but an empty list ++ * containing just the NULL sentinel. ++ */ ++struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client); ++errno_t kcm_ccdb_list_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t **_uuid_list); ++ ++/* ++ * Retrieve a ccache by name. ++ * ++ * If there is no such ccache, return EOK, but a NULL _cc pointer ++ */ ++struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name); ++errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc); ++ ++/* ++ * Retrieve a ccache by UUID ++ * ++ * If there is no such ccache, return EOK, but a NULL _cc pointer ++ */ ++struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc); ++ ++/* ++ * Retrieve the default ccache. If there is no default cache, ++ * return EOK, but a NULL UUID. ++ */ ++struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client); ++errno_t kcm_ccdb_get_default_recv(struct tevent_req *req, ++ uuid_t *uuid); ++ ++/* ++ * Translating name to UUID is often considerably faster than doing a full ++ * CC retrieval, hence this function and the converse. If the UUID cannot ++ * be found in the database, return ERR_KCM_CC_END ++ */ ++struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ const char **_name); ++ ++/* ++ * Translating UUID to name is often considerably faster than doing a full ++ * CC retrieval, hence this function and the converse. If the UUID cannot ++ * be found in the database, return ERR_KCM_CC_END ++ */ ++struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name); ++errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t _uuid); ++ ++/* ++ * Set the default ccache. Passing a NULL UUID is a legal operation ++ * that 'unsets' the default ccache. ++ */ ++struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++errno_t kcm_ccdb_set_default_recv(struct tevent_req *req); ++ ++/* ++ * Add a ccache to the database. ++ */ ++struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ struct kcm_ccache *cc); ++errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req); ++ ++/* ++ * Modify cache properties in a db ++ */ ++struct kcm_mod_ctx { ++ int32_t kdc_offset; ++ /* More settable properties (like name, when we support renames ++ * will be added later ++ */ ++}; ++ ++void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx); ++void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx); ++ ++struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct kcm_mod_ctx *mod_cc); ++errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req); ++ ++/* ++ * Store a credential in a cache ++ */ ++struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob); ++errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req); ++ ++/* ++ * Delete a ccache from the database ++ */ ++struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req); ++ ++void kcm_debug_uuid(uuid_t uuid); ++ ++/* ++ * The KCM clients are not allowed (except root) to create ccaches ++ * with arbitrary names. Instead, we assert that the ccache name ++ * begins with UID where UID is the stringified representation of ++ * the client's UID number ++ */ ++errno_t kcm_check_name(const char *name, struct cli_creds *client); ++ ++#endif /* _KCMSRV_CCACHE_H_ */ +diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h +new file mode 100644 +index 0000000000000000000000000000000000000000..1bd2b6981e227675866e82e0a5389445cac4df66 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache_be.h +@@ -0,0 +1,204 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM ccache database interface ++ ++ This file should only be included from the ccache.c module. ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef _KCMSRV_CCACHE_BE_ ++#define _KCMSRV_CCACHE_BE_ ++ ++#include "config.h" ++ ++#include ++#include "responder/kcm/kcmsrv_ccache.h" ++ ++typedef errno_t ++(*ccdb_init_fn)(struct kcm_ccdb *db); ++ ++typedef struct tevent_req * ++(*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client); ++typedef errno_t ++(*ccdb_nextid_recv_fn)(struct tevent_req *req, ++ unsigned int *_nextid); ++ ++typedef struct tevent_req * ++(*ccdb_set_default_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++typedef errno_t ++(*ccdb_set_default_recv_fn)(struct tevent_req *req); ++ ++typedef struct tevent_req * ++(*ccdb_get_default_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client); ++typedef errno_t ++(*ccdb_get_default_recv_fn)(struct tevent_req *req, ++ uuid_t dfl); ++ ++ ++typedef struct tevent_req * ++(*ccdb_list_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client); ++typedef errno_t ++(*ccdb_list_recv_fn)(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t **_uuid_list); ++ ++typedef struct tevent_req * ++(*ccdb_getbyname_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name); ++typedef errno_t ++(*ccdb_getbyname_recv_fn)(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc); ++ ++typedef struct tevent_req * ++(*ccdb_getbyuuid_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++typedef errno_t ++(*ccdb_getbyuuid_recv_fn)(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc); ++ ++typedef struct tevent_req * ++(*ccdb_name_by_uuid_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++typedef errno_t ++(*ccdb_name_by_uuid_recv_fn)(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ const char **_name); ++ ++typedef struct tevent_req * ++(*ccdb_uuid_by_name_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name); ++typedef errno_t ++(*ccdb_uuid_by_name_recv_fn)(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t _uuid); ++ ++typedef struct tevent_req * ++(*ccdb_create_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ struct kcm_ccache *cc); ++typedef errno_t ++(*ccdb_create_recv_fn)(struct tevent_req *req); ++ ++typedef struct tevent_req * ++(*ccdb_mod_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct kcm_mod_ctx *mod_cc); ++typedef errno_t ++(*ccdb_mod_recv_fn)(struct tevent_req *req); ++ ++typedef struct tevent_req * ++(*kcm_ccdb_store_cred_blob_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob); ++typedef errno_t ++(*kcm_ccdb_store_cred_blob_recv_fn)(struct tevent_req *req); ++ ++typedef struct tevent_req * ++(*ccdb_delete_send_fn)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid); ++typedef errno_t ++(*ccdb_delete_recv_fn)(struct tevent_req *req); ++ ++/* ++ * Each ccache back end (for example memory or secrets) must implement ++ * all these functions. The functions are wrapped by the kcm_ccdb ++ * interface that performs additional sanity checks or contains shared ++ * logic such as access checks but in general doesn't assume anything ++ * about how the operations work. ++ */ ++struct kcm_ccdb_ops { ++ ccdb_init_fn init; ++ ++ ccdb_nextid_send_fn nextid_send; ++ ccdb_nextid_recv_fn nextid_recv; ++ ++ ccdb_set_default_send_fn set_default_send; ++ ccdb_set_default_recv_fn set_default_recv; ++ ++ ccdb_get_default_send_fn get_default_send; ++ ccdb_get_default_recv_fn get_default_recv; ++ ++ ccdb_list_send_fn list_send; ++ ccdb_list_recv_fn list_recv; ++ ++ ccdb_getbyname_send_fn getbyname_send; ++ ccdb_getbyname_recv_fn getbyname_recv; ++ ++ ccdb_getbyuuid_send_fn getbyuuid_send; ++ ccdb_getbyuuid_recv_fn getbyuuid_recv; ++ ++ ccdb_name_by_uuid_send_fn name_by_uuid_send; ++ ccdb_name_by_uuid_recv_fn name_by_uuid_recv; ++ ++ ccdb_uuid_by_name_send_fn uuid_by_name_send; ++ ccdb_uuid_by_name_recv_fn uuid_by_name_recv; ++ ++ ccdb_create_send_fn create_send; ++ ccdb_create_recv_fn create_recv; ++ ++ ccdb_mod_send_fn mod_send; ++ ccdb_mod_recv_fn mod_recv; ++ ++ kcm_ccdb_store_cred_blob_send_fn store_cred_send; ++ kcm_ccdb_store_cred_blob_recv_fn store_cred_recv; ++ ++ ccdb_delete_send_fn delete_send; ++ ccdb_delete_recv_fn delete_recv; ++}; ++ ++extern const struct kcm_ccdb_ops ccdb_mem_ops; ++ ++#endif /* _KCMSRV_CCACHE_BE_ */ +diff --git a/src/responder/kcm/kcmsrv_ccache_pvt.h b/src/responder/kcm/kcmsrv_ccache_pvt.h +new file mode 100644 +index 0000000000000000000000000000000000000000..0cc24c2b8cd4d44080d2aa4384f7b2a73520c5a0 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache_pvt.h +@@ -0,0 +1,62 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM ccache operations - private structures ++ ++ Should be accessed only from the ccache layer. ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++#ifndef _KCMSRV_CCACHE_PVT_H ++#define _KCMSRV_CCACHE_PVT_H ++ ++#include "responder/kcm/kcmsrv_ccache.h" ++#include "responder/kcm/kcmsrv_ccache_be.h" ++ ++struct kcm_ccache_owner { ++ uid_t uid; ++ gid_t gid; ++}; ++ ++struct kcm_cred { ++ struct sss_iobuf *cred_blob; ++ /* Randomly generated 16 bytes */ ++ uuid_t uuid; ++ ++ struct kcm_cred *next; ++ struct kcm_cred *prev; ++}; ++ ++struct kcm_ccdb { ++ enum kcm_ccdb_be cc_be_type; ++ struct tevent_context *ev; ++ ++ void *db_handle; ++ const struct kcm_ccdb_ops *ops; ++}; ++ ++struct kcm_ccache { ++ const char *name; ++ struct kcm_ccache_owner owner; ++ uuid_t uuid; ++ ++ krb5_principal client; ++ int32_t kdc_offset; ++ ++ struct kcm_cred *creds; ++}; ++ ++#endif /* _KCMSRV_CCACHE_PVT_H */ +diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h +index fd1fd9fa32d59a323d465def68999f24f84e3923..a29680246c1e616da75e1bbff951ce2fad66fb65 100644 +--- a/src/responder/kcm/kcmsrv_pvt.h ++++ b/src/responder/kcm/kcmsrv_pvt.h +@@ -46,6 +46,7 @@ struct kcm_data { + */ + struct kcm_resp_ctx { + krb5_context k5c; ++ struct kcm_ccdb *db; + }; + + /* +-- +2.9.3 + diff --git a/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch b/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch new file mode 100644 index 0000000..6bfffe6 --- /dev/null +++ b/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch @@ -0,0 +1,907 @@ +From e7aa9061532b1ac139e155e7e9881c2447675e3c Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 7 Mar 2017 13:49:43 +0100 +Subject: [PATCH 25/36] KCM: Add a in-memory credential storage +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implements a simple back end for the ccache module that lets the KCM +server store credentials directly in memory. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + Makefile.am | 1 + + src/responder/kcm/kcm.c | 13 +- + src/responder/kcm/kcmsrv_ccache.c | 2 +- + src/responder/kcm/kcmsrv_ccache_mem.c | 805 ++++++++++++++++++++++++++++++++++ + 4 files changed, 817 insertions(+), 4 deletions(-) + create mode 100644 src/responder/kcm/kcmsrv_ccache_mem.c + +diff --git a/Makefile.am b/Makefile.am +index a2b9dc49e95fa2d025f5174d2902866fab180a78..5605c1a53c44fd9e83394e80b7f71828df1d39b6 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1492,6 +1492,7 @@ sssd_kcm_SOURCES = \ + src/responder/kcm/kcm.c \ + src/responder/kcm/kcmsrv_cmd.c \ + src/responder/kcm/kcmsrv_ccache.c \ ++ src/responder/kcm/kcmsrv_ccache_mem.c \ + src/util/sss_sockets.c \ + src/util/sss_krb5.c \ + src/util/sss_iobuf.c \ +diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c +index 90a6999c5e39d48a1a2ea8168d171612a65077d5..2c12ef215ce3967df183e51c20590c5f439d278f 100644 +--- a/src/responder/kcm/kcm.c ++++ b/src/responder/kcm/kcm.c +@@ -22,9 +22,9 @@ + #include "config.h" + + #include +-#include + + #include "responder/kcm/kcm.h" ++#include "responder/kcm/kcmsrv_ccache.h" + #include "responder/kcm/kcmsrv_pvt.h" + #include "responder/common/responder.h" + #include "util/util.h" +@@ -110,7 +110,8 @@ static int kcm_data_destructor(void *ptr) + return 0; + } + +-static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx) ++static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev) + { + struct kcm_resp_ctx *kcm_data; + krb5_error_code kret; +@@ -121,6 +122,12 @@ static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx) + return NULL; + } + ++ kcm_data->db = kcm_ccdb_init(kcm_data, ev, CCDB_BE_MEMORY); ++ if (kcm_data->db == NULL) { ++ talloc_free(kcm_data); ++ return NULL; ++ } ++ + kret = krb5_init_context(&kcm_data->k5c); + if (kret != EOK) { + talloc_free(kcm_data); +@@ -169,7 +176,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, + goto fail; + } + +- kctx->kcm_data = kcm_data_setup(kctx); ++ kctx->kcm_data = kcm_data_setup(kctx, ev); + if (kctx->kcm_data == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error initializing responder data\n"); +diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c +index 2c565b8378e3ec297faf655d3c48d7ab902713d3..2ae120269b0c62275ba2acdff6d6daa8b7077708 100644 +--- a/src/responder/kcm/kcmsrv_ccache.c ++++ b/src/responder/kcm/kcmsrv_ccache.c +@@ -240,7 +240,7 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx, + switch (cc_be) { + case CCDB_BE_MEMORY: + DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n"); +- /* Not implemented yet */ ++ ccdb->ops = &ccdb_mem_ops; + break; + case CCDB_BE_SECRETS: + DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n"); +diff --git a/src/responder/kcm/kcmsrv_ccache_mem.c b/src/responder/kcm/kcmsrv_ccache_mem.c +new file mode 100644 +index 0000000000000000000000000000000000000000..1c4f3df8d3b35b0428a143d4b545562d9cc0e574 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache_mem.c +@@ -0,0 +1,805 @@ ++/* ++ SSSD ++ ++ KCM Server - ccache in-memory storage ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "util/util.h" ++#include "responder/kcm/kcmsrv_ccache_pvt.h" ++#include "responder/kcm/kcmsrv_ccache_be.h" ++ ++struct ccdb_mem; ++ ++/* ++ * The KCM memory database is just a double-linked list of kcm_ccache structures ++ */ ++struct ccache_mem_wrap { ++ struct kcm_ccache *cc; ++ bool is_default; ++ ++ struct ccache_mem_wrap *next; ++ struct ccache_mem_wrap *prev; ++ ++ struct ccdb_mem *mem_be; ++}; ++ ++struct ccdb_mem { ++ /* Both ccaches and the next-id are kept in memory */ ++ struct ccache_mem_wrap *head; ++ unsigned int nextid; ++}; ++ ++/* In order to provide a consistent interface, we need to let the caller ++ * of getbyXXX own the ccache, therefore the memory back end returns a shallow ++ * copy of the ccache ++ */ ++static struct kcm_ccache *kcm_ccache_dup(TALLOC_CTX *mem_ctx, ++ struct kcm_ccache *in) ++{ ++ struct kcm_ccache *out; ++ ++ out = talloc_zero(mem_ctx, struct kcm_ccache); ++ if (out == NULL) { ++ return NULL; ++ } ++ memcpy(out, in, sizeof(struct kcm_ccache)); ++ ++ return out; ++} ++ ++static struct ccache_mem_wrap *memdb_get_by_uuid(struct ccdb_mem *memdb, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ uid_t uid; ++ struct ccache_mem_wrap *ccwrap = NULL; ++ struct ccache_mem_wrap *out = NULL; ++ ++ uid = cli_creds_get_uid(client); ++ ++ DLIST_FOR_EACH(ccwrap, memdb->head) { ++ if (ccwrap->cc == NULL) { ++ /* since KCM stores ccaches, better not crash.. */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n"); ++ continue; ++ } ++ ++ if (ccwrap->cc->owner.uid == uid) { ++ if (uuid_compare(uuid, ccwrap->cc->uuid) == 0) { ++ out = ccwrap; ++ break; ++ } ++ } ++ } ++ ++ return out; ++} ++ ++static struct ccache_mem_wrap *memdb_get_by_name(struct ccdb_mem *memdb, ++ struct cli_creds *client, ++ const char *name) ++{ ++ uid_t uid; ++ struct ccache_mem_wrap *ccwrap = NULL; ++ struct ccache_mem_wrap *out = NULL; ++ ++ uid = cli_creds_get_uid(client); ++ ++ DLIST_FOR_EACH(ccwrap, memdb->head) { ++ if (ccwrap->cc == NULL) { ++ /* since KCM stores ccaches, better not crash.. */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n"); ++ continue; ++ } ++ ++ if (ccwrap->cc->owner.uid == uid) { ++ if (strcmp(ccwrap->cc->name, name) == 0) { ++ out = ccwrap; ++ break; ++ } ++ } ++ } ++ ++ return out; ++} ++ ++/* Since with the in-memory database, the database operations are just ++ * fake-async wrappers around otherwise sync operations, we don't often ++ * need any state, so we use this empty structure instead ++ */ ++struct ccdb_mem_dummy_state { ++}; ++ ++static int ccwrap_destructor(void *ptr) ++{ ++ struct ccache_mem_wrap *ccwrap = talloc_get_type(ptr, struct ccache_mem_wrap); ++ ++ if (ccwrap == NULL) { ++ return 0; ++ } ++ ++ if (ccwrap->cc != NULL) { ++ if (ccwrap->cc->creds) { ++ safezero(sss_iobuf_get_data(ccwrap->cc->creds->cred_blob), ++ sss_iobuf_get_size(ccwrap->cc->creds->cred_blob)); ++ } ++ } ++ ++ ++ DLIST_REMOVE(ccwrap->mem_be->head, ccwrap); ++ ++ return 0; ++} ++ ++static errno_t ccdb_mem_init(struct kcm_ccdb *db) ++{ ++ struct ccdb_mem *memdb = NULL; ++ ++ memdb = talloc_zero(db, struct ccdb_mem); ++ if (memdb == NULL) { ++ return ENOMEM; ++ } ++ db->db_handle = memdb; ++ ++ return EOK; ++} ++ ++struct ccdb_mem_nextid_state { ++ unsigned int nextid; ++}; ++ ++static struct tevent_req *ccdb_mem_nextid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_nextid_state *state = NULL; ++ struct ccdb_mem *memdb = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_nextid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ if (memdb == NULL) { ++ ret = EIO; ++ goto immediate; ++ } ++ ++ state->nextid = memdb->nextid++; ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_nextid_recv(struct tevent_req *req, ++ unsigned int *_nextid) ++{ ++ struct ccdb_mem_nextid_state *state = tevent_req_data(req, ++ struct ccdb_mem_nextid_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_nextid = state->nextid; ++ return EOK; ++} ++ ++struct ccdb_mem_list_state { ++ uuid_t *uuid_list; ++}; ++ ++static struct tevent_req *ccdb_mem_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct ccache_mem_wrap *ccwrap = NULL; ++ struct ccdb_mem_list_state *state = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ size_t num_ccaches = 0; ++ size_t cc_index = 0; ++ errno_t ret; ++ uid_t uid; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_list_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ uid = cli_creds_get_uid(client); ++ ++ DLIST_FOR_EACH(ccwrap, memdb->head) { ++ if (ccwrap->cc->owner.uid == uid) { ++ num_ccaches++; ++ } ++ } ++ ++ state->uuid_list = talloc_zero_array(state, uuid_t, num_ccaches+1); ++ if (state->uuid_list == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ cc_index = 0; ++ DLIST_FOR_EACH(ccwrap, memdb->head) { ++ if (ccwrap->cc->owner.uid == uid) { ++ uuid_copy(state->uuid_list[cc_index], ccwrap->cc->uuid); ++ cc_index++; ++ } ++ } ++ uuid_clear(state->uuid_list[num_ccaches]); ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_list_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t **_uuid_list) ++{ ++ struct ccdb_mem_list_state *state = tevent_req_data(req, ++ struct ccdb_mem_list_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list); ++ return EOK; ++} ++ ++static struct tevent_req *ccdb_mem_set_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_dummy_state *state = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ struct ccache_mem_wrap *ccwrap = NULL; ++ uid_t uid = cli_creds_get_uid(client); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ /* Reset all ccache defaults first */ ++ DLIST_FOR_EACH(ccwrap, memdb->head) { ++ if (ccwrap->cc == NULL) { ++ /* since KCM stores ccaches, better not crash.. */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n"); ++ continue; ++ } ++ ++ if (ccwrap->cc->owner.uid == uid) { ++ ccwrap->is_default = false; ++ } ++ } ++ ++ /* Then set the default for the right ccache. This also allows to ++ * pass a null uuid to just reset the old ccache (for example after ++ * deleting the default ++ */ ++ ccwrap = memdb_get_by_uuid(memdb, client, uuid); ++ if (ccwrap != NULL) { ++ ccwrap->is_default = true; ++ } ++ ++ tevent_req_done(req); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_set_default_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++struct ccdb_mem_get_default_state { ++ uuid_t dfl_uuid; ++}; ++ ++static struct tevent_req *ccdb_mem_get_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_get_default_state *state = NULL; ++ struct ccache_mem_wrap *ccwrap = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ uid_t uid = cli_creds_get_uid(client); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_get_default_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ++ /* Reset all ccache defaults first */ ++ DLIST_FOR_EACH(ccwrap, memdb->head) { ++ if (ccwrap->cc == NULL) { ++ /* since KCM stores ccaches, better not crash.. */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n"); ++ continue; ++ } ++ ++ if (ccwrap->cc->owner.uid == uid && ccwrap->is_default == true) { ++ break; ++ } ++ } ++ ++ if (ccwrap == NULL) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "No ccache marked as default, returning null ccache\n"); ++ uuid_clear(state->dfl_uuid); ++ } else { ++ uuid_copy(state->dfl_uuid, ccwrap->cc->uuid); ++ } ++ ++ tevent_req_done(req); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_get_default_recv(struct tevent_req *req, ++ uuid_t dfl) ++{ ++ struct ccdb_mem_get_default_state *state = tevent_req_data(req, ++ struct ccdb_mem_get_default_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ uuid_copy(dfl, state->dfl_uuid); ++ return EOK; ++} ++ ++struct ccdb_mem_getbyuuid_state { ++ struct kcm_ccache *cc; ++}; ++ ++static struct tevent_req *ccdb_mem_getbyuuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_getbyuuid_state *state = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ struct ccache_mem_wrap *ccwrap = NULL; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyuuid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = memdb_get_by_uuid(memdb, client, uuid); ++ if (ccwrap != NULL) { ++ state->cc = kcm_ccache_dup(state, ccwrap->cc); ++ } ++ ++ tevent_req_done(req); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_getbyuuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct ccdb_mem_getbyuuid_state *state = tevent_req_data(req, ++ struct ccdb_mem_getbyuuid_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++struct ccdb_mem_getbyname_state { ++ struct kcm_ccache *cc; ++}; ++ ++static struct tevent_req *ccdb_mem_getbyname_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_getbyname_state *state = NULL; ++ struct ccache_mem_wrap *ccwrap = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyname_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = memdb_get_by_name(memdb, client, name); ++ if (ccwrap != NULL) { ++ state->cc = kcm_ccache_dup(state, ccwrap->cc); ++ } ++ ++ tevent_req_done(req); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_getbyname_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct ccdb_mem_getbyname_state *state = tevent_req_data(req, ++ struct ccdb_mem_getbyname_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++struct ccdb_mem_name_by_uuid_state { ++ const char *name; ++}; ++ ++struct tevent_req *ccdb_mem_name_by_uuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_name_by_uuid_state *state = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ struct ccache_mem_wrap *ccwrap = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_name_by_uuid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = memdb_get_by_uuid(memdb, client, uuid); ++ if (ccwrap == NULL) { ++ ret = ERR_KCM_CC_END; ++ goto immediate; ++ } ++ ++ state->name = talloc_strdup(state, ccwrap->cc->name); ++ if (state->name == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++errno_t ccdb_mem_name_by_uuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ const char **_name) ++{ ++ struct ccdb_mem_name_by_uuid_state *state = tevent_req_data(req, ++ struct ccdb_mem_name_by_uuid_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_name = talloc_steal(mem_ctx, state->name); ++ return EOK; ++} ++ ++struct ccdb_mem_uuid_by_name_state { ++ uuid_t uuid; ++}; ++ ++struct tevent_req *ccdb_mem_uuid_by_name_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_uuid_by_name_state *state = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ struct ccache_mem_wrap *ccwrap = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_uuid_by_name_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = memdb_get_by_name(memdb, client, name); ++ if (ccwrap == NULL) { ++ ret = ERR_KCM_CC_END; ++ goto immediate; ++ } ++ ++ uuid_copy(state->uuid, ccwrap->cc->uuid); ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++errno_t ccdb_mem_uuid_by_name_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t _uuid) ++{ ++ struct ccdb_mem_uuid_by_name_state *state = tevent_req_data(req, ++ struct ccdb_mem_uuid_by_name_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ uuid_copy(_uuid, state->uuid); ++ return EOK; ++} ++ ++static struct tevent_req *ccdb_mem_create_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ struct kcm_ccache *cc) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_dummy_state *state = NULL; ++ struct ccache_mem_wrap *ccwrap; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = talloc_zero(memdb, struct ccache_mem_wrap); ++ if (ccwrap == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ccwrap->cc = cc; ++ ccwrap->mem_be = memdb; ++ talloc_steal(ccwrap, cc); ++ ++ DLIST_ADD(memdb->head, ccwrap); ++ talloc_set_destructor((TALLOC_CTX *) ccwrap, ccwrap_destructor); ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_create_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++static struct tevent_req *ccdb_mem_mod_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct kcm_mod_ctx *mod_cc) ++{ ++ errno_t ret; ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_dummy_state *state = NULL; ++ struct ccache_mem_wrap *ccwrap = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ /* UUID is immutable, so search by that */ ++ ccwrap = memdb_get_by_uuid(memdb, client, uuid); ++ if (ccwrap == NULL) { ++ ret = ERR_KCM_CC_END; ++ goto immediate; ++ } ++ ++ kcm_mod_cc(ccwrap->cc, mod_cc); ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_mod_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++static struct tevent_req *ccdb_mem_store_cred_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_dummy_state *state = NULL; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ struct ccache_mem_wrap *ccwrap = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = memdb_get_by_uuid(memdb, client, uuid); ++ if (ccwrap == NULL) { ++ ret = ERR_KCM_CC_END; ++ goto immediate; ++ } ++ ++ ret = kcm_cc_store_cred_blob(ccwrap->cc, cred_blob); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot store credentials to ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ ret = EOK; ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_store_cred_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++static struct tevent_req *ccdb_mem_delete_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_mem_dummy_state *state = NULL; ++ struct ccache_mem_wrap *ccwrap; ++ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem); ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ ccwrap = memdb_get_by_uuid(memdb, client, uuid); ++ if (ccwrap == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "BUG: Attempting to free unknown ccache\n"); ++ ret = ERR_KCM_CC_END; ++ goto immediate; ++ } ++ ++ ret = EOK; ++ /* Destructor takes care of everything */ ++ talloc_free(ccwrap); ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_mem_delete_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++const struct kcm_ccdb_ops ccdb_mem_ops = { ++ .init = ccdb_mem_init, ++ ++ .nextid_send = ccdb_mem_nextid_send, ++ .nextid_recv = ccdb_mem_nextid_recv, ++ ++ .set_default_send = ccdb_mem_set_default_send, ++ .set_default_recv = ccdb_mem_set_default_recv, ++ ++ .get_default_send = ccdb_mem_get_default_send, ++ .get_default_recv = ccdb_mem_get_default_recv, ++ ++ .list_send = ccdb_mem_list_send, ++ .list_recv = ccdb_mem_list_recv, ++ ++ .getbyname_send = ccdb_mem_getbyname_send, ++ .getbyname_recv = ccdb_mem_getbyname_recv, ++ ++ .getbyuuid_send = ccdb_mem_getbyuuid_send, ++ .getbyuuid_recv = ccdb_mem_getbyuuid_recv, ++ ++ .name_by_uuid_send = ccdb_mem_name_by_uuid_send, ++ .name_by_uuid_recv = ccdb_mem_name_by_uuid_recv, ++ ++ .uuid_by_name_send = ccdb_mem_uuid_by_name_send, ++ .uuid_by_name_recv = ccdb_mem_uuid_by_name_recv, ++ ++ .create_send = ccdb_mem_create_send, ++ .create_recv = ccdb_mem_create_recv, ++ ++ .mod_send = ccdb_mem_mod_send, ++ .mod_recv = ccdb_mem_mod_recv, ++ ++ .store_cred_send = ccdb_mem_store_cred_send, ++ .store_cred_recv = ccdb_mem_store_cred_recv, ++ ++ .delete_send = ccdb_mem_delete_send, ++ .delete_recv = ccdb_mem_delete_recv, ++}; +-- +2.9.3 + diff --git a/SOURCES/0026-KCM-Implement-KCM-server-operations.patch b/SOURCES/0026-KCM-Implement-KCM-server-operations.patch new file mode 100644 index 0000000..56f486a --- /dev/null +++ b/SOURCES/0026-KCM-Implement-KCM-server-operations.patch @@ -0,0 +1,2326 @@ +From 5287b672cbba97a5c30ca79954101bd134a30eca Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 23 Sep 2016 14:49:09 +0200 +Subject: [PATCH 26/36] KCM: Implement KCM server operations +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implements the actual KCM server operations. On a high level, each +operation unmarhalls the needed data from the input buffer, calls into +the ccache db and marshalls a response. + +Only the operations that are also implemented by the MIT client are +implemented by our KCM server. + +Resolves: + https://pagure.io/SSSD/sssd/issue/2887 + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + Makefile.am | 2 + + src/responder/kcm/kcmsrv_cmd.c | 151 +++- + src/responder/kcm/kcmsrv_ops.c | 1954 ++++++++++++++++++++++++++++++++++++++++ + src/responder/kcm/kcmsrv_ops.h | 45 + + 4 files changed, 2143 insertions(+), 9 deletions(-) + create mode 100644 src/responder/kcm/kcmsrv_ops.c + create mode 100644 src/responder/kcm/kcmsrv_ops.h + +diff --git a/Makefile.am b/Makefile.am +index 5605c1a53c44fd9e83394e80b7f71828df1d39b6..49b4cabf9ee3ce1417f955c972376894f3709b33 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -714,6 +714,7 @@ dist_noinst_HEADERS = \ + src/responder/kcm/kcmsrv_ccache.h \ + src/responder/kcm/kcmsrv_ccache_pvt.h \ + src/responder/kcm/kcmsrv_ccache_be.h \ ++ src/responder/kcm/kcmsrv_ops.h \ + src/sbus/sbus_client.h \ + src/sbus/sssd_dbus.h \ + src/sbus/sssd_dbus_meta.h \ +@@ -1493,6 +1494,7 @@ sssd_kcm_SOURCES = \ + src/responder/kcm/kcmsrv_cmd.c \ + src/responder/kcm/kcmsrv_ccache.c \ + src/responder/kcm/kcmsrv_ccache_mem.c \ ++ src/responder/kcm/kcmsrv_ops.c \ + src/util/sss_sockets.c \ + src/util/sss_krb5.c \ + src/util/sss_iobuf.c \ +diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c +index cbf70353730d8a4e03d8f75c97395f4ef007e77f..537e88953fd1a190a9a73bcdd430d8e0db8f9291 100644 +--- a/src/responder/kcm/kcmsrv_cmd.c ++++ b/src/responder/kcm/kcmsrv_cmd.c +@@ -23,10 +23,10 @@ + + #include "config.h" + #include "util/util.h" +-#include "util/sss_iobuf.h" + #include "responder/common/responder.h" + #include "responder/kcm/kcmsrv_pvt.h" + #include "responder/kcm/kcm.h" ++#include "responder/kcm/kcmsrv_ops.h" + + /* The first four bytes of a message is always the size */ + #define KCM_MSG_LEN_SIZE 4 +@@ -133,7 +133,6 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, + { + size_t lc = 0; + size_t mc = 0; +- uint16_t opcode = 0; + uint16_t opcode_be = 0; + uint32_t len_be = 0; + uint32_t msglen; +@@ -162,7 +161,7 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, + return EBADMSG; + } + +- /* First 16 bits are 8 bit major and 8bit major protocol version */ ++ /* First 16 bits are 8 bit major and 8bit minor protocol version */ + SAFEALIGN_COPY_UINT8_CHECK(&proto_maj, + reqbuf->v_msg.kiov_base + mc, + reqbuf->v_msg.kiov_len, +@@ -191,8 +190,16 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, + reqbuf->v_msg.kiov_len, + &mc); + +- opcode = be16toh(opcode_be); +- DEBUG(SSSDBG_TRACE_LIBS, "Received operation code %"PRIu16"\n", opcode); ++ op_io->op = kcm_get_opt(be16toh(opcode_be)); ++ if (op_io->op == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Did not find a KCM operation handler for the requested opcode\n"); ++ return ERR_KCM_MALFORMED_IN_PKT; ++ } ++ ++ /* The operation only receives the payload, not the opcode or the protocol info */ ++ op_io->request.data = reqbuf->v_msg.kiov_base + mc; ++ op_io->request.length = reqbuf->v_msg.nprocessed - mc; + + return EOK; + } +@@ -240,6 +247,46 @@ static errno_t kcm_failbuf_construct(errno_t ret, + c = 0; + SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c); + ++ DEBUG(SSSDBG_TRACE_LIBS, "Sent reply with error %d\n", ret); ++ return EOK; ++} ++ ++/* retcode is 0 if the operation at least ran, non-zero if there ++ * was some kind of internal KCM error, like input couldn't be parsed ++ */ ++static errno_t kcm_output_construct(struct kcm_op_io *op_io, ++ struct kcm_repbuf *repbuf) ++{ ++ size_t c; ++ size_t replen; ++ ++ replen = sss_iobuf_get_len(op_io->reply); ++ if (replen > KCM_PACKET_MAX_SIZE) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Reply exceeds the KCM protocol limit, aborting\n"); ++ return E2BIG; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Sending a reply with %zu bytes of payload\n", replen); ++ c = 0; ++ SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, htobe32(replen), &c); ++ ++ c = 0; ++ SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, 0, &c); ++ ++ if (replen > 0) { ++ c = 0; ++ SAFEALIGN_MEMCPY_CHECK(repbuf->msgbuf, ++ sss_iobuf_get_data(op_io->reply), ++ replen, ++ repbuf->v_msg.kiov_len, ++ &c); ++ ++ /* Length of the buffer to send to KCM client */ ++ repbuf->v_msg.kiov_len = replen; ++ } ++ + return EOK; + } + +@@ -260,7 +307,8 @@ static void kcm_reply_error(struct cli_ctx *cctx, + + ret = kcm_failbuf_construct(kerr, repbuf); + if (ret != EOK) { +- /* If we can't construct the reply buffer, just terminate the client */ ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot construct the reply buffer, terminating client\n"); + talloc_free(cctx); + return; + } +@@ -268,6 +316,24 @@ static void kcm_reply_error(struct cli_ctx *cctx, + TEVENT_FD_WRITEABLE(cctx->cfde); + } + ++static void kcm_send_reply(struct cli_ctx *cctx, ++ struct kcm_op_io *op_io, ++ struct kcm_repbuf *repbuf) ++{ ++ errno_t ret; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Sending a reply\n"); ++ ret = kcm_output_construct(op_io, repbuf); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot construct the reply buffer, terminating client\n"); ++ kcm_reply_error(cctx, ret, repbuf); ++ return; ++ } ++ ++ TEVENT_FD_WRITEABLE(cctx->cfde); ++} ++ + /** + * Request-reply dispatcher + */ +@@ -285,17 +351,67 @@ struct kcm_req_ctx { + struct kcm_op_io op_io; + }; + ++static void kcm_cmd_request_done(struct tevent_req *req); ++ ++static errno_t kcm_cmd_dispatch(struct kcm_req_ctx *req_ctx) ++{ ++ struct tevent_req *req; ++ struct cli_ctx *cctx; ++ ++ cctx = req_ctx->cctx; ++ ++ req = kcm_cmd_send(req_ctx, cctx->ev, req_ctx->kctx->kcm_data, ++ req_ctx->cctx->creds, ++ &req_ctx->op_io.request, ++ req_ctx->op_io.op); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to schedule KCM operation.\n"); ++ return ENOMEM; ++ } ++ ++ tevent_req_set_callback(req, kcm_cmd_request_done, req_ctx); ++ return EOK; ++} ++ ++static void kcm_cmd_request_done(struct tevent_req *req) ++{ ++ struct kcm_req_ctx *req_ctx; ++ struct cli_ctx *cctx; ++ errno_t ret; ++ ++ req_ctx = tevent_req_callback_data(req, struct kcm_req_ctx); ++ cctx = req_ctx->cctx; ++ ++ ret = kcm_cmd_recv(req_ctx, req, ++ &req_ctx->op_io.reply); ++ talloc_free(req); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "KCM operation failed [%d]: %s\n", ret, sss_strerror(ret)); ++ kcm_reply_error(cctx, ret, &req_ctx->repbuf); ++ return; ++ } ++ ++ kcm_send_reply(cctx, &req_ctx->op_io, &req_ctx->repbuf); ++} ++ + static errno_t kcm_recv_data(int fd, struct kcm_reqbuf *reqbuf) + { + errno_t ret; + + ret = kcm_read_iovec(fd, &reqbuf->v_len); + if (ret != EOK) { ++ /* Not all errors are fatal, hence we don't print DEBUG messages ++ * here, but in the caller ++ */ + return ret; + } + + ret = kcm_read_iovec(fd, &reqbuf->v_msg); + if (ret != EOK) { ++ /* Not all errors are fatal, hence we don't print DEBUG messages ++ * here, but in the caller ++ */ + return ret; + } + +@@ -389,7 +505,15 @@ static void kcm_recv(struct cli_ctx *cctx) + /* do not read anymore, client is done sending */ + TEVENT_FD_NOT_READABLE(cctx->cfde); + +- kcm_reply_error(cctx, ret, &req->repbuf); ++ ret = kcm_cmd_dispatch(req); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Failed to dispatch KCM operation [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto fail; ++ } ++ ++ /* Dispatched request resumes in kcm_cmd_request_done */ + return; + + fail: +@@ -406,16 +530,25 @@ static int kcm_send_data(struct cli_ctx *cctx) + + ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len); + if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to write the length iovec [%d]: %s\n", ++ ret, sss_strerror(ret)); + return ret; + } + + ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc); + if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to write the retcode iovec [%d]: %s\n", ++ ret, sss_strerror(ret)); + return ret; + } + + ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg); + if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to write the msg iovec [%d]: %s\n", ++ ret, sss_strerror(ret)); + return ret; + } + +@@ -428,7 +561,7 @@ static void kcm_send(struct cli_ctx *cctx) + + ret = kcm_send_data(cctx); + if (ret == EAGAIN) { +- /* not all data was sent, loop again */ ++ DEBUG(SSSDBG_TRACE_ALL, "Sending data again..\n"); + return; + } else if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n"); +@@ -436,7 +569,7 @@ static void kcm_send(struct cli_ctx *cctx) + return; + } + +- /* ok all sent */ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "All data sent!\n"); + TEVENT_FD_NOT_WRITEABLE(cctx->cfde); + TEVENT_FD_READABLE(cctx->cfde); + talloc_zfree(cctx->state_ctx); +diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c +new file mode 100644 +index 0000000000000000000000000000000000000000..50e8cc635424e15d53e3c8d122c5525044f59c8a +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ops.c +@@ -0,0 +1,1954 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM server operations ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++ ++#include "util/sss_iobuf.h" ++#include "util/sss_krb5.h" ++#include "util/util_creds.h" ++#include "responder/kcm/kcm.h" ++#include "responder/kcm/kcmsrv_pvt.h" ++#include "responder/kcm/kcmsrv_ops.h" ++#include "responder/kcm/kcmsrv_ccache.h" ++ ++#define KCM_REPLY_MAX 2048 ++ ++struct kcm_op_ctx { ++ struct kcm_resp_ctx *kcm_data; ++ struct cli_creds *client; ++ ++ struct sss_iobuf *input; ++ struct sss_iobuf *reply; ++}; ++ ++/* Each operation follows the same pattern and is implemented using ++ * functions with this prototype. The operation receives an op_ctx ++ * that serves as a state of the operation and can be used to keep ++ * track of any temporary data. The operation writes its output data ++ * into the op_ctx reply IO buffer and returns the op_ret status code ++ * separately. ++ * ++ * The operation always returns EOK unless an internal error occurs, ++ * the result of the operation is stored in the op_ret variable ++ */ ++typedef struct tevent_req* ++(*kcm_srv_send_method)(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx); ++typedef errno_t ++(*kcm_srv_recv_method)(struct tevent_req *req, ++ uint32_t *_op_ret); ++ ++struct kcm_op { ++ const char *name; ++ kcm_srv_send_method fn_send; ++ kcm_srv_recv_method fn_recv; ++}; ++ ++struct kcm_cmd_state { ++ struct kcm_op *op; ++ ++ struct kcm_op_ctx *op_ctx; ++ struct sss_iobuf *reply; ++ ++ uint32_t op_ret; ++}; ++ ++static void kcm_cmd_done(struct tevent_req *subreq); ++ ++struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_resp_ctx *kcm_data, ++ struct cli_creds *client, ++ struct kcm_data *input, ++ struct kcm_op *op) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_cmd_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_cmd_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op = op; ++ ++ if (op == NULL) { ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "KCM operation %s\n", op->name); ++ DEBUG(SSSDBG_TRACE_LIBS, "%zu bytes on KCM input\n", input->length); ++ ++ state->reply = sss_iobuf_init_empty(state, ++ KCM_REPLY_MAX, ++ KCM_REPLY_MAX); ++ if (state->reply == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ if (op->fn_send == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "KCM op %s has no handler\n", kcm_opt_name(op)); ++ ret = ERR_KCM_OP_NOT_IMPLEMENTED; ++ goto immediate; ++ } ++ ++ /* Allocating op_ctx on the heap makes it possible for operations to use ++ * op_ctx as their temporary context and avoid tmp_ctx altogether ++ */ ++ state->op_ctx = talloc_zero(state, struct kcm_op_ctx); ++ if (state->op_ctx == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ state->op_ctx->kcm_data = kcm_data; ++ state->op_ctx->client = client; ++ ++ state->op_ctx->input = sss_iobuf_init_readonly(state->op_ctx, ++ input->data, ++ input->length); ++ if (state->op_ctx->input == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ /* ++ * The internal operation returns the opcode and the buffer separately. ++ * The KCM server reply to the client also always contains zero if the ++ * operation ran to completion, both are uint32_t. ++ * FIXME: ++ * Alternatively, we could extend iobuf API so that we can just pass ++ * the reply's buffer+sizeof(2*uint32_t) and avoid the useless allocations ++ */ ++ state->op_ctx->reply = sss_iobuf_init_empty( ++ state, ++ KCM_REPLY_MAX - 2*sizeof(uint32_t), ++ KCM_REPLY_MAX - 2*sizeof(uint32_t)); ++ if (state->reply == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ subreq = op->fn_send(state, ev, state->op_ctx); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_cmd_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_cmd_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state); ++ errno_t ret; ++ krb5_error_code kerr; ++ ++ ret = state->op->fn_recv(subreq, &state->op_ret); ++ talloc_free(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "op receive function failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "KCM operation %s returned [%d]: %s\n", ++ kcm_opt_name(state->op), state->op_ret, sss_strerror(state->op_ret)); ++ ++ kerr = sss2krb5_error(state->op_ret); ++ ++ /* The first four bytes of the reply is the operation status code */ ++ ret = sss_iobuf_write_uint32(state->reply, htobe32(kerr)); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ ret = sss_iobuf_write_len(state->reply, ++ sss_iobuf_get_data(state->op_ctx->reply), ++ sss_iobuf_get_len(state->op_ctx->reply)); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct sss_iobuf **_reply) ++{ ++ struct kcm_cmd_state *state = NULL; ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ state = tevent_req_data(req, struct kcm_cmd_state); ++ ++ *_reply = talloc_steal(mem_ctx, state->reply); ++ return EOK; ++} ++ ++/* ======= KCM operations ======= */ ++ ++/* Operations that don't return any extra information except for the op_ret ++ * can use this macro in the _recv function to avoid code duplication ++ */ ++#define KCM_OP_RET_FROM_TYPE(req, state_type, _op_ret_out) do { \ ++ state_type *state = NULL; \ ++ state = tevent_req_data(req, state_type); \ ++ TEVENT_REQ_RETURN_ON_ERROR(req); \ ++ *_op_ret_out = state->op_ret; \ ++ return EOK; \ ++} while(0); ++ ++struct kcm_op_common_state { ++ uint32_t op_ret; ++ struct kcm_op_ctx *op_ctx; ++ struct tevent_context *ev; ++}; ++ ++static errno_t kcm_op_common_recv(struct tevent_req *req, ++ uint32_t *_op_ret) ++{ ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_op_ret = state->op_ret; ++ return EOK; ++} ++ ++/* () -> (name) */ ++static void kcm_op_gen_new_done(struct tevent_req *subreq); ++ ++static struct tevent_req *kcm_op_gen_new_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ subreq = kcm_ccdb_nextid_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_gen_new_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_gen_new_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ char *newid; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ ++ ret = kcm_ccdb_nextid_recv(subreq, state, &newid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot generate a new ID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Generated a new ID %s\n", newid); ++ ++ ret = sss_iobuf_write_stringz(state->op_ctx->reply, newid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot write generated ID %d: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (princ) -> () */ ++struct kcm_op_initialize_state { ++ uint32_t op_ret; ++ struct kcm_op_ctx *op_ctx; ++ struct tevent_context *ev; ++ ++ struct kcm_ccache *new_cc; ++ const char *name; ++ krb5_principal princ; ++}; ++ ++static void kcm_op_initialize_got_byname(struct tevent_req *subreq); ++static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq); ++static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq); ++static void kcm_op_initialize_create_step(struct tevent_req *req); ++static void kcm_op_initialize_got_default(struct tevent_req *subreq); ++static void kcm_op_initialize_set_default_done(struct tevent_req *subreq); ++ ++static struct tevent_req *kcm_op_initialize_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_initialize_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_initialize_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ state->ev = ev; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &state->name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot read input name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Initializing ccache %s\n", state->name); ++ ++ ret = kcm_check_name(state->name, op_ctx->client); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Name %s is malformed [%d]: %s\n", ++ state->name, ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ ret = sss_krb5_unmarshal_princ(op_ctx, op_ctx->input, &state->princ); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot unmarshal principal [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ subreq = kcm_ccdb_getbyname_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ state->name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_initialize_got_byname, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_initialize_got_byname(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_initialize_state *state = tevent_req_data(req, ++ struct kcm_op_initialize_state); ++ bool ok; ++ uuid_t uuid; ++ ++ ret = kcm_ccdb_getbyname_recv(subreq, state, &state->new_cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (state->new_cc != NULL) { ++ ok = kcm_cc_access(state->new_cc, state->op_ctx->client); ++ if (!ok) { ++ state->op_ret = EACCES; ++ tevent_req_done(req); ++ return; ++ } ++ ++ ret = kcm_cc_get_uuid(state->new_cc, uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get new ccache UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return; ++ } ++ ++ /* Nuke any previous cache and its contents during initialization */ ++ subreq = kcm_ccdb_delete_cc_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_initialize_cc_delete_done, req); ++ return; ++ } ++ ++ kcm_op_initialize_create_step(req); ++} ++ ++static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ errno_t ret; ++ ++ ret = kcm_ccdb_delete_cc_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot delete ccache from the db %d: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ kcm_op_initialize_create_step(req); ++} ++ ++static void kcm_op_initialize_create_step(struct tevent_req *req) ++{ ++ struct tevent_req *subreq; ++ struct kcm_op_initialize_state *state = tevent_req_data(req, ++ struct kcm_op_initialize_state); ++ errno_t ret; ++ ++ ret = kcm_cc_new(state->op_ctx, ++ state->op_ctx->kcm_data->k5c, ++ state->op_ctx->client, ++ state->name, ++ state->princ, ++ &state->new_cc); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot create new ccache %d: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = kcm_ccdb_create_cc_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ state->new_cc); ++ if (subreq == NULL) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_initialize_cc_create_done, req); ++} ++ ++static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_initialize_state *state = tevent_req_data(req, ++ struct kcm_op_initialize_state); ++ errno_t ret; ++ ++ ret = kcm_ccdb_create_cc_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot add ccache to db %d: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ /* If there was no previous default ccache, set this one as default */ ++ subreq = kcm_ccdb_get_default_send(state, state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client); ++ if (subreq == NULL) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_initialize_got_default, req); ++} ++ ++static void kcm_op_initialize_got_default(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_initialize_state *state = tevent_req_data(req, ++ struct kcm_op_initialize_state); ++ errno_t ret; ++ uuid_t dfl_uuid; ++ uuid_t old_dfl_uuid; ++ ++ ret = kcm_ccdb_get_default_recv(subreq, &old_dfl_uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get default ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (uuid_is_null(old_dfl_uuid) == false) { ++ /* If there was a previous default ccache, switch to the initialized ++ * one by default ++ */ ++ ret = kcm_cc_get_uuid(state->new_cc, dfl_uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get new ccache UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return; ++ } ++ ++ subreq = kcm_ccdb_set_default_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ dfl_uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_initialize_set_default_done, req); ++ return; ++ } ++ ++ /* ENOENT, done */ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++static void kcm_op_initialize_set_default_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_initialize_state *state = tevent_req_data(req, ++ struct kcm_op_initialize_state); ++ errno_t ret; ++ ++ ret = kcm_ccdb_set_default_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++static errno_t kcm_op_initialize_recv(struct tevent_req *req, ++ uint32_t *_op_ret) ++{ ++ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_initialize_state, _op_ret); ++} ++ ++/* (name) -> () */ ++static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq); ++static void kcm_op_destroy_delete_done(struct tevent_req *subreq); ++ ++static struct tevent_req *kcm_op_destroy_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ state->ev = ev; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot unmarshall input name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Destroying credentials of %s\n", name); ++ ++ subreq = kcm_ccdb_uuid_by_name_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_destroy_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ uuid_t uuid; ++ ++ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get matching ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = kcm_ccdb_delete_cc_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_destroy_delete_done, req); ++} ++ ++static void kcm_op_destroy_delete_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ ++ ret = kcm_ccdb_delete_cc_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot delete ccache from the db [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (name, cred) -> () */ ++struct kcm_op_store_state { ++ uint32_t op_ret; ++ struct kcm_op_ctx *op_ctx; ++ struct tevent_context *ev; ++ ++ struct sss_iobuf *cred_blob; ++}; ++ ++static void kcm_op_store_getbyname_done(struct tevent_req *subreq); ++static void kcm_op_store_done(struct tevent_req *subreq); ++ ++static struct tevent_req *kcm_op_store_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_store_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ size_t creds_len; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_store_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ state->ev = ev; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot unmarshall input name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Storing credentials for %s\n", name); ++ ++ creds_len = sss_iobuf_get_size(op_ctx->input) - strlen(name) -1; ++ if (creds_len > KCM_REPLY_MAX) { ++ /* Protects against underflows and in general adds sanity */ ++ ret = E2BIG; ++ goto immediate; ++ } ++ ++ state->cred_blob = sss_iobuf_init_empty(state, ++ creds_len, ++ creds_len); ++ if (state->cred_blob == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ ret = sss_iobuf_read(op_ctx->input, ++ creds_len, ++ sss_iobuf_get_data(state->cred_blob), ++ NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot unmarshall input cred blob [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ subreq = kcm_ccdb_uuid_by_name_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_store_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_store_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_store_state *state = tevent_req_data(req, ++ struct kcm_op_store_state); ++ uuid_t uuid; ++ ++ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = kcm_ccdb_store_cred_blob_send(state, state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ uuid, ++ state->cred_blob); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_store_done, req); ++} ++ ++static void kcm_op_store_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_store_state *state = tevent_req_data(req, ++ struct kcm_op_store_state); ++ ++ ret = kcm_ccdb_store_cred_blob_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot store credentials [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++static errno_t kcm_op_store_recv(struct tevent_req *req, ++ uint32_t *_op_ret) ++{ ++ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_store_state, _op_ret); ++} ++ ++/* (name) -> (princ) */ ++static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq); ++ ++static struct tevent_req *kcm_op_get_principal_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ goto immediate; ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Requested principal %s\n", name); ++ ++ subreq = kcm_ccdb_getbyname_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_principal_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct kcm_ccache *cc; ++ krb5_principal princ; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ ++ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n"); ++ state->op_ret = ERR_NO_MATCHING_CREDS; ++ tevent_req_done(req); ++ return; ++ } ++ ++ /* Marshall the principal to the reply */ ++ princ = kcm_cc_get_client_principal(cc); ++ if (princ == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Credentials with no principal?\n"); ++ tevent_req_error(req, EIO); ++ return; ++ } ++ ++ ret = sss_krb5_marshal_princ(princ, state->op_ctx->reply); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot marshall principal [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (name) -> (uuid, ...) */ ++static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_get_cred_uuid_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ goto immediate; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Returning UUID list for %s\n", name); ++ ++ subreq = kcm_ccdb_getbyname_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_cred_uuid_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct kcm_ccache *cc; ++ struct kcm_cred *crd; ++ uuid_t uuid; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ ++ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n"); ++ state->op_ret = ERR_NO_CREDS; ++ tevent_req_done(req); ++ return; ++ } ++ ++ for (crd = kcm_cc_get_cred(cc); ++ crd != NULL; ++ crd = kcm_cc_next_cred(crd)) { ++ ret = kcm_cred_get_uuid(crd, uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Credential has no UUID, skipping\n"); ++ continue; ++ } ++ ++ kcm_debug_uuid(uuid); ++ ++ ret = sss_iobuf_write_len(state->op_ctx->reply, ++ uuid, UUID_BYTES); ++ if (ret != EOK) { ++ char uuid_errbuf[UUID_STR_SIZE]; ++ uuid_parse(uuid_errbuf, uuid); ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot marshall UUID %s [%d]: %s\n", ++ uuid_errbuf, ret, sss_strerror(ret)); ++ continue; ++ } ++ } ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (name, uuid) -> (cred) */ ++static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_get_cred_by_uuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ goto immediate; ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Returning creds by UUID for %s\n", name); ++ ++ subreq = kcm_ccdb_getbyname_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_cred_by_uuid_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ errno_t ret; ++ struct kcm_ccache *cc; ++ struct kcm_cred *crd; ++ uuid_t uuid_in; ++ uuid_t uuid; ++ struct sss_iobuf *cred_blob; ++ ++ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n"); ++ state->op_ret = ERR_NO_MATCHING_CREDS; ++ tevent_req_done(req); ++ return; ++ } ++ ++ ret = sss_iobuf_read_len(state->op_ctx->input, ++ UUID_BYTES, uuid_in); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot read input UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ for (crd = kcm_cc_get_cred(cc); ++ crd != NULL; ++ crd = kcm_cc_next_cred(crd)) { ++ ret = kcm_cred_get_uuid(crd, uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot get UUID from creds, skipping\n"); ++ continue; ++ } ++ ++ if (uuid_compare(uuid, uuid_in) == 0) { ++ break; ++ } ++ kcm_debug_uuid(uuid); ++ } ++ ++ if (crd == NULL) { ++ state->op_ret = ERR_KCM_CC_END; ++ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n"); ++ tevent_req_done(req); ++ return; ++ } ++ ++ cred_blob = kcm_cred_get_creds(crd); ++ if (cred_blob == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n"); ++ state->op_ret = ERR_NO_CREDS; ++ tevent_req_done(req); ++ return; ++ } ++ ++ ret = sss_iobuf_write_len(state->op_ctx->reply, ++ sss_iobuf_get_data(cred_blob), ++ sss_iobuf_get_size(cred_blob)); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot write ccache blob [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (name, flags, credtag) -> () */ ++/* FIXME */ ++static struct tevent_req * ++kcm_op_remove_cred_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct kcm_op_common_state *state = NULL; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ state->op_ret = ERR_KCM_OP_NOT_IMPLEMENTED; ++ tevent_req_post(req, ev); ++ tevent_req_done(req); ++ return req; ++} ++ ++/* () -> (uuid, ...) */ ++static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_get_cache_uuid_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Returning full UUID list\n"); ++ ++ subreq = kcm_ccdb_list_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_cache_uuid_list_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ errno_t ret; ++ uuid_t *uuid_list; ++ ++ ret = kcm_ccdb_list_recv(subreq, state, &uuid_list); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot list the ccache DB [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (uuid_list == NULL || uuid_list[0] == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Nothing to list\n"); ++ state->op_ret = ERR_NO_MATCHING_CREDS; ++ tevent_req_done(req); ++ return; ++ } ++ ++ for (int i = 0; ++ uuid_is_null(uuid_list[i]) == false; ++ i++) { ++ kcm_debug_uuid(uuid_list[i]); ++ ++ ret = sss_iobuf_write_len(state->op_ctx->reply, ++ uuid_list[i], ++ UUID_BYTES); ++ if (ret != EOK) { ++ char uuid_errbuf[UUID_STR_SIZE]; ++ uuid_parse(uuid_errbuf, uuid_list[i]); ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot marshall UUID %s [%d]: %s\n", ++ uuid_errbuf, ret, sss_strerror(ret)); ++ tevent_req_done(req); ++ return; ++ } ++ } ++ ++ tevent_req_done(req); ++} ++ ++/* (uuid) -> (name) */ ++static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_get_cache_by_uuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ uuid_t uuid_in; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Retrieving cache by UUID\n"); ++ ++ ret = sss_iobuf_read_len(op_ctx->input, ++ UUID_BYTES, uuid_in); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot read input UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ kcm_debug_uuid(uuid_in); ++ ++ subreq = kcm_ccdb_getbyuuid_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ uuid_in); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_cache_by_uuid_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct kcm_ccache *cc; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ const char *name; ++ ++ ret = kcm_ccdb_getbyuuid_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccahe by UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ state->op_ret = ERR_KCM_CC_END; ++ tevent_req_done(req); ++ return; ++ } ++ ++ name = kcm_cc_get_name(cc); ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %s by UUID\n", name); ++ ++ ret = sss_iobuf_write_stringz(state->op_ctx->reply, ++ name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot write output name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* () -> (name) */ ++struct kcm_op_get_default_ccache_state { ++ uint32_t op_ret; ++ struct kcm_op_ctx *op_ctx; ++ struct tevent_context *ev; ++ ++ const char *name; ++}; ++ ++static void kcm_op_get_get_default_done(struct tevent_req *subreq); ++static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq); ++static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq); ++static errno_t ++kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state); ++ ++static struct tevent_req * ++kcm_op_get_default_ccache_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_get_default_ccache_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct kcm_op_get_default_ccache_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ state->ev = ev; ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Getting client's default ccache\n"); ++ ++ subreq = kcm_ccdb_get_default_send(state, ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_get_default_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_get_default_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct kcm_op_get_default_ccache_state *state = tevent_req_data(req, ++ struct kcm_op_get_default_ccache_state); ++ errno_t ret; ++ uuid_t dfl_uuid; ++ ++ ret = kcm_ccdb_get_default_recv(subreq, &dfl_uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get default ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (uuid_is_null(dfl_uuid) == true) { ++ /* No cache marked as default -- get an existing ccache for ID ++ * and treat the default as simply the first one ++ */ ++ subreq = kcm_ccdb_list_send(state, state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_list_done, req); ++ return; ++ } ++ ++ /* Existing default */ ++ subreq = kcm_ccdb_name_by_uuid_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ dfl_uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req); ++ return; ++} ++ ++static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct kcm_op_get_default_ccache_state *state = tevent_req_data(req, ++ struct kcm_op_get_default_ccache_state); ++ errno_t ret; ++ ++ ret = kcm_ccdb_name_by_uuid_recv(subreq, state, &state->name); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccahe by UUID [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ ret = kcm_op_get_default_ccache_reply_step(state); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct kcm_op_get_default_ccache_state *state = tevent_req_data(req, ++ struct kcm_op_get_default_ccache_state); ++ errno_t ret; ++ uuid_t *uuid_list; ++ ++ ret = kcm_ccdb_list_recv(subreq, state, &uuid_list); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot list ccaches [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (uuid_list == NULL || uuid_is_null(uuid_list[0])) { ++ /* No cache at all, just send back a reply */ ++ ret = kcm_op_get_default_ccache_reply_step(state); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++ return; ++ } ++ ++ /* Otherwise resolve the first cache and use it as a default */ ++ subreq = kcm_ccdb_name_by_uuid_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ uuid_list[0]); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req); ++ return; ++} ++ ++static errno_t ++kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state) ++{ ++ errno_t ret; ++ ++ if (state->name == NULL) { ++ state->name = talloc_asprintf(state, ++ "%"SPRIuid, ++ cli_creds_get_uid(state->op_ctx->client)); ++ if (state->name == NULL) { ++ return ENOMEM; ++ } ++ } ++ DEBUG(SSSDBG_TRACE_INTERNAL, "The default ccache is %s\n", state->name); ++ ++ ret = sss_iobuf_write_stringz(state->op_ctx->reply, state->name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot write output name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ return EOK; ++} ++ ++static errno_t kcm_op_get_default_ccache_recv(struct tevent_req *req, ++ uint32_t *_op_ret) ++{ ++ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_get_default_ccache_state, _op_ret); ++} ++ ++/* (name) -> () */ ++static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq); ++static void kcm_op_set_default_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_set_default_ccache_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ state->ev = ev; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot read input name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Setting default ccache %s\n", name); ++ ++ subreq = kcm_ccdb_uuid_by_name_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_set_default_ccache_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ uuid_t dfl_uuid; ++ ++ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, dfl_uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get ccache by name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = kcm_ccdb_set_default_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ dfl_uuid); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_set_default_done, req); ++ return; ++} ++ ++static void kcm_op_set_default_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ ++ ret = kcm_ccdb_set_default_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (name) -> (offset) */ ++static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_get_kdc_offset_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_common_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot read input name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Requested offset for principal %s\n", name); ++ ++ subreq = kcm_ccdb_getbyname_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_get_kdc_offset_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct kcm_ccache *cc; ++ int32_t offset; ++ int32_t offset_be; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_common_state *state = tevent_req_data(req, ++ struct kcm_op_common_state); ++ ++ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get matching ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "No matching credentials\n"); ++ state->op_ret = ERR_NO_MATCHING_CREDS; ++ tevent_req_done(req); ++ return; ++ } ++ ++ offset = kcm_cc_get_offset(cc); ++ DEBUG(SSSDBG_TRACE_LIBS, "KDC offset: %"PRIu32"\n", offset); ++ ++ offset_be = htobe32(offset); ++ ret = sss_iobuf_write_int32(state->op_ctx->reply, offset_be); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot write KDC offset [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++/* (name, offset) -> () */ ++/* () -> (name) */ ++struct kcm_op_set_kdc_offset_state { ++ uint32_t op_ret; ++ struct kcm_op_ctx *op_ctx; ++ struct tevent_context *ev; ++}; ++ ++static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq); ++static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++kcm_op_set_kdc_offset_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_op_ctx *op_ctx) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct kcm_op_set_kdc_offset_state *state = NULL; ++ errno_t ret; ++ const char *name; ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_set_kdc_offset_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->op_ctx = op_ctx; ++ state->ev = ev; ++ ++ ret = sss_iobuf_read_stringz(op_ctx->input, &name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot read input name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Setting offset for principal %s\n", name); ++ ++ subreq = kcm_ccdb_uuid_by_name_send(state, ev, ++ op_ctx->kcm_data->db, ++ op_ctx->client, ++ name); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct kcm_mod_ctx *mod_ctx; ++ int32_t offset_be; ++ uuid_t uuid; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req, ++ struct kcm_op_set_kdc_offset_state); ++ ++ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get matching ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ ret = sss_iobuf_read_int32(state->op_ctx->input, &offset_be); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot read KDC offset [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ mod_ctx = talloc(state, struct kcm_mod_ctx); ++ if (mod_ctx == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ kcm_mod_ctx_clear(mod_ctx); ++ mod_ctx->kdc_offset = be32toh(offset_be); ++ ++ subreq = kcm_ccdb_mod_cc_send(state, ++ state->ev, ++ state->op_ctx->kcm_data->db, ++ state->op_ctx->client, ++ uuid, ++ mod_ctx); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_mod_done, req); ++} ++ ++static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req, ++ struct kcm_op_set_kdc_offset_state); ++ ++ ret = kcm_ccdb_mod_cc_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot modify ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ state->op_ret = EOK; ++ tevent_req_done(req); ++} ++ ++static errno_t kcm_op_set_kdc_offset_recv(struct tevent_req *req, ++ uint32_t *_op_ret) ++{ ++ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_kdc_offset_state, _op_ret); ++} ++ ++static struct kcm_op kcm_optable[] = { ++ { "NOOP", NULL, NULL }, ++ { "GET_NAME", NULL, NULL }, ++ { "RESOLVE", NULL, NULL }, ++ { "GEN_NEW", kcm_op_gen_new_send, NULL }, ++ { "INITIALIZE", kcm_op_initialize_send, kcm_op_initialize_recv }, ++ { "DESTROY", kcm_op_destroy_send, NULL }, ++ { "STORE", kcm_op_store_send, kcm_op_store_recv }, ++ { "RETRIEVE", NULL, NULL }, ++ { "GET_PRINCIPAL", kcm_op_get_principal_send, NULL }, ++ { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list_send, NULL }, ++ { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid_send, NULL }, ++ { "REMOVE_CRED", kcm_op_remove_cred_send, NULL }, ++ { "SET_FLAGS", NULL, NULL }, ++ { "CHOWN", NULL, NULL }, ++ { "CHMOD", NULL, NULL }, ++ { "GET_INITIAL_TICKET", NULL, NULL }, ++ { "GET_TICKET", NULL, NULL }, ++ { "MOVE_CACHE", NULL, NULL }, ++ { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list_send, NULL }, ++ { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid_send, NULL }, ++ { "GET_DEFAULT_CACHE", kcm_op_get_default_ccache_send, kcm_op_get_default_ccache_recv }, ++ { "SET_DEFAULT_CACHE", kcm_op_set_default_ccache_send, NULL }, ++ { "GET_KDC_OFFSET", kcm_op_get_kdc_offset_send, NULL }, ++ { "SET_KDC_OFFSET", kcm_op_set_kdc_offset_send, kcm_op_set_kdc_offset_recv }, ++ { "ADD_NTLM_CRED", NULL, NULL }, ++ { "HAVE_NTLM_CRED", NULL, NULL }, ++ { "DEL_NTLM_CRED", NULL, NULL }, ++ { "DO_NTLM_AUTH", NULL, NULL }, ++ { "GET_NTLM_USER_LIST", NULL, NULL }, ++ ++ { NULL, NULL, NULL } ++}; ++ ++struct kcm_op *kcm_get_opt(uint16_t opcode) ++{ ++ struct kcm_op *op; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "The client requested operation %"PRIu16"\n", opcode); ++ ++ if (opcode >= KCM_OP_SENTINEL) { ++ return NULL; ++ } ++ ++ op = &kcm_optable[opcode]; ++ if (op->fn_recv == NULL) { ++ op->fn_recv = kcm_op_common_recv; ++ } ++ return op; ++} ++ ++const char *kcm_opt_name(struct kcm_op *op) ++{ ++ if (op == NULL || op->name == NULL) { ++ return "Unknown operation"; ++ } ++ ++ return op->name; ++} +diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h +new file mode 100644 +index 0000000000000000000000000000000000000000..8e6feaf56a10b73c8b6375aea9ef26c392b5b492 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ops.h +@@ -0,0 +1,45 @@ ++/* ++ SSSD ++ ++ KCM Server - private header file ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef __KCMSRV_OPS_H__ ++#define __KCMSRV_OPS_H__ ++ ++#include "config.h" ++ ++#include ++#include "util/sss_iobuf.h" ++#include "responder/kcm/kcmsrv_pvt.h" ++ ++struct kcm_op; ++struct kcm_op *kcm_get_opt(uint16_t opcode); ++const char *kcm_opt_name(struct kcm_op *op); ++ ++struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_resp_ctx *kcm_data, ++ struct cli_creds *client, ++ struct kcm_data *input, ++ struct kcm_op *op); ++errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct sss_iobuf **_reply); ++ ++#endif /* __KCMSRV_OPS_H__ */ +-- +2.9.3 + diff --git a/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch b/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch new file mode 100644 index 0000000..39a1629 --- /dev/null +++ b/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch @@ -0,0 +1,278 @@ +From 14d42e26c2050c1941874e83761fae69585ddc27 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 8 Mar 2017 17:46:09 +0100 +Subject: [PATCH 27/36] MAN: Add a manual page for sssd-kcm +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + contrib/sssd.spec.in | 1 + + src/man/Makefile.am | 9 ++- + src/man/po/po4a.cfg | 1 + + src/man/sssd-kcm.8.xml | 193 +++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 203 insertions(+), 1 deletion(-) + create mode 100644 src/man/sssd-kcm.8.xml + +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 52d33b4de281dc1d91a9027ac1c8c878e66fb396..1d4d020415ee28292bb4d88c78de205465d812f1 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -1206,6 +1206,7 @@ done + %config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache + %{_unitdir}/sssd-kcm.socket + %{_unitdir}/sssd-kcm.service ++%{_mandir}/man8/sssd-kcm.8* + %endif + + %pre common +diff --git a/src/man/Makefile.am b/src/man/Makefile.am +index 142d6e2743f814294e3d92c8342070b8230bb3e5..3a063614f085691652db32d76315375466e0d3de 100644 +--- a/src/man/Makefile.am ++++ b/src/man/Makefile.am +@@ -27,6 +27,9 @@ endif + if BUILD_SECRETS + SEC_CONDS = ;with_secrets + endif ++if BUILD_SECRETS ++KCM_CONDS = ;with_kcm ++endif + if GPO_DEFAULT_ENFORCING + GPO_CONDS = ;gpo_default_enforcing + else +@@ -40,7 +43,7 @@ FILES_CONDS = ;enable_files_domain + else + FILES_CONDS = ;no_enable_files_domain + endif +-CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS)$(FILES_CONDS) ++CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS)$(FILES_CONDS)$(KCM_CONDS) + + + #Special Rules: +@@ -85,6 +88,10 @@ if BUILD_SECRETS + man_MANS += sssd-secrets.5 + endif + ++if BUILD_KCM ++man_MANS += sssd-kcm.8 ++endif ++ + if BUILD_NFS_IDMAP + man_MANS += sss_rpcidmapd.5 + endif +diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg +index d1f6ac39f841c61ae3d2393fb3402dc21b9cbd69..a02f97e777fa76615e4d5cbcfc788956706d8cd0 100644 +--- a/src/man/po/po4a.cfg ++++ b/src/man/po/po4a.cfg +@@ -31,6 +31,7 @@ + [type:docbook] sssctl.8.xml $lang:$(builddir)/$lang/sssctl.8.xml + [type:docbook] sssd-files.5.xml $lang:$(builddir)/$lang/sssd-files.5.xml + [type:docbook] sssd-secrets.5.xml $lang:$(builddir)/$lang/sssd-secrets.5.xml ++[type:docbook] sssd-kcm.8.xml $lang:$(builddir)/$lang/sssd-kcm.8.xml + [type:docbook] include/service_discovery.xml $lang:$(builddir)/$lang/include/service_discovery.xml opt:"-k 0" + [type:docbook] include/upstream.xml $lang:$(builddir)/$lang/include/upstream.xml opt:"-k 0" + [type:docbook] include/failover.xml $lang:$(builddir)/$lang/include/failover.xml opt:"-k 0" +diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml +new file mode 100644 +index 0000000000000000000000000000000000000000..5dc93838e48723bdb470c0a9c8575bd17c7593e8 +--- /dev/null ++++ b/src/man/sssd-kcm.8.xml +@@ -0,0 +1,193 @@ ++ ++ ++ ++SSSD Manual pages ++ ++ ++ ++ ++ sssd-kcm ++ 8 ++ File Formats and Conventions ++ ++ ++ ++ sssd-kcm ++ SSSD Kerberos Cache Manager ++ ++ ++ ++ DESCRIPTION ++ ++ This manual page describes the configuration of the SSSD Kerberos ++ Cache Manager (KCM). KCM is a process that stores, tracks and ++ manages Kerberos credential caches. It originates in the Heimdal ++ Kerberos project, although the MIT Kerberos library also provides ++ client side (more details on that below) support for the KCM ++ credential cache. ++ ++ ++ In a setup where Kerberos caches are managed by KCM, the ++ Kerberos library (typically used through an application, like ++ e.g., ++ ++ kinit1 ++ , ++ is a "KCM client" and the KCM daemon ++ is being referred to as a "KCM server". The client ++ and server communicate over a UNIX socket. ++ ++ ++ The KCM server keeps track of each credential caches's owner and ++ performs access check control based on the UID and GID of the ++ KCM client. The root user has access to all credential caches. ++ ++ ++ The KCM credential cache has several interesting properties: ++ ++ ++ ++ since the process runs in userspace, it is subject to UID namespacing, ulike the kernel keyring ++ ++ ++ ++ ++ unlike the kernel keyring-based cache, which is shared between all containers, the KCM server is a separate process whose entry point is a UNIX socket ++ ++ ++ ++ ++ the SSSD implementation stores the ccaches in the SSSD ++ ++ sssd-secrets5 ++ ++ secrets store, allowing the ccaches to survive KCM server restarts or machine reboots. ++ ++ ++ ++ This allows the system to use a collection-aware credential ++ cache, yet share the credential cache between some or no ++ containers by bind-mounting the socket. ++ ++ ++ ++ ++ USING THE KCM CREDENTIAL CACHE ++ ++ In order to use KCM credential cache, it must be selected as the default ++ credential type in ++ ++ krb5.conf5 ++ , ++ The credentials cache name must be only KCM: ++ without any template expansions. For example: ++ ++[libdefaults] ++ default_ccache_name = KCM: ++ ++ ++ ++ Next, make sure the Kerberos client libraries and the KCM server must agree ++ on the UNIX socket path. By default, both use the same path ++ /var/run/.heim_org.h5l.kcm-socket. To configure ++ the Kerberos library, change its kcm_socket option which ++ is described in the ++ ++ krb5.conf5 ++ ++ manual page. ++ ++ ++ Finally, make sure the SSSD KCM server can be contacted. ++ The KCM service is typically socket-activated by ++ ++ systemd ++ 1 ++ . ++ Unlike ++ other SSSD services, it cannot be started by adding the ++ kcm string to the service ++ directive. ++ ++systemctl start sssd-kcm.socket ++systemctl enable sssd-kcm.socket ++systemctl enable sssd-kcm.service ++ ++ Please note your distribution may already configure the units ++ for you. ++ ++ ++ ++ ++ THE CREDENTIAL CACHE STORAGE ++ ++ The credential caches are stored in the SSSD secrets service (see ++ ++ sssd-secrets5 ++ ++ for more details). Therefore it is important that also the sssd-secrets ++ service is enabled and its socket is started: ++ ++systemctl start sssd-secrets.socket ++systemctl enable sssd-secrets.socket ++systemctl enable sssd-secrets.service ++ ++ Your distribution should already set the dependencies between the services. ++ ++ ++ ++ ++ CONFIGURATION OPTIONS ++ ++ The KCM service is configured in the kcm ++ section of the sssd.conf file. Please note that currently, ++ is it not sufficient to restart the sssd-kcm service, because ++ the sssd configuration is only parsed and read to an internal ++ configuration database by the sssd service. Therefore you ++ must restart the sssd service if you change anything in the ++ kcm section of sssd.conf. ++ For a detailed syntax reference, refer to the FILE FORMAT section of the ++ ++ sssd.conf ++ 5 ++ manual page. ++ ++ ++ The generic SSSD service options such as ++ debug_level or fd_limit are ++ accepted by the kcm service. Please refer to the ++ ++ sssd.conf ++ 5 ++ manual page for a complete list. In addition, ++ there are some KCM-specific options as well. ++ ++ ++ ++ socket_path (string) ++ ++ ++ The socket the KCM service will listen on. ++ ++ ++ Default: /var/run/.heim_org.h5l.kcm-socket ++ ++ ++ ++ ++ ++ ++ ++ SEE ALSO ++ ++ ++ sssd8 ++ , ++ ++ sssd.conf5 ++ , ++ ++ ++ ++ +-- +2.9.3 + diff --git a/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch b/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch new file mode 100644 index 0000000..776bf94 --- /dev/null +++ b/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch @@ -0,0 +1,799 @@ +From ad820beebae89c886f1ba4f0d2ddac4ca36857b7 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 13 Dec 2016 17:17:16 +0100 +Subject: [PATCH 28/36] TESTS: Add integration tests for the KCM responder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +Reviewed-by: Lukáš Slebodník +--- + contrib/ci/configure.sh | 7 + + contrib/ci/deps.sh | 6 + + src/tests/intg/Makefile.am | 4 + + src/tests/intg/kdc.py | 175 +++++++++++++++++++++ + src/tests/intg/krb5utils.py | 156 +++++++++++++++++++ + src/tests/intg/test_kcm.py | 361 ++++++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 709 insertions(+) + create mode 100644 src/tests/intg/kdc.py + create mode 100644 src/tests/intg/krb5utils.py + create mode 100644 src/tests/intg/test_kcm.py + +diff --git a/contrib/ci/configure.sh b/contrib/ci/configure.sh +index 8e779cfe634a7555e0e8e3b52f42c07e62980fbc..7590743c2aa5fe31bcdf1a3e92a3f482dbec699b 100644 +--- a/contrib/ci/configure.sh ++++ b/contrib/ci/configure.sh +@@ -38,6 +38,13 @@ if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-6.*- || + "--disable-cifs-idmap-plugin" + "--with-syslog=syslog" + "--without-python3-bindings" ++ "--without-kcm" ++ ) ++fi ++ ++if [[ "$DISTRO_BRANCH" == -redhat-fedora-2[0-2]* ]]; then ++ CONFIGURE_ARG_LIST+=( ++ "--without-kcm" + ) + fi + +diff --git a/contrib/ci/deps.sh b/contrib/ci/deps.sh +index c525e62e8c1d5b9fa042dee4ad03790dbceba242..4467e117c3a896a7f01ef7cb9e94fe28c2ea2838 100644 +--- a/contrib/ci/deps.sh ++++ b/contrib/ci/deps.sh +@@ -47,6 +47,8 @@ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then + uid_wrapper + python-requests + curl-devel ++ krb5-server ++ krb5-workstation + ) + _DEPS_LIST_SPEC=` + sed -e 's/@PACKAGE_VERSION@/0/g' \ +@@ -122,6 +124,10 @@ if [[ "$DISTRO_BRANCH" == -debian-* ]]; then + libhttp-parser-dev + libjansson-dev + libcurl4-openssl-dev ++ krb5-kdc ++ krb5-admin-server ++ krb5-user ++ uuid-dev + ) + DEPS_INTGCHECK_SATISFIED=true + fi +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 1d36fa0d2d50307fbc871f5b2a6f0cb1cc95db81..8526beace09b15c99aa27ac98d5038d1980f6a71 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -26,6 +26,9 @@ dist_noinst_DATA = \ + files_ops.py \ + test_files_ops.py \ + test_files_provider.py \ ++ kdc.py \ ++ krb5utils.py \ ++ test_kcm.py \ + $(NULL) + + config.py: config.py.m4 +@@ -80,5 +83,6 @@ intgcheck-installed: config.py passwd group + NSS_WRAPPER_MODULE_FN_PREFIX="sss" \ + UID_WRAPPER=1 \ + UID_WRAPPER_ROOT=1 \ ++ NON_WRAPPED_UID=$$(echo $$UID) \ + fakeroot $(PYTHON2) $(PYTEST) -v --tb=native $(INTGCHECK_PYTEST_ARGS) . + rm -f $(DESTDIR)$(logpath)/* +diff --git a/src/tests/intg/kdc.py b/src/tests/intg/kdc.py +new file mode 100644 +index 0000000000000000000000000000000000000000..dec33a979916c0979561afb22dc39d6eb8894ff3 +--- /dev/null ++++ b/src/tests/intg/kdc.py +@@ -0,0 +1,175 @@ ++# ++# MIT Kerberos server class ++# ++# Copyright (c) 2016 Red Hat, Inc. ++# ++# This is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 only ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++import os ++import signal ++import shutil ++import subprocess ++ ++from util import * ++ ++ ++class KDC(object): ++ """ ++ MIT Kerberos KDC instance ++ """ ++ ++ def __init__(self, basedir, realm, ++ includedir=None, ++ kdc_port=10088, ++ kadmin_port=10749, ++ master_key='master'): ++ self.basedir = basedir ++ self.realm = realm ++ self.kdc_port = kdc_port ++ self.kadmin_port = kadmin_port ++ self.master_key = master_key ++ ++ self.kdc_basedir = self.basedir + "/var/krb5kdc" ++ self.includedir = includedir or (self.kdc_basedir + "/include") ++ self.kdc_logdir = self.kdc_basedir + "/log" ++ self.kdc_conf_path = self.kdc_basedir + "/kdc.conf" ++ self.krb5_conf_path = self.kdc_basedir + "/krb5.conf" ++ ++ self.kdc_pid_file = self.kdc_basedir + "/kdc.pid" ++ ++ self.acl_file = self.kdc_basedir + "/kadm5.acl" ++ ++ self.admin_princ = "admin/admin@" + self.realm ++ ++ def start_kdc(self, extra_args=[]): ++ args = ["krb5kdc", '-P', self.kdc_pid_file] + extra_args ++ return self._run_in_env(args, self.get_krb5_env()) ++ ++ def stop_kdc(self): ++ try: ++ with open(self.kdc_pid_file, "r") as pid_file: ++ os.kill(int(pid_file.read()), signal.SIGTERM) ++ except IOError as ioex: ++ if ioex.errno == 2: ++ pass ++ else: ++ raise ioex ++ ++ def teardown(self): ++ self.stop_kdc() ++ shutil.rmtree(self.kdc_basedir) ++ ++ def set_up(self): ++ self._create_config() ++ self._create_acl() ++ self._create_kdb() ++ ++ def get_krb5_env(self): ++ my_env = os.environ ++ my_env['KRB5_CONFIG'] = self.krb5_conf_path ++ my_env['KRB5_KDC_PROFILE'] = self.kdc_conf_path ++ return my_env ++ ++ def add_config(self, include_files): ++ for name, contents in include_files.items(): ++ include_fpath = os.path.join(self.includedir, name) ++ with open(include_fpath, 'w') as include_file: ++ include_file.write(contents) ++ ++ def add_principal(self, princ, password=None): ++ args = ["kadmin.local", "-q"] ++ if password is None: ++ args += ["addprinc -randkey %s" % (princ)] ++ else: ++ args += ["addprinc -pw %s %s" % (password, princ)] ++ return self._run_in_env(args, self.get_krb5_env()) ++ ++ def _run_in_env(self, args, env): ++ cmd = subprocess.Popen(args, env=env) ++ out, err = cmd.communicate() ++ return cmd.returncode, out, err ++ ++ def _create_config(self): ++ try: ++ os.makedirs(self.kdc_basedir) ++ os.makedirs(self.kdc_logdir) ++ os.makedirs(self.includedir) ++ except OSError as osex: ++ if osex.errno == 17: ++ pass ++ ++ kdc_conf = self._format_kdc_conf() ++ with open(self.kdc_conf_path, 'w') as kdc_conf_file: ++ kdc_conf_file.write(kdc_conf) ++ ++ krb5_conf = self._format_krb5_conf() ++ with open(self.krb5_conf_path, 'w') as krb5_conf_file: ++ krb5_conf_file.write(krb5_conf) ++ ++ def _create_acl(self): ++ with open(self.acl_file, 'w') as acl_fobject: ++ acl_fobject.write(self.admin_princ) ++ ++ def _create_kdb(self): ++ self._run_in_env( ++ ['kdb5_util', 'create', '-W', '-s', '-P', self.master_key], ++ self.get_krb5_env() ++ ) ++ ++ def _format_kdc_conf(self): ++ database_path = self.kdc_basedir + "/principal" ++ key_stash = self.kdc_basedir + "/stash." + self.realm ++ ++ kdc_logfile = "FILE:" + self.kdc_logdir + "/krb5kdc.log" ++ kadmin_logfile = "FILE:" + self.kdc_logdir + "/kadmin.log" ++ libkrb5_logfile = "FILE:" + self.kdc_logdir + "/libkrb5.log" ++ ++ kdc_conf = unindent(""" ++ [kdcdefaults] ++ kdc_ports = {self.kdc_port} ++ kdc_tcp_ports = {self.kdc_port} ++ ++ [realms] ++ {self.realm} = {{ ++ kadmind_port = {self.kadmin_port} ++ database_name = {database_path} ++ key_stash_file = {key_stash} ++ acl_file = {self.acl_file} ++ }} ++ ++ [logging] ++ kdc = {kdc_logfile} ++ admin_server = {kadmin_logfile} ++ default = {libkrb5_logfile} ++ """).format(**locals()) ++ return kdc_conf ++ ++ def _format_krb5_conf(self): ++ kdc_uri = "localhost:%d" % self.kdc_port ++ kadmin_uri = "localhost:%d" % self.kadmin_port ++ ++ krb5_conf = unindent(""" ++ includedir {self.includedir} ++ ++ [libdefaults] ++ default_realm = {self.realm} ++ dns_lookup_kdc = false ++ dns_lookup_realm = false ++ ++ [realms] ++ {self.realm} = {{ ++ kdc = {kdc_uri} ++ admin_server = {kadmin_uri} ++ }} ++ """).format(**locals()) ++ return krb5_conf +diff --git a/src/tests/intg/krb5utils.py b/src/tests/intg/krb5utils.py +new file mode 100644 +index 0000000000000000000000000000000000000000..775cffd0bbfa011f2d8ffc1169dccfef96d78fab +--- /dev/null ++++ b/src/tests/intg/krb5utils.py +@@ -0,0 +1,156 @@ ++# ++# MIT Kerberos server class ++# ++# Copyright (c) 2016 Red Hat, Inc. ++# ++# This is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 only ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++import os ++import subprocess ++ ++ ++class NoPrincipals(Exception): ++ def __init__(self): ++ Exception.__init__(self, 'No principals in the collection') ++ ++ ++class PrincNotFound(Exception): ++ def __init__(self, principal): ++ Exception.__init__(self, 'Principal %s not found' % principal) ++ ++ ++class Krb5Utils(object): ++ """ ++ Helper class to test Kerberos command line utilities ++ """ ++ def __init__(self, krb5_conf_path): ++ self.krb5_conf_path = krb5_conf_path ++ ++ def _run_in_env(self, args, stdin=None, extra_env=None): ++ my_env = os.environ ++ my_env['KRB5_CONFIG'] = self.krb5_conf_path ++ ++ if 'KRB5CCNAME' in my_env: ++ del my_env['KRB5CCNAME'] ++ if extra_env is not None: ++ my_env.update(extra_env) ++ ++ cmd = subprocess.Popen(args, ++ env=my_env, ++ stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE) ++ out, err = cmd.communicate(stdin) ++ return cmd.returncode, out.decode('utf-8'), err.decode('utf-8') ++ ++ def kinit(self, principal, password, env=None): ++ args = ["kinit", principal] ++ return self._run_in_env(args, password.encode('utf-8'), env) ++ ++ def kvno(self, principal, env=None): ++ args = ["kvno", principal] ++ return self._run_in_env(args, env) ++ ++ def kdestroy(self, all_ccaches=False, env=None): ++ args = ["kdestroy"] ++ if all_ccaches is True: ++ args += ["-A"] ++ retval, _, _ = self._run_in_env(args, env) ++ return retval ++ ++ def kswitch(self, principal, env=None): ++ args = ["kswitch", '-p', principal] ++ retval, _, _ = self._run_in_env(args, env) ++ return retval ++ ++ def _check_klist_l(self, line, exp_principal, exp_cache): ++ try: ++ princ, cache = line.split() ++ except ValueError: ++ return False ++ ++ if exp_cache is not None and cache != exp_cache: ++ return False ++ ++ if exp_principal != princ: ++ return False ++ ++ return True ++ ++ def num_princs(self, env=None): ++ args = ["klist", "-l"] ++ retval, out, err = self._run_in_env(args, extra_env=env) ++ if retval != 0: ++ return 0 ++ ++ outlines = [l for l in out.split('\n') if len(l) > 1] ++ return len(outlines) - 2 ++ ++ def list_princs(self, env=None): ++ args = ["klist", "-l"] ++ retval, out, err = self._run_in_env(args, extra_env=env) ++ if retval == 1: ++ raise NoPrincipals ++ elif retval != 0: ++ raise Exception("klist failed: %d: %s\n", retval, err) ++ ++ outlines = out.split('\n') ++ if len(outlines) < 2: ++ raise Exception("Not enough output from klist -l") ++ ++ return [l for l in outlines[2:] if len(l) > 0] ++ ++ def has_principal(self, exp_principal, exp_cache=None, env=None): ++ try: ++ princlist = self.list_princs(env) ++ except NoPrincipals: ++ return False ++ ++ for line in princlist: ++ matches = self._check_klist_l(line, exp_principal, exp_cache) ++ if matches is True: ++ return True ++ ++ return False ++ ++ def default_principal(self, env=None): ++ principals = self.list_princs(env) ++ return principals[0].split()[0] ++ ++ def _parse_klist_a(self, out): ++ dflprinc = None ++ thisrealm = None ++ ccache_dict = dict() ++ ++ for line in [l for l in out.split('\n') if len(l) > 0]: ++ if line.startswith("Default principal"): ++ dflprinc = line.split()[2] ++ thisrealm = '@' + dflprinc.split('@')[1] ++ elif thisrealm is not None and line.endswith(thisrealm): ++ svc = line.split()[-1] ++ if dflprinc in ccache_dict: ++ ccache_dict[dflprinc].append(svc) ++ else: ++ ccache_dict[dflprinc] = [svc] ++ ++ return ccache_dict ++ ++ def list_all_princs(self, env=None): ++ args = ["klist", "-A"] ++ retval, out, err = self._run_in_env(args, extra_env=env) ++ if retval == 1: ++ raise NoPrincipals ++ elif retval != 0: ++ raise Exception("klist -A failed: %d: %s\n", retval, err) ++ ++ return self._parse_klist_a(out) +diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py +new file mode 100644 +index 0000000000000000000000000000000000000000..ad1e4923bfe339cb040464757431d2ef3bf57ce1 +--- /dev/null ++++ b/src/tests/intg/test_kcm.py +@@ -0,0 +1,361 @@ ++# ++# KCM responder integration tests ++# ++# Copyright (c) 2016 Red Hat, Inc. ++# ++# This is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 only ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++import os ++import os.path ++import stat ++import subprocess ++import pytest ++import socket ++import time ++import signal ++ ++import kdc ++import krb5utils ++import config ++from util import unindent, run_shell ++ ++class KcmTestEnv(object): ++ def __init__(self, k5kdc, k5util): ++ self.k5kdc = k5kdc ++ self.k5util = k5util ++ self.counter = 0 ++ ++ def my_uid(self): ++ s_myuid = os.environ['NON_WRAPPED_UID'] ++ return int(s_myuid) ++ ++ def ccname(self, my_uid=None): ++ if my_uid is None: ++ my_uid = self.my_uid() ++ ++ return "KCM:%d" % my_uid ++ ++ ++@pytest.fixture(scope="module") ++def kdc_instance(request): ++ """Kerberos server instance fixture""" ++ kdc_instance = kdc.KDC(config.PREFIX, "KCMTEST") ++ try: ++ kdc_instance.set_up() ++ kdc_instance.start_kdc() ++ except: ++ kdc_instance.teardown() ++ raise ++ request.addfinalizer(kdc_instance.teardown) ++ return kdc_instance ++ ++ ++def create_conf_fixture(request, contents): ++ """Generate sssd.conf and add teardown for removing it""" ++ with open(config.CONF_PATH, "w") as conf: ++ conf.write(contents) ++ os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR) ++ request.addfinalizer(lambda: os.unlink(config.CONF_PATH)) ++ ++ ++def create_sssd_kcm_fixture(sock_path, request): ++ if subprocess.call(['sssd', "--genconf"]) != 0: ++ raise Exception("failed to regenerate confdb") ++ ++ resp_path = os.path.join(config.LIBEXEC_PATH, "sssd", "sssd_kcm") ++ if not os.access(resp_path, os.X_OK): ++ # It would be cleaner to use pytest.mark.skipif on the package level ++ # but upstream insists on supporting RHEL-6.. ++ pytest.skip("No KCM responder, skipping") ++ ++ kcm_pid = os.fork() ++ assert kcm_pid >= 0 ++ ++ if kcm_pid == 0: ++ if subprocess.call([resp_path, "--uid=0", "--gid=0"]) != 0: ++ print("sssd_kcm failed to start") ++ sys.exit(99) ++ else: ++ abs_sock_path = os.path.join(config.RUNSTATEDIR, sock_path) ++ sck = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) ++ for _ in range(1, 10): ++ try: ++ sck.connect(abs_sock_path) ++ except: ++ time.sleep(0.1) ++ else: ++ break ++ sck.close() ++ assert os.path.exists(abs_sock_path) ++ ++ def kcm_teardown(): ++ if kcm_pid == 0: ++ return ++ os.kill(kcm_pid, signal.SIGTERM) ++ ++ request.addfinalizer(kcm_teardown) ++ return kcm_pid ++ ++ ++@pytest.fixture ++def setup_for_kcm(request, kdc_instance): ++ """ ++ Just set up the local provider for tests and enable the KCM ++ responder ++ """ ++ kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket") ++ ++ sssd_conf = unindent("""\ ++ [sssd] ++ domains = local ++ services = nss ++ ++ [domain/local] ++ id_provider = local ++ ++ [kcm] ++ socket_path = {kcm_path} ++ """).format(**locals()) ++ ++ kcm_socket_include = unindent(""" ++ [libdefaults] ++ default_ccache_name = KCM: ++ kcm_socket = {kcm_path} ++ """).format(**locals()) ++ kdc_instance.add_config({'kcm_socket': kcm_socket_include}) ++ ++ create_conf_fixture(request, sssd_conf) ++ create_sssd_kcm_fixture(kcm_path, request) ++ ++ k5util = krb5utils.Krb5Utils(kdc_instance.krb5_conf_path) ++ ++ return KcmTestEnv(kdc_instance, k5util) ++ ++ ++def test_kcm_init_list_destroy(setup_for_kcm): ++ """ ++ Test that kinit, kdestroy and klist work with KCM ++ """ ++ testenv = setup_for_kcm ++ testenv.k5kdc.add_principal("kcmtest", "Secret123") ++ ++ ok = testenv.k5util.has_principal("kcmtest@KCMTEST") ++ assert ok is False ++ nprincs = testenv.k5util.num_princs() ++ assert nprincs == 0 ++ ++ out, _, _ = testenv.k5util.kinit("kcmtest", "Secret123") ++ assert out == 0 ++ nprincs = testenv.k5util.num_princs() ++ assert nprincs == 1 ++ ++ exp_ccname = testenv.ccname() ++ ok = testenv.k5util.has_principal("kcmtest@KCMTEST", exp_ccname) ++ assert ok is True ++ ++ out = testenv.k5util.kdestroy() ++ assert out == 0 ++ ++ ok = testenv.k5util.has_principal("kcmtest@KCMTEST") ++ assert ok is False ++ nprincs = testenv.k5util.num_princs() ++ assert nprincs == 0 ++ ++ ++def test_kcm_overwrite(setup_for_kcm): ++ """ ++ That that reusing a ccache reinitializes the cache and doesn't ++ add the same principal twice ++ """ ++ testenv = setup_for_kcm ++ testenv.k5kdc.add_principal("kcmtest", "Secret123") ++ exp_ccache = {'kcmtest@KCMTEST': ['krbtgt/KCMTEST@KCMTEST']} ++ ++ assert testenv.k5util.num_princs() == 0 ++ ++ out, _, _ = testenv.k5util.kinit("kcmtest", "Secret123") ++ assert out == 0 ++ assert exp_ccache == testenv.k5util.list_all_princs() ++ ++ out, _, _ = testenv.k5util.kinit("kcmtest", "Secret123") ++ assert out == 0 ++ assert exp_ccache == testenv.k5util.list_all_princs() ++ ++ ++def test_collection_init_list_destroy(setup_for_kcm): ++ """ ++ Test that multiple principals and service tickets can be stored ++ in a collection. ++ """ ++ testenv = setup_for_kcm ++ testenv.k5kdc.add_principal("alice", "alicepw") ++ testenv.k5kdc.add_principal("bob", "bobpw") ++ testenv.k5kdc.add_principal("carol", "carolpw") ++ testenv.k5kdc.add_principal("host/somehostname") ++ ++ assert testenv.k5util.num_princs() == 0 ++ ++ out, _, _ = testenv.k5util.kinit("alice", "alicepw") ++ assert out == 0 ++ assert testenv.k5util.default_principal() == 'alice@KCMTEST' ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 1 ++ assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert 'bob@KCMTEST' not in cc_coll ++ assert 'carol@KCMTEST' not in cc_coll ++ ++ out, _, _ = testenv.k5util.kinit("bob", "bobpw") ++ assert out == 0 ++ assert testenv.k5util.default_principal() == 'bob@KCMTEST' ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 2 ++ assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert 'carol@KCMTEST' not in cc_coll ++ ++ out, _, _ = testenv.k5util.kinit("carol", "carolpw") ++ assert out == 0 ++ assert testenv.k5util.default_principal() == 'carol@KCMTEST' ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 3 ++ assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert cc_coll['carol@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ ++ out, _, _ = testenv.k5util.kvno('host/somehostname') ++ assert out == 0 ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 3 ++ assert set(cc_coll['carol@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST', ++ 'host/somehostname@KCMTEST']) ++ ++ out = testenv.k5util.kdestroy() ++ assert out == 0 ++ assert testenv.k5util.default_principal() == 'bob@KCMTEST' ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 2 ++ assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert 'carol@KCMTEST' not in cc_coll ++ ++ # FIXME - a bug in libkrb5? ++ #out = testenv.k5util.kdestroy(all_ccaches=True) ++ #assert out == 0 ++ #cc_coll = testenv.k5util.list_all_princs() ++ #assert len(cc_coll) == 0 ++ ++ ++def test_kswitch(setup_for_kcm): ++ """ ++ Test switching between principals ++ """ ++ testenv = setup_for_kcm ++ testenv.k5kdc.add_principal("alice", "alicepw") ++ testenv.k5kdc.add_principal("bob", "bobpw") ++ testenv.k5kdc.add_principal("host/somehostname") ++ testenv.k5kdc.add_principal("host/differenthostname") ++ ++ out, _, _ = testenv.k5util.kinit("alice", "alicepw") ++ assert out == 0 ++ assert testenv.k5util.default_principal() == 'alice@KCMTEST' ++ ++ out, _, _ = testenv.k5util.kinit("bob", "bobpw") ++ assert out == 0 ++ assert testenv.k5util.default_principal() == 'bob@KCMTEST' ++ ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 2 ++ assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ ++ out = testenv.k5util.kswitch("alice@KCMTEST") ++ assert testenv.k5util.default_principal() == 'alice@KCMTEST' ++ out, _, _ = testenv.k5util.kvno('host/somehostname') ++ assert out == 0 ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 2 ++ assert set(cc_coll['alice@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST', ++ 'host/somehostname@KCMTEST']) ++ assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] ++ ++ out = testenv.k5util.kswitch("bob@KCMTEST") ++ assert testenv.k5util.default_principal() == 'bob@KCMTEST' ++ out, _, _ = testenv.k5util.kvno('host/differenthostname') ++ assert out == 0 ++ cc_coll = testenv.k5util.list_all_princs() ++ assert len(cc_coll) == 2 ++ assert set(cc_coll['alice@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST', ++ 'host/somehostname@KCMTEST']) ++ assert set(cc_coll['bob@KCMTEST']) == set([ ++ 'krbtgt/KCMTEST@KCMTEST', ++ 'host/differenthostname@KCMTEST']) ++ ++ ++def test_subsidiaries(setup_for_kcm): ++ """ ++ Test that subsidiary caches are usable and KCM: without specifying UID ++ can be used to identify the collection ++ """ ++ testenv = setup_for_kcm ++ testenv.k5kdc.add_principal("alice", "alicepw") ++ testenv.k5kdc.add_principal("bob", "bobpw") ++ testenv.k5kdc.add_principal("host/somehostname") ++ testenv.k5kdc.add_principal("host/differenthostname") ++ ++ out, _, _ = testenv.k5util.kinit("alice", "alicepw") ++ assert out == 0 ++ out, _, _ = testenv.k5util.kvno('host/somehostname') ++ ++ out, _, _ = testenv.k5util.kinit("bob", "bobpw") ++ assert out == 0 ++ out, _, _ = testenv.k5util.kvno('host/differenthostname') ++ ++ exp_cc_coll = dict() ++ exp_cc_coll['alice@KCMTEST'] = 'host/somehostname@KCMTEST' ++ exp_cc_coll['bob@KCMTEST'] = 'host/differenthostname@KCMTEST' ++ ++ klist_l = testenv.k5util.list_princs() ++ princ_ccache = dict() ++ for line in klist_l: ++ princ, subsidiary = line.split() ++ princ_ccache[princ] = subsidiary ++ ++ for princ, subsidiary in princ_ccache.items(): ++ env = {'KRB5CCNAME': subsidiary} ++ cc_coll = testenv.k5util.list_all_princs(env=env) ++ assert len(cc_coll) == 1 ++ assert princ in cc_coll ++ assert exp_cc_coll[princ] in cc_coll[princ] ++ ++ cc_coll = testenv.k5util.list_all_princs(env={'KRB5CCNAME': 'KCM:'}) ++ assert len(cc_coll) == 2 ++ assert set(cc_coll['alice@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST', ++ 'host/somehostname@KCMTEST']) ++ assert set(cc_coll['bob@KCMTEST']) == set([ ++ 'krbtgt/KCMTEST@KCMTEST', ++ 'host/differenthostname@KCMTEST']) ++ ++ ++def test_kdestroy_nocache(setup_for_kcm): ++ """ ++ Destroying a non-existing ccache should not throw an error ++ """ ++ testenv = setup_for_kcm ++ testenv.k5kdc.add_principal("alice", "alicepw") ++ out, _, _ = testenv.k5util.kinit("alice", "alicepw") ++ assert out == 0 ++ ++ testenv.k5util.kdestroy() ++ assert out == 0 ++ out = testenv.k5util.kdestroy() ++ assert out == 0 +-- +2.9.3 + diff --git a/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch b/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch new file mode 100644 index 0000000..3b1bee7 --- /dev/null +++ b/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch @@ -0,0 +1,405 @@ +From 27e11e8f03e1bad5d1be276efaf1406b16b11625 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 3 Jan 2017 16:00:38 +0100 +Subject: [PATCH 29/36] SECRETS: Create DB path before the operation itself +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is a refactoring where instead of creating the ldb path in the +operation itself, we create the ldb path when creating the local db request +and pass the path to the operation. + +This would allow us to store different kind of objects in the secrets +storage later. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/responder/secrets/local.c | 170 +++++++++++++++++++++--------------------- + 1 file changed, 84 insertions(+), 86 deletions(-) + +diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c +index ed70193bcb27d84eaf449f6f7571c94f466c9896..9dcdd9925e542499d3a962b4998103b07c26a5ab 100644 +--- a/src/responder/secrets/local.c ++++ b/src/responder/secrets/local.c +@@ -199,39 +199,36 @@ static char *local_dn_to_path(TALLOC_CTX *mem_ctx, + return path; + } + ++struct local_db_req { ++ char *path; ++ struct ldb_dn *basedn; ++}; ++ + #define LOCAL_SIMPLE_FILTER "(type=simple)" + #define LOCAL_CONTAINER_FILTER "(type=container)" + + static int local_db_get_simple(TALLOC_CTX *mem_ctx, + struct local_context *lctx, +- const char *req_path, ++ struct local_db_req *lc_req, + char **secret) + { + TALLOC_CTX *tmp_ctx; + static const char *attrs[] = { "secret", "enctype", NULL }; + struct ldb_result *res; +- struct ldb_dn *dn; + const char *attr_secret; + const char *attr_enctype; + int ret; + +- DEBUG(SSSDBG_TRACE_FUNC, "Retrieving a secret from [%s]\n", req_path); ++ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving a secret from [%s]\n", lc_req->path); + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; + +- ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret)); +- goto done; +- } +- + DEBUG(SSSDBG_TRACE_INTERNAL, + "Searching for [%s] at [%s] with scope=base\n", +- LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(dn)); ++ LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(lc_req->basedn)); + +- ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, ++ ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_BASE, + attrs, "%s", LOCAL_SIMPLE_FILTER); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_LIBS, +@@ -278,34 +275,26 @@ done: + + static int local_db_list_keys(TALLOC_CTX *mem_ctx, + struct local_context *lctx, +- const char *req_path, ++ struct local_db_req *lc_req, + char ***_keys, + int *num_keys) + { + TALLOC_CTX *tmp_ctx; + static const char *attrs[] = { "secret", NULL }; + struct ldb_result *res; +- struct ldb_dn *dn; + char **keys; + int ret; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; + +- DEBUG(SSSDBG_TRACE_FUNC, "Listing keys at [%s]\n", req_path); +- +- ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret)); +- goto done; +- } ++ DEBUG(SSSDBG_TRACE_FUNC, "Listing keys at [%s]\n", lc_req->path); + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Searching for [%s] at [%s] with scope=subtree\n", +- LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(dn)); ++ LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(lc_req->basedn)); + +- ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE, ++ ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_SUBTREE, + attrs, "%s", LOCAL_SIMPLE_FILTER); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_LIBS, +@@ -327,7 +316,7 @@ static int local_db_list_keys(TALLOC_CTX *mem_ctx, + } + + for (unsigned i = 0; i < res->count; i++) { +- keys[i] = local_dn_to_path(keys, dn, res->msgs[i]->dn); ++ keys[i] = local_dn_to_path(keys, lc_req->basedn, res->msgs[i]->dn); + if (!keys[i]) { + ret = ENOMEM; + goto done; +@@ -474,7 +463,7 @@ static int local_check_max_payload_size(struct local_context *lctx, + + static int local_db_put_simple(TALLOC_CTX *mem_ctx, + struct local_context *lctx, +- const char *req_path, ++ struct local_db_req *lc_req, + const char *secret) + { + struct ldb_message *msg; +@@ -482,20 +471,14 @@ static int local_db_put_simple(TALLOC_CTX *mem_ctx, + char *enc_secret; + int ret; + ++ DEBUG(SSSDBG_TRACE_FUNC, "Adding a secret to [%s]\n", lc_req->path); ++ + msg = ldb_msg_new(mem_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } +- +- DEBUG(SSSDBG_TRACE_FUNC, "Adding a secret to [%s]\n", req_path); +- +- ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret)); +- goto done; +- } ++ msg->dn = lc_req->basedn; + + /* make sure containers exist */ + ret = local_db_check_containers(msg, lctx, msg->dn); +@@ -585,32 +568,24 @@ done: + + static int local_db_delete(TALLOC_CTX *mem_ctx, + struct local_context *lctx, +- const char *req_path) ++ struct local_db_req *lc_req) + { + TALLOC_CTX *tmp_ctx; +- struct ldb_dn *dn; + static const char *attrs[] = { NULL }; + struct ldb_result *res; + int ret; + +- DEBUG(SSSDBG_TRACE_FUNC, "Removing a secret from [%s]\n", req_path); ++ DEBUG(SSSDBG_TRACE_FUNC, "Removing a secret from [%s]\n", lc_req->path); + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; + +- ret = local_db_dn(mem_ctx, lctx->ldb, req_path, &dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret)); +- goto done; +- } +- + DEBUG(SSSDBG_TRACE_INTERNAL, + "Searching for [%s] at [%s] with scope=base\n", +- LOCAL_CONTAINER_FILTER, ldb_dn_get_linearized(dn)); ++ LOCAL_CONTAINER_FILTER, ldb_dn_get_linearized(lc_req->basedn)); + +- ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, +- attrs, LOCAL_CONTAINER_FILTER); ++ ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_BASE, ++ attrs, LOCAL_CONTAINER_FILTER); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_LIBS, + "ldb_search returned %d: %s\n", ret, ldb_strerror(ret)); +@@ -619,8 +594,8 @@ static int local_db_delete(TALLOC_CTX *mem_ctx, + + if (res->count == 1) { + DEBUG(SSSDBG_TRACE_INTERNAL, +- "Searching for children of [%s]\n", ldb_dn_get_linearized(dn)); +- ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL, ++ "Searching for children of [%s]\n", ldb_dn_get_linearized(lc_req->basedn)); ++ ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_ONELEVEL, + attrs, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_LIBS, +@@ -632,13 +607,13 @@ static int local_db_delete(TALLOC_CTX *mem_ctx, + ret = EEXIST; + DEBUG(SSSDBG_OP_FAILURE, + "Failed to remove '%s': Container is not empty\n", +- ldb_dn_get_linearized(dn)); ++ ldb_dn_get_linearized(lc_req->basedn)); + + goto done; + } + } + +- ret = ldb_delete(lctx->ldb, dn); ++ ret = ldb_delete(lctx->ldb, lc_req->basedn); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_LIBS, + "ldb_delete returned %d: %s\n", ret, ldb_strerror(ret)); +@@ -653,25 +628,19 @@ done: + + static int local_db_create(TALLOC_CTX *mem_ctx, + struct local_context *lctx, +- const char *req_path) ++ struct local_db_req *lc_req) + { + struct ldb_message *msg; + int ret; + ++ DEBUG(SSSDBG_TRACE_FUNC, "Creating a container at [%s]\n", lc_req->path); ++ + msg = ldb_msg_new(mem_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } +- +- DEBUG(SSSDBG_TRACE_FUNC, "Creating a container at [%s]\n", req_path); +- +- ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret)); +- goto done; +- } ++ msg->dn = lc_req->basedn; + + /* make sure containers exist */ + ret = local_db_check_containers(msg, lctx, msg->dn); +@@ -724,10 +693,13 @@ done: + } + + static int local_secrets_map_path(TALLOC_CTX *mem_ctx, ++ struct ldb_context *ldb, + struct sec_req_ctx *secreq, +- char **local_db_path) ++ struct local_db_req **_lc_req) + { + int ret; ++ struct local_db_req *lc_req; ++ const char *basedn; + + /* be strict for now */ + if (secreq->parsed_url.fragment != NULL) { +@@ -755,20 +727,46 @@ static int local_secrets_map_path(TALLOC_CTX *mem_ctx, + } + } + +- /* drop SEC_BASEPATH prefix */ +- *local_db_path = +- talloc_strdup(mem_ctx, &secreq->mapped_path[sizeof(SEC_BASEPATH) - 1]); +- if (!*local_db_path) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to map request to local db path\n"); ++ lc_req = talloc(mem_ctx, struct local_db_req); ++ if (lc_req == NULL) { + return ENOMEM; + } + +- DEBUG(SSSDBG_TRACE_LIBS, "Local DB path is %s\n", *local_db_path); +- return EOK; ++ /* drop the prefix and select a basedn instead */ ++ if (strncmp(secreq->mapped_path, ++ SEC_BASEPATH, sizeof(SEC_BASEPATH) - 1) == 0) { ++ lc_req->path = talloc_strdup(lc_req, ++ secreq->mapped_path + (sizeof(SEC_BASEPATH) - 1)); ++ basedn = SECRETS_BASEDN; ++ } else { ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (lc_req->path == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to map request to local db path\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = local_db_dn(mem_ctx, ldb, basedn, lc_req->path, &lc_req->basedn); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to map request to local db DN\n"); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Local DB path is %s\n", lc_req->path); ++ ret = EOK; ++ *_lc_req = lc_req; ++done: ++ if (ret != EOK) { ++ talloc_free(lc_req); ++ } ++ return ret; + } + +- + struct local_secret_state { + struct tevent_context *ev; + struct sec_req_ctx *secreq; +@@ -785,7 +783,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + struct sec_data body = { 0 }; + const char *content_type; + bool body_is_json; +- char *req_path; ++ struct local_db_req *lc_req; + char *secret; + char **keys; + int nkeys; +@@ -821,14 +819,14 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + } + DEBUG(SSSDBG_TRACE_LIBS, "Content-Type: %s\n", content_type); + +- ret = local_secrets_map_path(state, secreq, &req_path); ++ ret = local_secrets_map_path(state, lctx->ldb, secreq, &lc_req); + if (ret) goto done; + + switch (secreq->method) { + case HTTP_GET: +- DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP GET at [%s]\n", req_path); +- if (req_path[strlen(req_path) - 1] == '/') { +- ret = local_db_list_keys(state, lctx, req_path, &keys, &nkeys); ++ DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP GET at [%s]\n", lc_req->path); ++ if (lc_req->path[strlen(lc_req->path) - 1] == '/') { ++ ret = local_db_list_keys(state, lctx, lc_req, &keys, &nkeys); + if (ret) goto done; + + ret = sec_array_to_json(state, keys, nkeys, &body.data); +@@ -838,7 +836,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + break; + } + +- ret = local_db_get_simple(state, lctx, req_path, &secret); ++ ret = local_db_get_simple(state, lctx, lc_req, &secret); + if (ret) goto done; + + if (body_is_json) { +@@ -855,7 +853,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + break; + + case HTTP_PUT: +- DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP PUT at [%s]\n", req_path); ++ DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP PUT at [%s]\n", lc_req->path); + if (body_is_json) { + ret = sec_json_to_simple_secret(state, secreq->body.data, + &secret); +@@ -866,27 +864,27 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + } + if (ret) goto done; + +- ret = local_db_put_simple(state, lctx, req_path, secret); ++ ret = local_db_put_simple(state, lctx, lc_req, secret); + if (ret) goto done; + break; + + case HTTP_DELETE: +- ret = local_db_delete(state, lctx, req_path); ++ ret = local_db_delete(state, lctx, lc_req); + if (ret) goto done; + break; + + case HTTP_POST: +- DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP POST at [%s]\n", req_path); +- plen = strlen(req_path); ++ DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP POST at [%s]\n", lc_req->path); ++ plen = strlen(lc_req->path); + +- if (req_path[plen - 1] != '/') { ++ if (lc_req->path[plen - 1] != '/') { + ret = EINVAL; + goto done; + } + +- req_path[plen - 1] = '\0'; ++ lc_req->path[plen - 1] = '\0'; + +- ret = local_db_create(state, lctx, req_path); ++ ret = local_db_create(state, lctx, lc_req); + if (ret) goto done; + break; + +-- +2.9.3 + diff --git a/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch b/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch new file mode 100644 index 0000000..2c596dc --- /dev/null +++ b/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch @@ -0,0 +1,40 @@ +From ddda18a37ac4b732ad109dbb129255dc3edd8fbb Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 3 Feb 2017 14:33:47 +0100 +Subject: [PATCH 30/36] SECRETS: Return a nicer error message on request with + no PUT data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +I managed to create this pathological situation with the tcurl tool +which didn't send any PUT data. The error in sssd-secrets was quite +strange (ENOMEM). This patch just adds a safeguard sooner so that we +return a graceful error. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/responder/secrets/local.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c +index 9dcdd9925e542499d3a962b4998103b07c26a5ab..26c97a2849febbf0ac482d526cf927bfc103b4f2 100644 +--- a/src/responder/secrets/local.c ++++ b/src/responder/secrets/local.c +@@ -853,6 +853,12 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + break; + + case HTTP_PUT: ++ if (secreq->body.length == 0) { ++ DEBUG(SSSDBG_OP_FAILURE, "PUT with no data\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ + DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP PUT at [%s]\n", lc_req->path); + if (body_is_json) { + ret = sec_json_to_simple_secret(state, secreq->body.data, +-- +2.9.3 + diff --git a/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch b/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch new file mode 100644 index 0000000..09d388a --- /dev/null +++ b/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch @@ -0,0 +1,206 @@ +From 91c099a993252680f103084431b1d0f5798d8a24 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Mar 2017 14:14:42 +0100 +Subject: [PATCH 31/36] SECRETS: Store ccaches in secrets for the KCM responder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a new "hive" to the secrets responder whose base path is /kcm. Only +root can contact the /kcm hive, because the KCM responder only runs as +root and it must impersonate other users and store ccaches on their behalf. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/responder/secrets/local.c | 16 +++++++- + src/responder/secrets/providers.c | 71 ++++++++++++++++++++++++++++++---- + src/responder/secrets/secsrv_private.h | 10 ++++- + 3 files changed, 86 insertions(+), 11 deletions(-) + +diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c +index 26c97a2849febbf0ac482d526cf927bfc103b4f2..02007ada8b673071ecba033df0eb3f81af93fcbd 100644 +--- a/src/responder/secrets/local.c ++++ b/src/responder/secrets/local.c +@@ -26,6 +26,9 @@ + + #define MKEY_SIZE (256 / 8) + ++#define SECRETS_BASEDN "cn=secrets" ++#define KCM_BASEDN "cn=kcm" ++ + struct local_context { + struct ldb_context *ldb; + struct sec_data master_key; +@@ -119,6 +122,7 @@ static int local_encrypt(struct local_context *lctx, TALLOC_CTX *mem_ctx, + + static int local_db_dn(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, ++ const char *basedn, + const char *req_path, + struct ldb_dn **req_dn) + { +@@ -126,7 +130,7 @@ static int local_db_dn(TALLOC_CTX *mem_ctx, + const char *s, *e; + int ret; + +- dn = ldb_dn_new(mem_ctx, ldb, "cn=secrets"); ++ dn = ldb_dn_new(mem_ctx, ldb, basedn); + if (!dn) { + ret = ENOMEM; + goto done; +@@ -738,6 +742,11 @@ static int local_secrets_map_path(TALLOC_CTX *mem_ctx, + lc_req->path = talloc_strdup(lc_req, + secreq->mapped_path + (sizeof(SEC_BASEPATH) - 1)); + basedn = SECRETS_BASEDN; ++ } else if (strncmp(secreq->mapped_path, ++ SEC_KCM_BASEPATH, sizeof(SEC_KCM_BASEPATH) - 1) == 0) { ++ lc_req->path = talloc_strdup(lc_req, ++ secreq->mapped_path + (sizeof(SEC_KCM_BASEPATH) - 1)); ++ basedn = KCM_BASEDN; + } else { + ret = EINVAL; + goto done; +@@ -820,7 +829,10 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx, + DEBUG(SSSDBG_TRACE_LIBS, "Content-Type: %s\n", content_type); + + ret = local_secrets_map_path(state, lctx->ldb, secreq, &lc_req); +- if (ret) goto done; ++ if (ret) { ++ DEBUG(SSSDBG_OP_FAILURE, "Cannot map request path to local path\n"); ++ goto done; ++ } + + switch (secreq->method) { + case HTTP_GET: +diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c +index eba555d2e422d08db211979422a2957e48b51589..94831c73036d269addca45c0117811a2c68873fd 100644 +--- a/src/responder/secrets/providers.c ++++ b/src/responder/secrets/providers.c +@@ -24,6 +24,14 @@ + #include "responder/secrets/secsrv_proxy.h" + #include + ++typedef int (*url_mapper_fn)(struct sec_req_ctx *secreq, ++ char **mapped_path); ++ ++struct url_pfx_router { ++ const char *prefix; ++ url_mapper_fn mapper_fn; ++}; ++ + static int sec_map_url_to_user_path(struct sec_req_ctx *secreq, + char **mapped_path) + { +@@ -42,10 +50,43 @@ static int sec_map_url_to_user_path(struct sec_req_ctx *secreq, + return ENOMEM; + } + +- DEBUG(SSSDBG_TRACE_LIBS, "User-specific path is [%s]\n", *mapped_path); ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "User-specific secrets path is [%s]\n", *mapped_path); + return EOK; + } + ++static int kcm_map_url_to_path(struct sec_req_ctx *secreq, ++ char **mapped_path) ++{ ++ uid_t c_euid; ++ ++ c_euid = client_euid(secreq->cctx->creds); ++ if (c_euid != KCM_PEER_UID) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "UID %"SPRIuid" is not allowed to access " ++ "the "SEC_KCM_BASEPATH" hive\n", ++ c_euid); ++ return EPERM; ++ } ++ ++ *mapped_path = talloc_strdup(secreq, secreq->parsed_url.path ); ++ if (!*mapped_path) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to map request to user specific url\n"); ++ return ENOMEM; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "User-specific KCM path is [%s]\n", *mapped_path); ++ return EOK; ++} ++ ++static struct url_pfx_router secrets_url_mapping[] = { ++ { SEC_BASEPATH, sec_map_url_to_user_path }, ++ { SEC_KCM_BASEPATH, kcm_map_url_to_path }, ++ { NULL, NULL }, ++}; ++ + int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq, + struct provider_handle **handle) + { +@@ -55,21 +96,35 @@ int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq, + char *provider; + int num_sections; + int ret; ++ url_mapper_fn mapper_fn = NULL; + + sctx = talloc_get_type(secreq->cctx->rctx->pvt_ctx, struct sec_ctx); + +- /* patch must start with /secrets/ for now */ +- ret = strncasecmp(secreq->parsed_url.path, +- SEC_BASEPATH, sizeof(SEC_BASEPATH) - 1); +- if (ret != 0) { ++ for (int i = 0; secrets_url_mapping[i].prefix != NULL; i++) { ++ if (strncasecmp(secreq->parsed_url.path, ++ secrets_url_mapping[i].prefix, ++ strlen(secrets_url_mapping[i].prefix)) == 0) { ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Mapping prefix %s\n", secrets_url_mapping[i].prefix); ++ mapper_fn = secrets_url_mapping[i].mapper_fn; ++ break; ++ } ++ } ++ ++ if (mapper_fn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "Path [%s] does not start with "SEC_BASEPATH"\n", ++ "Path [%s] does not start with any allowed prefix\n", + secreq->parsed_url.path); + return EPERM; + } + +- ret = sec_map_url_to_user_path(secreq, &secreq->mapped_path); +- if (ret) return ret; ++ ret = mapper_fn(secreq, &secreq->mapped_path); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to map the user path [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } + + /* source default provider */ + ret = confdb_get_string(secreq->cctx->rctx->cdb, mem_ctx, +diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h +index 1c3fbd8eadb237551233f048503ddc01b4ba00ae..a8544f656517a17fe4576247779bff4850beaf97 100644 +--- a/src/responder/secrets/secsrv_private.h ++++ b/src/responder/secrets/secsrv_private.h +@@ -101,7 +101,15 @@ int sec_get_provider(struct sec_ctx *sctx, const char *name, + struct provider_handle **out_handle); + int sec_add_provider(struct sec_ctx *sctx, struct provider_handle *handle); + +-#define SEC_BASEPATH "/secrets/" ++#define SEC_BASEPATH "/secrets/" ++#define SEC_KCM_BASEPATH "/kcm/" ++ ++/* The KCM responder must "impersonate" the owner of the credentials. ++ * Only a trusted UID can do that -- root by default, but unit ++ * tests might choose otherwise */ ++#ifndef KCM_PEER_UID ++#define KCM_PEER_UID 0 ++#endif /* KCM_PEER_UID */ + + /* providers.c */ + int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq, +-- +2.9.3 + diff --git a/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch b/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch new file mode 100644 index 0000000..c94fe07 --- /dev/null +++ b/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch @@ -0,0 +1,129 @@ +From 2777ccdcc9038d8f62be81a24ae885639fe6ea9a Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 14 Mar 2017 15:34:57 +0100 +Subject: [PATCH 32/36] TCURL: Support HTTP POST for creating containers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The curl integration must allow us to create containers, therefore we +also add support of the POST HTTP request type. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/tests/intg/test_secrets.py | 28 ++++++++++++++++++++++++++++ + src/tests/tcurl_test_tool.c | 5 +++++ + src/util/tev_curl.c | 7 +++++++ + src/util/tev_curl.h | 1 + + 4 files changed, 41 insertions(+) + +diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py +index cbc1a1f06d2abb826bc0a880cb5a842f577657ea..d71c1904558cc6f8a6eee36c4049582705bc30ac 100644 +--- a/src/tests/intg/test_secrets.py ++++ b/src/tests/intg/test_secrets.py +@@ -271,6 +271,34 @@ def test_curlwrap_crd_ops(setup_for_secrets, + 'http://localhost/secrets/foo'], + 404) + ++ # Create a container ++ run_curlwrap_tool([curlwrap_tool, '-o', ++ '-v', '-s', sock_path, ++ 'http://localhost/secrets/cont/'], ++ 200) ++ ++ # set a secret foo:bar ++ run_curlwrap_tool([curlwrap_tool, '-p', ++ '-v', '-s', sock_path, ++ 'http://localhost/secrets/cont/cfoo', ++ 'foo_under_cont'], ++ 200) ++ ++ # list secrets ++ output = run_curlwrap_tool([curlwrap_tool, ++ '-v', '-s', sock_path, ++ 'http://localhost/secrets/cont/'], ++ 200) ++ assert "cfoo" in output ++ ++ # get the foo secret ++ output = run_curlwrap_tool([curlwrap_tool, ++ '-v', '-s', sock_path, ++ 'http://localhost/secrets/cont/cfoo'], ++ 200) ++ assert "foo_under_cont" in output ++ ++ + + def test_curlwrap_parallel(setup_for_secrets, + curlwrap_tool): +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 38cea432885c97ca3827c8f158bf7e3ebfc67b31..2af950ebb76a22bdf4a6dfd58442b10486e64293 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -88,6 +88,7 @@ int main(int argc, const char *argv[]) + { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL }, + { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL }, + { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, ++ { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL }, + { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL }, + POPT_TABLEEND + }; +@@ -118,6 +119,9 @@ int main(int argc, const char *argv[]) + case 'd': + req_type = TCURL_HTTP_DELETE; + break; ++ case 'o': ++ req_type = TCURL_HTTP_POST; ++ break; + case 'v': + pc_verbose = 1; + break; +@@ -145,6 +149,7 @@ int main(int argc, const char *argv[]) + switch (req_type) { + case TCURL_HTTP_GET: + case TCURL_HTTP_DELETE: ++ case TCURL_HTTP_POST: + urls[n_reqs++] = extra_arg_ptr; + break; + case TCURL_HTTP_PUT: +diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c +index fd436653b5aeb611a9648a8b81a330fd3fcfe875..645d1182d10f825f209f48e0ba7e6804dde1971c 100644 +--- a/src/util/tev_curl.c ++++ b/src/util/tev_curl.c +@@ -154,6 +154,8 @@ static const char *http_req2str(enum tcurl_http_request req) + return "PUT"; + case TCURL_HTTP_DELETE: + return "DELETE"; ++ case TCURL_HTTP_POST: ++ return "POST"; + } + + return "Uknown request type"; +@@ -815,6 +817,11 @@ static errno_t tcurl_set_options(struct tcurl_http_state *state, + } + + switch (req_type) { ++ case TCURL_HTTP_POST: ++ crv = curl_easy_setopt(state->http_handle, ++ CURLOPT_CUSTOMREQUEST, ++ "POST"); ++ break; + case TCURL_HTTP_PUT: + /* CURLOPT_UPLOAD enables HTTP_PUT */ + crv = curl_easy_setopt(state->http_handle, +diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h +index de0601df4327d97001a8a825cd4709936f6c8466..444eb286e09d189b4588e2b2152b5202df3914d8 100644 +--- a/src/util/tev_curl.h ++++ b/src/util/tev_curl.h +@@ -34,6 +34,7 @@ enum tcurl_http_request { + TCURL_HTTP_GET, + TCURL_HTTP_PUT, + TCURL_HTTP_DELETE, ++ TCURL_HTTP_POST, + }; + + /** +-- +2.9.3 + diff --git a/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch b/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch new file mode 100644 index 0000000..d08a786 --- /dev/null +++ b/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch @@ -0,0 +1,3787 @@ +From 8c30024303436d98818977288e28511ed74c018a Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 20 Mar 2017 11:49:43 +0100 +Subject: [PATCH 33/36] KCM: Store ccaches in secrets +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a new KCM responder ccache back end that forwards all requests to +sssd-secrets. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + Makefile.am | 40 +- + contrib/sssd.spec.in | 1 + + src/responder/kcm/kcmsrv_ccache.h | 49 + + src/responder/kcm/kcmsrv_ccache_json.c | 921 +++++++++++ + src/responder/kcm/kcmsrv_ccache_secrets.c | 2169 ++++++++++++++++++++++++++ + src/tests/cmocka/test_kcm_json_marshalling.c | 234 +++ + src/tests/intg/test_kcm.py | 132 +- + src/util/util_errors.c | 2 + + src/util/util_errors.h | 2 + + 9 files changed, 3525 insertions(+), 25 deletions(-) + create mode 100644 src/responder/kcm/kcmsrv_ccache_json.c + create mode 100644 src/responder/kcm/kcmsrv_ccache_secrets.c + create mode 100644 src/tests/cmocka/test_kcm_json_marshalling.c + +diff --git a/Makefile.am b/Makefile.am +index 49b4cabf9ee3ce1417f955c972376894f3709b33..e9eaa312c91e3aee40bcf13c90a0ad8c683045d5 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -303,6 +303,10 @@ if HAVE_INOTIFY + non_interactive_cmocka_based_tests += test_inotify + endif # HAVE_INOTIFY + ++if BUILD_KCM ++non_interactive_cmocka_based_tests += test_kcm_json ++endif # BUILD_KCM ++ + if BUILD_SAMBA + non_interactive_cmocka_based_tests += \ + ad_access_filter_tests \ +@@ -1494,19 +1498,26 @@ sssd_kcm_SOURCES = \ + src/responder/kcm/kcmsrv_cmd.c \ + src/responder/kcm/kcmsrv_ccache.c \ + src/responder/kcm/kcmsrv_ccache_mem.c \ ++ src/responder/kcm/kcmsrv_ccache_json.c \ ++ src/responder/kcm/kcmsrv_ccache_secrets.c \ + src/responder/kcm/kcmsrv_ops.c \ + src/util/sss_sockets.c \ + src/util/sss_krb5.c \ + src/util/sss_iobuf.c \ ++ src/util/tev_curl.c \ + $(SSSD_RESPONDER_OBJ) \ + $(NULL) + sssd_kcm_CFLAGS = \ + $(AM_CFLAGS) \ + $(KRB5_CFLAGS) \ + $(UUID_CFLAGS) \ ++ $(CURL_CFLAGS) \ ++ $(JANSSON_CFLAGS) \ + $(NULL) + sssd_kcm_LDADD = \ + $(KRB5_LIBS) \ ++ $(CURL_LIBS) \ ++ $(JANSSON_LIBS) \ + $(SSSD_LIBS) \ + $(UUID_LIBS) \ + $(SYSTEMD_DAEMON_LIBS) \ +@@ -3369,6 +3380,30 @@ sss_certmap_test_LDADD = \ + libsss_certmap.la \ + $(NULL) + endif ++ ++if BUILD_KCM ++test_kcm_json_SOURCES = \ ++ src/tests/cmocka/test_kcm_json_marshalling.c \ ++ src/responder/kcm/kcmsrv_ccache_json.c \ ++ src/responder/kcm/kcmsrv_ccache.c \ ++ src/util/sss_krb5.c \ ++ src/util/sss_iobuf.c \ ++ $(NULL) ++test_kcm_json_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(UUID_CFLAGS) \ ++ $(NULL) ++test_kcm_json_LDADD = \ ++ $(JANSSON_LIBS) \ ++ $(UUID_LIBS) \ ++ $(KRB5_LIBS) \ ++ $(CMOCKA_LIBS) \ ++ $(SSSD_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ $(NULL) ++endif # BUILD_KCM ++ + endif # HAVE_CMOCKA + + noinst_PROGRAMS = pam_test_client +@@ -3431,8 +3466,9 @@ intgcheck-prepare: + --enable-intgcheck-reqs \ + --without-semanage \ + --enable-files-domain \ +- $(INTGCHECK_CONFIGURE_FLAGS); \ +- $(MAKE) $(AM_MAKEFLAGS); \ ++ $(INTGCHECK_CONFIGURE_FLAGS) \ ++ CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \ ++ $(MAKE) $(AM_MAKEFLAGS) ; \ + : Force single-thread install to workaround concurrency issues; \ + $(MAKE) $(AM_MAKEFLAGS) -j1 install; \ + : Remove .la files from LDB module directory to avoid loader warnings; \ +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 1d4d020415ee28292bb4d88c78de205465d812f1..af14d4e3d6b9ffeb4696f1517113b8daa575cb99 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -223,6 +223,7 @@ BuildRequires: systemtap-sdt-devel + BuildRequires: http-parser-devel + BuildRequires: jansson-devel + BuildRequires: libuuid-devel ++BuildRequires: libcurl-devel + + %description + Provides a set of daemons to manage access to remote directories and +diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h +index 130ae48ae30d5e1e2ab238a647a9b9dc76cc4945..18c8c47ad4ecb24521a85a1833b239c7a2a8bb45 100644 +--- a/src/responder/kcm/kcmsrv_ccache.h ++++ b/src/responder/kcm/kcmsrv_ccache.h +@@ -303,4 +303,53 @@ void kcm_debug_uuid(uuid_t uuid); + */ + errno_t kcm_check_name(const char *name, struct cli_creds *client); + ++/* ++ * ccahe marshalling to and from JSON. This is used when the ccaches ++ * are stored in the secrets store ++ */ ++ ++/* ++ * The secrets store is a key-value store at heart. We store the UUID ++ * and the name in the key to allow easy lookups be either key ++ */ ++bool sec_key_match_name(const char *sec_key, ++ const char *name); ++ ++bool sec_key_match_uuid(const char *sec_key, ++ uuid_t uuid); ++ ++const char *sec_key_get_name(const char *sec_key); ++ ++errno_t sec_key_get_uuid(const char *sec_key, ++ uuid_t uuid); ++ ++/* Create a URL for the default client's ccache */ ++const char *sec_dfl_url_create(TALLOC_CTX *mem_ctx, ++ struct cli_creds *client); ++ ++/* Create a URL for the client's ccache container */ ++const char *sec_container_url_create(TALLOC_CTX *mem_ctx, ++ struct cli_creds *client); ++ ++const char *sec_cc_url_create(TALLOC_CTX *mem_ctx, ++ struct cli_creds *client, ++ const char *sec_key); ++ ++/* ++ * sec_key is a concatenation of the ccache's UUID and name ++ * sec_value is the JSON dump of the ccache contents ++ */ ++errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx, ++ const char *sec_key, ++ const char *sec_value, ++ struct cli_creds *client, ++ struct kcm_ccache **_cc); ++ ++/* Convert a kcm_ccache to a key-value pair to be stored in secrets */ ++errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx, ++ struct kcm_ccache *cc, ++ struct cli_creds *client, ++ const char **_url, ++ struct sss_iobuf **_payload); ++ + #endif /* _KCMSRV_CCACHE_H_ */ +diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c +new file mode 100644 +index 0000000000000000000000000000000000000000..40b64861c209206d6f60ccd0843857edee24a844 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache_json.c +@@ -0,0 +1,921 @@ ++/* ++ SSSD ++ ++ KCM Server - ccache JSON (un)marshalling for storing ccaches in ++ sssd-secrets ++ ++ Copyright (C) Red Hat, 2017 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "util/util.h" ++#include "util/util_creds.h" ++#include "util/crypto/sss_crypto.h" ++#include "responder/kcm/kcmsrv_ccache_pvt.h" ++ ++/* The base for storing secrets is: ++ * http://localhost/kcm/persistent/$uid ++ * ++ * Under $base, there are two containers: ++ * /ccache - stores the ccaches ++ * /ntlm - stores NTLM creds [Not implement yet] ++ * ++ * There is also a special entry that contains the UUID of the default ++ * cache for this UID: ++ * /default - stores the UUID of the default ccache for this UID ++ * ++ * Each ccache has a name and an UUID. On the secrets level, the 'secret' ++ * is a concatenation of the stringified UUID and the name separated ++ * by a plus-sign. ++ */ ++#define KCM_SEC_URL "http://localhost/kcm/persistent" ++#define KCM_SEC_BASE_FMT KCM_SEC_URL"/%"SPRIuid"/" ++#define KCM_SEC_CCACHE_FMT KCM_SEC_BASE_FMT"ccache/" ++#define KCM_SEC_DFL_FMT KCM_SEC_BASE_FMT"default" ++ ++ ++/* ++ * We keep the JSON representation of the ccache versioned to allow ++ * us to modify the format in a future version ++ */ ++#define KS_JSON_VERSION 1 ++ ++/* ++ * The secrets store is a key-value store at heart. We store the UUID ++ * and the name in the key to allow easy lookups be either key ++ */ ++#define SEC_KEY_SEPARATOR '-' ++ ++/* Compat definition of json_array_foreach for older systems */ ++#ifndef json_array_foreach ++#define json_array_foreach(array, idx, value) \ ++ for(idx = 0; \ ++ idx < json_array_size(array) && (value = json_array_get(array, idx)); \ ++ idx++) ++#endif ++ ++const char *sec_container_url_create(TALLOC_CTX *mem_ctx, ++ struct cli_creds *client) ++{ ++ return talloc_asprintf(mem_ctx, ++ KCM_SEC_CCACHE_FMT, ++ cli_creds_get_uid(client)); ++} ++ ++const char *sec_cc_url_create(TALLOC_CTX *mem_ctx, ++ struct cli_creds *client, ++ const char *sec_key) ++{ ++ return talloc_asprintf(mem_ctx, ++ KCM_SEC_CCACHE_FMT"%s", ++ cli_creds_get_uid(client), ++ sec_key); ++} ++ ++const char *sec_dfl_url_create(TALLOC_CTX *mem_ctx, ++ struct cli_creds *client) ++{ ++ return talloc_asprintf(mem_ctx, ++ KCM_SEC_DFL_FMT, ++ cli_creds_get_uid(client)); ++} ++ ++static const char *sec_key_create(TALLOC_CTX *mem_ctx, ++ const char *name, ++ uuid_t uuid) ++{ ++ char uuid_str[UUID_STR_SIZE]; ++ ++ uuid_unparse(uuid, uuid_str); ++ return talloc_asprintf(mem_ctx, ++ "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name); ++} ++ ++static errno_t sec_key_parse(TALLOC_CTX *mem_ctx, ++ const char *sec_key, ++ const char **_name, ++ uuid_t uuid) ++{ ++ char uuid_str[UUID_STR_SIZE]; ++ ++ if (strlen(sec_key) < UUID_STR_SIZE + 2) { ++ /* One char for separator and at least one for the name */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); ++ return EINVAL; ++ } ++ ++ strncpy(uuid_str, sec_key, sizeof(uuid_str)-1); ++ if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n"); ++ return EINVAL; ++ } ++ uuid_str[UUID_STR_SIZE-1] = '\0'; ++ ++ *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE); ++ if (*_name == NULL) { ++ return ENOMEM; ++ } ++ uuid_parse(uuid_str, uuid); ++ ++ return EOK; ++} ++ ++errno_t sec_key_get_uuid(const char *sec_key, ++ uuid_t uuid) ++{ ++ char uuid_str[UUID_STR_SIZE]; ++ ++ if (strlen(sec_key) < UUID_STR_SIZE + 2) { ++ /* One char for separator and at least one for the name */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); ++ return EINVAL; ++ } ++ ++ if (sec_key[UUID_STR_SIZE-1] != SEC_KEY_SEPARATOR) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n"); ++ return EINVAL; ++ } ++ ++ strncpy(uuid_str, sec_key, UUID_STR_SIZE-1); ++ uuid_str[UUID_STR_SIZE-1] = '\0'; ++ uuid_parse(uuid_str, uuid); ++ return EOK; ++} ++ ++const char *sec_key_get_name(const char *sec_key) ++{ ++ if (strlen(sec_key) < UUID_STR_SIZE + 2) { ++ /* One char for separator and at least one for the name */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); ++ return NULL; ++ } ++ ++ return sec_key + UUID_STR_SIZE; ++} ++ ++bool sec_key_match_name(const char *sec_key, ++ const char *name) ++{ ++ if (strlen(sec_key) < UUID_STR_SIZE + 2) { ++ /* One char for separator and at least one for the name */ ++ DEBUG(SSSDBG_MINOR_FAILURE, "Key %s is too short\n", sec_key); ++ return false; ++ } ++ ++ return strcmp(sec_key + UUID_STR_SIZE, name) == 0; ++} ++ ++bool sec_key_match_uuid(const char *sec_key, ++ uuid_t uuid) ++{ ++ errno_t ret; ++ uuid_t key_uuid; ++ ++ ret = sec_key_get_uuid(sec_key, key_uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n"); ++ return false; ++ } ++ ++ return uuid_compare(key_uuid, uuid) == 0; ++} ++ ++/* ++ * Creates an array of principal elements that will be used later ++ * in the form of: ++ * "componenets": [ "elem1", "elem2", ...] ++ */ ++static json_t *princ_data_to_json(TALLOC_CTX *mem_ctx, ++ krb5_principal princ) ++{ ++ json_t *jdata = NULL; ++ json_t *data_array = NULL; ++ int ret; ++ char *str_princ_data; ++ ++ data_array = json_array(); ++ if (data_array == NULL) { ++ return NULL; ++ } ++ ++ for (ssize_t i = 0; i < princ->length; i++) { ++ /* FIXME - it might be cleaner to use stringn here, but the libjansson ++ * version on RHEL-7 doesn't support that ++ */ ++ str_princ_data = talloc_zero_array(mem_ctx, ++ char, ++ princ->data[i].length + 1); ++ if (str_princ_data == NULL) { ++ return NULL; ++ } ++ memcpy(str_princ_data, princ->data[i].data, princ->data[i].length); ++ str_princ_data[princ->data[i].length] = '\0'; ++ ++ jdata = json_string(str_princ_data); ++ talloc_free(str_princ_data); ++ if (jdata == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert principal data to string\n"); ++ json_decref(data_array); ++ return NULL; ++ } ++ ++ ret = json_array_append_new(data_array, jdata); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot append principal data to array\n"); ++ json_decref(jdata); ++ json_decref(data_array); ++ return NULL; ++ } ++ /* data_array now owns the reference to jdata */ ++ } ++ ++ return data_array; ++} ++ ++/* Creates: ++ * { ++ * "type": "number", ++ * "realm": "string", ++ * "componenents": [ "elem1", "elem2", ...] ++ * } ++ */ ++static json_t *princ_to_json(TALLOC_CTX *mem_ctx, ++ krb5_principal princ) ++{ ++ json_t *jprinc = NULL; ++ json_t *components = NULL; ++ json_error_t error; ++ char *str_realm_data; ++ ++ components = princ_data_to_json(mem_ctx, princ); ++ if (components == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert principal data to JSON\n"); ++ return NULL; ++ } ++ ++ /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson ++ * version on RHEL-7 doesn't support that ++ */ ++ str_realm_data = talloc_zero_array(mem_ctx, ++ char, ++ princ->realm.length + 1); ++ if (str_realm_data == NULL) { ++ return NULL; ++ } ++ memcpy(str_realm_data, princ->realm.data, princ->realm.length); ++ str_realm_data[princ->realm.length] = '\0'; ++ ++ jprinc = json_pack_ex(&error, ++ JSON_STRICT, ++ "{s:i, s:s, s:o}", ++ "type", princ->type, ++ "realm", str_realm_data, ++ "components", components); ++ talloc_free(str_realm_data); ++ if (jprinc == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to pack JSON princ structure on line %d: %s\n", ++ error.line, error.text); ++ json_decref(components); ++ return NULL; ++ } ++ ++ return jprinc; ++} ++ ++/* Creates: ++ * { ++ * "uuid": , ++ * "payload": , ++ * }, ++ */ ++static json_t *cred_to_json(struct kcm_cred *crd) ++{ ++ char uuid_str[UUID_STR_SIZE]; ++ uint8_t *cred_blob_data; ++ size_t cred_blob_size; ++ json_t *jcred; ++ json_error_t error; ++ char *base64_cred_blob; ++ ++ uuid_unparse(crd->uuid, uuid_str); ++ cred_blob_data = sss_iobuf_get_data(crd->cred_blob); ++ cred_blob_size = sss_iobuf_get_size(crd->cred_blob); ++ ++ base64_cred_blob = sss_base64_encode(crd, cred_blob_data, cred_blob_size); ++ if (base64_cred_blob == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot base64 encode the certificate blob\n"); ++ return NULL; ++ } ++ ++ jcred = json_pack_ex(&error, ++ JSON_STRICT, ++ "{s:s, s:s}", ++ "uuid", uuid_str, ++ "payload", base64_cred_blob); ++ talloc_free(base64_cred_blob); ++ if (jcred == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to pack JSON cred structure on line %d: %s\n", ++ error.line, error.text); ++ return NULL; ++ } ++ return jcred; ++} ++ ++/* ++ * Creates: ++ * [ ++ * { ++ * "uuid": , ++ * "payload": , ++ * }, ++ * ... ++ * ] ++ */ ++static json_t *creds_to_json_array(struct kcm_cred *creds) ++{ ++ struct kcm_cred *crd; ++ json_t *array; ++ json_t *jcred; ++ ++ array = json_array(); ++ if (array == NULL) { ++ return NULL; ++ } ++ ++ DLIST_FOR_EACH(crd, creds) { ++ jcred = cred_to_json(crd); ++ if (jcred == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert credentials to JSON\n"); ++ json_decref(array); ++ return NULL; ++ } ++ ++ json_array_append_new(array, jcred); ++ /* array now owns jcred */ ++ jcred = NULL; ++ } ++ ++ return array; ++} ++ ++/* ++ * The ccache is formatted in JSON as: ++ * { ++ * version: number ++ * kdc_offset: number ++ * principal : { ++ * "type": "number", ++ * "realm": "string", ++ * "componenents": [ "elem1", "elem2", ...] ++ * } ++ * creds : [ ++ * { ++ * "uuid": , ++ * "payload": , ++ * }, ++ * { ++ * ... ++ * } ++ * ] ++ * } ++ * } ++ */ ++static json_t *ccache_to_json(struct kcm_ccache *cc) ++{ ++ json_t *princ = NULL; ++ json_t *creds = NULL; ++ json_t *jcc = NULL; ++ json_error_t error; ++ ++ princ = princ_to_json(cc, cc->client); ++ if (princ == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert princ to JSON\n"); ++ return NULL; ++ } ++ ++ creds = creds_to_json_array(cc->creds); ++ if (creds == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert creds to JSON array\n"); ++ json_decref(princ); ++ return NULL; ++ } ++ ++ jcc = json_pack_ex(&error, ++ JSON_STRICT, ++ "{s:i, s:i, s:o, s:o}", ++ "version", KS_JSON_VERSION, ++ "kdc_offset", cc->kdc_offset, ++ "principal", princ, ++ "creds", creds); ++ if (jcc == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to pack JSON ccache structure on line %d: %s\n", ++ error.line, error.text); ++ json_decref(creds); ++ json_decref(princ); ++ return NULL; ++ } ++ ++ return jcc; ++} ++ ++static errno_t ccache_to_sec_kv(TALLOC_CTX *mem_ctx, ++ struct kcm_ccache *cc, ++ const char **_sec_key, ++ const char **_sec_value) ++{ ++ json_t *jcc = NULL; ++ char *jdump; ++ ++ jcc = ccache_to_json(cc); ++ if (jcc == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert ccache to JSON\n"); ++ return ERR_JSON_ENCODING; ++ } ++ ++ /* it would be more efficient to learn the size with json_dumpb and ++ * a NULL buffer, but that's only available since 2.10 ++ */ ++ jdump = json_dumps(jcc, JSON_INDENT(4) | JSON_ENSURE_ASCII); ++ if (jdump == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot dump JSON\n"); ++ return ERR_JSON_ENCODING; ++ } ++ ++ *_sec_key = sec_key_create(mem_ctx, cc->name, cc->uuid); ++ *_sec_value = talloc_strdup(mem_ctx, jdump); ++ free(jdump); ++ json_decref(jcc); ++ if (*_sec_key == NULL || *_sec_value == NULL) { ++ return ENOMEM; ++ } ++ ++ return EOK; ++} ++ ++errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx, ++ struct kcm_ccache *cc, ++ struct cli_creds *client, ++ const char **_url, ++ struct sss_iobuf **_payload) ++{ ++ errno_t ret; ++ const char *key; ++ const char *value; ++ const char *url; ++ struct sss_iobuf *payload; ++ TALLOC_CTX *tmp_ctx; ++ ++ tmp_ctx = talloc_new(mem_ctx); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = ccache_to_sec_kv(mem_ctx, cc, &key, &value); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert cache %s to JSON [%d]: %s\n", ++ cc->name, ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ url = sec_cc_url_create(tmp_ctx, client, key); ++ if (url == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ payload = sss_iobuf_init_readonly(tmp_ctx, ++ (const uint8_t *) value, ++ strlen(value)+1); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot create payload buffer\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++ *_url = talloc_steal(mem_ctx, url); ++ *_payload = talloc_steal(mem_ctx, payload); ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static errno_t sec_value_to_json(const char *input, ++ json_t **_root) ++{ ++ json_t *root = NULL; ++ json_error_t error; ++ int ok; ++ ++ root = json_loads(input, 0, &error); ++ if (root == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to parse JSON payload on line %d: %s\n", ++ error.line, error.text); ++ return ERR_JSON_DECODING; ++ } ++ ++ ok = json_is_object(root); ++ if (!ok) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Json data is not an object.\n"); ++ json_decref(root); ++ return ERR_JSON_DECODING; ++ } ++ ++ *_root = root; ++ return EOK; ++} ++ ++/* ++ * ccache unmarshalling from JSON ++ */ ++static errno_t json_element_to_krb5_data(TALLOC_CTX *mem_ctx, ++ json_t *element, ++ krb5_data *data) ++{ ++ const char *str_value; ++ size_t str_len; ++ ++ /* FIXME - it might be cleaner to use stringn here, but the libjansson ++ * version on RHEL-7 doesn't support that ++ */ ++ str_value = json_string_value(element); ++ if (str_value == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "JSON element not a string\n"); ++ return EINVAL; ++ } ++ str_len = strlen(str_value); ++ ++ data->data = talloc_strndup(mem_ctx, str_value, str_len); ++ if (data->data == NULL) { ++ return ENOMEM; ++ } ++ data->length = str_len; ++ ++ return EOK; ++} ++ ++static errno_t json_array_to_krb5_data(TALLOC_CTX *mem_ctx, ++ json_t *array, ++ krb5_data **_data, ++ size_t *_len) ++{ ++ errno_t ret; ++ int ok; ++ size_t len; ++ size_t idx; ++ json_t *element; ++ krb5_data *data; ++ ++ ok = json_is_array(array); ++ if (!ok) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Json object is not an array.\n"); ++ return ERR_JSON_DECODING; ++ } ++ ++ len = json_array_size(array); ++ if (len == 0) { ++ *_data = NULL; ++ *_len = 0; ++ return EOK; ++ } ++ ++ data = talloc_zero_array(mem_ctx, krb5_data, len); ++ if (data == NULL) { ++ return ENOMEM; ++ } ++ ++ json_array_foreach(array, idx, element) { ++ ret = json_element_to_krb5_data(data, element, &data[idx]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert krb5 data element from JSON"); ++ talloc_free(data); ++ return ret; ++ } ++ } ++ ++ *_data = data; ++ *_len = len; ++ return EOK; ++} ++ ++static errno_t json_to_princ(TALLOC_CTX *mem_ctx, ++ json_t *js_princ, ++ krb5_principal *_princ) ++{ ++ errno_t ret; ++ json_t *components = NULL; ++ int ok; ++ krb5_principal princ = NULL; ++ TALLOC_CTX *tmp_ctx = NULL; ++ char *realm_str; ++ size_t realm_size; ++ json_error_t error; ++ ++ ok = json_is_object(js_princ); ++ if (!ok) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Json principal is not an object.\n"); ++ ret = ERR_JSON_DECODING; ++ goto done; ++ } ++ ++ tmp_ctx = talloc_new(mem_ctx); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ princ = talloc_zero(tmp_ctx, struct krb5_principal_data); ++ if (princ == NULL) { ++ return ENOMEM; ++ } ++ princ->magic = KV5M_PRINCIPAL; ++ ++ /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson ++ * version on RHEL-7 doesn't support that ++ */ ++ ret = json_unpack_ex(js_princ, ++ &error, ++ JSON_STRICT, ++ "{s:i, s:s, s:o}", ++ "type", &princ->type, ++ "realm", &realm_str, ++ "components", &components); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to unpack JSON princ structure on line %d: %s\n", ++ error.line, error.text); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ realm_size = strlen(realm_str); ++ ++ princ->realm.data = talloc_strndup(mem_ctx, realm_str, realm_size); ++ if (princ->realm.data == NULL) { ++ return ENOMEM; ++ } ++ princ->realm.length = realm_size; ++ princ->realm.magic = 0; ++ ++ ret = json_array_to_krb5_data(princ, components, ++ &princ->data, ++ (size_t *) &princ->length); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert principal from JSON"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ *_princ = talloc_steal(mem_ctx, princ); ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static errno_t json_elem_to_cred(TALLOC_CTX *mem_ctx, ++ json_t *element, ++ struct kcm_cred **_crd) ++{ ++ errno_t ret; ++ char *uuid_str; ++ json_error_t error; ++ uuid_t uuid; ++ struct sss_iobuf *cred_blob; ++ const char *base64_cred_blob; ++ struct kcm_cred *crd; ++ uint8_t *outbuf; ++ size_t outbuf_size; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ ret = json_unpack_ex(element, ++ &error, ++ JSON_STRICT, ++ "{s:s, s:s}", ++ "uuid", &uuid_str, ++ "payload", &base64_cred_blob); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to unpack JSON cred structure on line %d: %s\n", ++ error.line, error.text); ++ return EINVAL; ++ } ++ ++ uuid_parse(uuid_str, uuid); ++ ++ tmp_ctx = talloc_new(mem_ctx); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ outbuf = sss_base64_decode(tmp_ctx, base64_cred_blob, &outbuf_size); ++ if (outbuf == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot decode cred blob\n"); ++ ret = EIO; ++ goto done; ++ } ++ ++ cred_blob = sss_iobuf_init_readonly(tmp_ctx, outbuf, outbuf_size); ++ if (cred_blob == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ crd = kcm_cred_new(tmp_ctx, uuid, cred_blob); ++ if (crd == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = EOK; ++ *_crd = talloc_steal(mem_ctx, crd); ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static errno_t json_to_creds(struct kcm_ccache *cc, ++ json_t *jcreds) ++{ ++ errno_t ret; ++ int ok; ++ size_t idx; ++ json_t *value; ++ struct kcm_cred *crd; ++ ++ ok = json_is_array(jcreds); ++ if (!ok) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Json creds object is not an array.\n"); ++ return ERR_JSON_DECODING; ++ } ++ ++ json_array_foreach(jcreds, idx, value) { ++ ret = json_elem_to_cred(cc, value, &crd); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert JSON cred element [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ ret = kcm_cc_store_creds(cc, crd); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot store creds in ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ } ++ ++ return EOK; ++} ++ ++static errno_t sec_json_value_to_ccache(struct kcm_ccache *cc, ++ json_t *root) ++{ ++ errno_t ret; ++ json_t *princ = NULL; ++ json_t *creds = NULL; ++ json_error_t error; ++ int version; ++ ++ ret = json_unpack_ex(root, ++ &error, ++ JSON_STRICT, ++ "{s:i, s:i, s:o, s:o}", ++ "version", &version, ++ "kdc_offset", &cc->kdc_offset, ++ "principal", &princ, ++ "creds", &creds); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to unpack JSON creds structure on line %d: %s\n", ++ error.line, error.text); ++ return EINVAL; ++ } ++ ++ if (version != KS_JSON_VERSION) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Expected version %d, received version %d\n", ++ KS_JSON_VERSION, version); ++ return EINVAL; ++ } ++ ++ ret = json_to_princ(cc, princ, &cc->client); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot store JSON to principal [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ ret = json_to_creds(cc, creds); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot store JSON to creds [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return EOK; ++ } ++ ++ return EOK; ++} ++ ++/* ++ * sec_key is a concatenation of the ccache's UUID and name ++ * sec_value is the JSON dump of the ccache contents ++ */ ++errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx, ++ const char *sec_key, ++ const char *sec_value, ++ struct cli_creds *client, ++ struct kcm_ccache **_cc) ++{ ++ errno_t ret; ++ json_t *root = NULL; ++ struct kcm_ccache *cc = NULL; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ ret = sec_value_to_json(sec_value, &root); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot store secret to JSN [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ tmp_ctx = talloc_new(mem_ctx); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cc = talloc_zero(tmp_ctx, struct kcm_ccache); ++ if (cc == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ /* We rely on sssd-secrets only searching the user's subtree so we ++ * set the ownership to the client ++ */ ++ cc->owner.uid = cli_creds_get_uid(client); ++ cc->owner.gid = cli_creds_get_gid(client); ++ ++ ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannt parse secret key [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = sec_json_value_to_ccache(cc, root); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannt parse secret value [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ *_cc = talloc_steal(mem_ctx, cc); ++done: ++ talloc_free(tmp_ctx); ++ json_decref(root); ++ return ret; ++} +diff --git a/src/responder/kcm/kcmsrv_ccache_secrets.c b/src/responder/kcm/kcmsrv_ccache_secrets.c +new file mode 100644 +index 0000000000000000000000000000000000000000..8be7daea59bd6e04ab8393aae9f8adc29df11c21 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_ccache_secrets.c +@@ -0,0 +1,2169 @@ ++/* ++ SSSD ++ ++ KCM Server - ccache storage in sssd-secrets ++ ++ Copyright (C) Red Hat, 2016 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "util/util.h" ++#include "util/crypto/sss_crypto.h" ++#include "util/tev_curl.h" ++#include "responder/kcm/kcmsrv_ccache_pvt.h" ++#include "responder/kcm/kcmsrv_ccache_be.h" ++ ++#ifndef SSSD_SECRETS_SOCKET ++#define SSSD_SECRETS_SOCKET VARDIR"/run/secrets.socket" ++#endif /* SSSD_SECRETS_SOCKET */ ++ ++#ifndef SEC_TIMEOUT ++#define SEC_TIMEOUT 5 ++#endif /* SEC_TIMEOUT */ ++ ++/* Just to keep the name of the ccache readable */ ++#define MAX_CC_NUM 99999 ++ ++/* Compat definition of json_array_foreach for older systems */ ++#ifndef json_array_foreach ++#define json_array_foreach(array, idx, value) \ ++ for(idx = 0; \ ++ idx < json_array_size(array) && (value = json_array_get(array, idx)); \ ++ idx++) ++#endif ++ ++static const char *find_by_name(const char **sec_key_list, ++ const char *name) ++{ ++ const char *sec_name = NULL; ++ ++ if (sec_key_list == NULL) { ++ return NULL; ++ } ++ ++ for (int i = 0; sec_key_list[i]; i++) { ++ if (sec_key_match_name(sec_key_list[i], name)) { ++ sec_name = sec_key_list[i]; ++ break; ++ } ++ } ++ ++ return sec_name; ++} ++ ++static const char *find_by_uuid(const char **sec_key_list, ++ uuid_t uuid) ++{ ++ const char *sec_name = NULL; ++ ++ if (sec_key_list == NULL) { ++ return NULL; ++ } ++ ++ for (int i = 0; sec_key_list[i]; i++) { ++ if (sec_key_match_uuid(sec_key_list[i], uuid)) { ++ sec_name = sec_key_list[i]; ++ break; ++ } ++ } ++ ++ return sec_name; ++} ++ ++static const char *sec_headers[] = { ++ "Content-type: application/octet-stream", ++ NULL, ++}; ++ ++struct ccdb_sec { ++ struct tcurl_ctx *tctx; ++}; ++ ++static errno_t http2errno(int http_code) ++{ ++ if (http_code != 200) { ++ DEBUG(SSSDBG_OP_FAILURE, "HTTP request returned %d\n", http_code); ++ } ++ ++ switch (http_code) { ++ case 200: ++ return EOK; ++ case 404: ++ return ERR_NO_CREDS; ++ case 400: ++ return ERR_INPUT_PARSE; ++ case 403: ++ return EACCES; ++ case 409: ++ return EEXIST; ++ case 413: ++ return E2BIG; ++ case 507: ++ return ENOSPC; ++ } ++ ++ return EIO; ++} ++ ++/* ++ * Helper request to list all UUID+name pairs ++ */ ++struct sec_list_state { ++ const char **sec_key_list; ++ size_t sec_key_list_len; ++}; ++ ++static void sec_list_done(struct tevent_req *subreq); ++static errno_t sec_list_parse(struct sss_iobuf *outbuf, ++ TALLOC_CTX *mem_ctx, ++ const char ***_list, ++ size_t *_list_len); ++ ++static struct tevent_req *sec_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ccdb_sec *secdb, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct sec_list_state *state = NULL; ++ errno_t ret; ++ const char *container_url; ++ ++ req = tevent_req_create(mem_ctx, &state, struct sec_list_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches in the secrets store\n"); ++ container_url = sec_container_url_create(state, client); ++ if (container_url == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ subreq = tcurl_http_send(state, ev, secdb->tctx, ++ TCURL_HTTP_GET, ++ SSSD_SECRETS_SOCKET, ++ container_url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, sec_list_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void sec_list_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct sec_list_state *state = tevent_req_data(req, ++ struct sec_list_state); ++ struct sss_iobuf *outbuf; ++ int http_code; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, &outbuf); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "list HTTP request failed [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code == 404) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Nothing to list\n"); ++ /* If no ccaches are found, return an empty list */ ++ state->sec_key_list = talloc_zero_array(state, const char *, 1); ++ if (state->sec_key_list == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ } else if (http_code == 200) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu items\n", state->sec_key_list_len); ++ ret = sec_list_parse(outbuf, state, ++ &state->sec_key_list, ++ &state->sec_key_list_len); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ } else { ++ tevent_req_error(req, http2errno(http_code)); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "list done\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t sec_list_parse(struct sss_iobuf *outbuf, ++ TALLOC_CTX *mem_ctx, ++ const char ***_list, ++ size_t *_list_len) ++{ ++ json_t *root; ++ uint8_t *sec_http_list; ++ json_error_t error; ++ json_t *element; ++ errno_t ret; ++ int ok; ++ size_t idx; ++ const char **list = NULL; ++ size_t list_len; ++ ++ sec_http_list = sss_iobuf_get_data(outbuf); ++ if (sec_http_list == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "No data in output buffer?\n"); ++ return EINVAL; ++ } ++ ++ root = json_loads((const char *) sec_http_list, 0, &error); ++ if (root == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to parse JSON payload on line %d: %s\n", ++ error.line, error.text); ++ return ERR_JSON_DECODING; ++ } ++ ++ ok = json_is_array(root); ++ if (!ok) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "list reply is not an object.\n"); ++ ret = ERR_JSON_DECODING; ++ goto done; ++ } ++ ++ list_len = json_array_size(root); ++ list = talloc_zero_array(mem_ctx, const char *, list_len + 1); ++ if (list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ json_array_foreach(root, idx, element) { ++ list[idx] = talloc_strdup(list, json_string_value(element)); ++ if (list[idx] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ ret = EOK; ++ *_list = list; ++ *_list_len = list_len; ++done: ++ if (ret != EOK) { ++ talloc_free(list); ++ } ++ json_decref(root); ++ return ret; ++} ++ ++static errno_t sec_list_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ const char ***_sec_key_list, ++ size_t *_sec_key_list_len) ++ ++{ ++ struct sec_list_state *state = tevent_req_data(req, ++ struct sec_list_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ if (_sec_key_list != NULL) { ++ *_sec_key_list = talloc_steal(mem_ctx, state->sec_key_list); ++ } ++ if (_sec_key_list_len != NULL) { ++ *_sec_key_list_len = state->sec_key_list_len; ++ } ++ return EOK; ++} ++ ++/* ++ * Helper request to get a ccache by key ++ */ ++struct sec_get_state { ++ const char *sec_key; ++ struct cli_creds *client; ++ ++ struct kcm_ccache *cc; ++}; ++ ++static void sec_get_done(struct tevent_req *subreq); ++ ++static struct tevent_req *sec_get_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ccdb_sec *secdb, ++ struct cli_creds *client, ++ const char *sec_key) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct sec_get_state *state = NULL; ++ errno_t ret; ++ const char *cc_url; ++ ++ req = tevent_req_create(mem_ctx, &state, struct sec_get_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->sec_key = sec_key; ++ state->client = client; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Retrieving ccache %s\n", sec_key); ++ ++ cc_url = sec_cc_url_create(state, state->client, state->sec_key); ++ if (cc_url == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ subreq = tcurl_http_send(state, ++ ev, ++ secdb->tctx, ++ TCURL_HTTP_GET, ++ SSSD_SECRETS_SOCKET, ++ cc_url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, sec_get_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void sec_get_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct sec_get_state *state = tevent_req_data(req, ++ struct sec_get_state); ++ struct sss_iobuf *outbuf; ++ const char *sec_value; ++ int http_code; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, &outbuf); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "GET HTTP request failed [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code != 200) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "GET operation returned HTTP error %d\n", http_code); ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ sec_value = (const char *) sss_iobuf_get_data(outbuf); ++ if (sec_value == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "No data in output buffer\n"); ++ tevent_req_error(req, EINVAL); ++ return; ++ } ++ ++ ret = sec_kv_to_ccache(state, ++ state->sec_key, ++ sec_value, ++ state->client, ++ &state->cc); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot convert JSON keyval to ccache blob [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "GET done\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t sec_get_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct sec_get_state *state = tevent_req_data(req, struct sec_get_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++/* ++ * Helper request to get a ccache name or ID ++ */ ++struct sec_get_ccache_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ struct cli_creds *client; ++ const char *name; ++ uuid_t uuid; ++ ++ const char *sec_key; ++ ++ struct kcm_ccache *cc; ++}; ++ ++static void sec_get_ccache_list_done(struct tevent_req *subreq); ++static void sec_get_ccache_done(struct tevent_req *subreq); ++ ++static struct tevent_req *sec_get_ccache_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ccdb_sec *secdb, ++ struct cli_creds *client, ++ const char *name, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct sec_get_ccache_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct sec_get_ccache_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ state->client = client; ++ state->name = name; ++ uuid_copy(state->uuid, uuid); ++ ++ if ((name == NULL && uuid_is_null(uuid)) ++ || (name != NULL && !uuid_is_null(uuid))) { ++ DEBUG(SSSDBG_OP_FAILURE, "Expected one of name, uuid to be set\n"); ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ subreq = sec_list_send(state, ev, secdb, client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, sec_get_ccache_list_done, req); ++ return req; ++ ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void sec_get_ccache_list_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct sec_get_ccache_state *state = tevent_req_data(req, ++ struct sec_get_ccache_state); ++ const char **sec_key_list; ++ ++ ret = sec_list_recv(subreq, state, &sec_key_list, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot list keys [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (state->name != NULL) { ++ state->sec_key = find_by_name(sec_key_list, state->name); ++ } else { ++ state->sec_key = find_by_uuid(sec_key_list, state->uuid); ++ } ++ ++ if (state->sec_key == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot find item in the ccache list\n"); ++ /* Don't error out, just return an empty list */ ++ tevent_req_done(req); ++ return; ++ } ++ ++ subreq = sec_get_send(state, ++ state->ev, ++ state->secdb, ++ state->client, ++ state->sec_key); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, sec_get_ccache_done, req); ++} ++ ++static void sec_get_ccache_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct sec_get_ccache_state *state = tevent_req_data(req, ++ struct sec_get_ccache_state); ++ ++ ret = sec_get_recv(subreq, state, &state->cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot resolve key to ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static errno_t sec_get_ccache_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct sec_get_ccache_state *state = tevent_req_data(req, ++ struct sec_get_ccache_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++/* ++ * The actual sssd-secrets back end ++ */ ++static errno_t ccdb_sec_init(struct kcm_ccdb *db) ++{ ++ struct ccdb_sec *secdb = NULL; ++ ++ secdb = talloc_zero(db, struct ccdb_sec); ++ if (secdb == NULL) { ++ return ENOMEM; ++ } ++ ++ secdb->tctx = tcurl_init(secdb, db->ev); ++ if (secdb->tctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot initialize tcurl\n"); ++ talloc_zfree(secdb); ++ return ENOMEM; ++ } ++ ++ /* We just need the random numbers to generate pseudo-random ccache names ++ * and avoid conflicts */ ++ srand(time(NULL)); ++ ++ db->db_handle = secdb; ++ return EOK; ++} ++ ++/* ++ * Helper request to get a ccache by key ++ */ ++struct sec_patch_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ struct cli_creds *client; ++ ++ const char *sec_key_url; ++ struct sss_iobuf *sec_value; ++}; ++ ++static void sec_patch_del_done(struct tevent_req *subreq); ++static void sec_patch_put_done(struct tevent_req *subreq); ++ ++static struct tevent_req *sec_patch_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ccdb_sec *secdb, ++ struct cli_creds *client, ++ const char *sec_key_url, ++ struct sss_iobuf *sec_value) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct sec_patch_state *state = NULL; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct sec_patch_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ state->client = client; ++ state->sec_key_url = sec_key_url; ++ state->sec_value = sec_value; ++ ++ subreq = tcurl_http_send(state, state->ev, ++ state->secdb->tctx, ++ TCURL_HTTP_DELETE, ++ SSSD_SECRETS_SOCKET, ++ state->sec_key_url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, sec_patch_del_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void sec_patch_del_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct sec_patch_state *state = tevent_req_data(req, ++ struct sec_patch_state); ++ int http_code; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot delete key [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code == 404) { ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Key %s does not exist, moving on\n", state->sec_key_url); ++ } else if (http_code != 200) { ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Adding new payload\n"); ++ ++ subreq = tcurl_http_send(state, ++ state->ev, ++ state->secdb->tctx, ++ TCURL_HTTP_PUT, ++ SSSD_SECRETS_SOCKET, ++ state->sec_key_url, ++ sec_headers, ++ state->sec_value, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, sec_patch_put_done, req); ++} ++ ++static void sec_patch_put_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct sec_patch_state *state = tevent_req_data(req, ++ struct sec_patch_state); ++ int http_code; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot put new value [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code != 200) { ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n"); ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n"); ++ tevent_req_done(req); ++} ++ ++ ++static errno_t sec_patch_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++/* The operations between the KCM and sssd-secrets */ ++ ++struct ccdb_sec_nextid_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ struct cli_creds *client; ++ ++ unsigned int nextid; ++ char *nextid_name; ++ ++ int maxtries; ++ int numtry; ++}; ++ ++static errno_t ccdb_sec_nextid_generate(struct tevent_req *req); ++static void ccdb_sec_nextid_list_done(struct tevent_req *subreq); ++ ++/* Generate a unique ID */ ++/* GET the name from secrets, if doesn't exist, OK, if exists, try again */ ++static struct tevent_req *ccdb_sec_nextid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct ccdb_sec_nextid_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_nextid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ state->client = client; ++ ++ state->maxtries = 3; ++ state->numtry = 0; ++ ++ ret = ccdb_sec_nextid_generate(req); ++ if (ret != EOK) { ++ goto immediate; ++ } ++ ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static errno_t ccdb_sec_nextid_generate(struct tevent_req *req) ++{ ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_nextid_state *state = tevent_req_data(req, ++ struct ccdb_sec_nextid_state); ++ ++ if (state->numtry >= state->maxtries) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to find a random ccache in %d tries\n", state->numtry); ++ return EBUSY; ++ } ++ ++ state->nextid = rand() % MAX_CC_NUM; ++ state->nextid_name = talloc_asprintf(state, "%"SPRIuid":%u", ++ cli_creds_get_uid(state->client), ++ state->nextid); ++ if (state->nextid_name == NULL) { ++ return ENOMEM; ++ } ++ ++ subreq = sec_list_send(state, state->ev, state->secdb, state->client); ++ if (subreq == NULL) { ++ return ENOMEM; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_nextid_list_done, req); ++ ++ state->numtry++; ++ return EOK; ++} ++ ++static void ccdb_sec_nextid_list_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_nextid_state *state = tevent_req_data(req, ++ struct ccdb_sec_nextid_state); ++ const char **sec_key_list; ++ size_t sec_key_list_len; ++ size_t i; ++ ++ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot list keys [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ for (i = 0; i < sec_key_list_len; i++) { ++ if (sec_key_match_name(sec_key_list[i], state->nextid_name) == true) { ++ break; ++ } ++ } ++ ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Failed to find a random key, trying again..\n"); ++ if (i < sec_key_list_len) { ++ /* Try again */ ++ ret = ccdb_sec_nextid_generate(req); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Generated new ccache name %u\n", state->nextid); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_nextid_recv(struct tevent_req *req, ++ unsigned int *_nextid) ++{ ++ struct ccdb_sec_nextid_state *state = tevent_req_data(req, ++ struct ccdb_sec_nextid_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_nextid = state->nextid; ++ return EOK; ++} ++ ++/* IN: HTTP PUT $base/default -d 'uuid' */ ++/* We chose only UUID here to avoid issues later with renaming */ ++struct ccdb_sec_set_default_state { ++}; ++ ++static void ccdb_sec_set_default_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_set_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_set_default_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ struct sss_iobuf *uuid_iobuf; ++ errno_t ret; ++ const char *url; ++ char uuid_str[UUID_STR_SIZE]; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_set_default_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ uuid_unparse(uuid, uuid_str); ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Setting the default ccache to %s\n", uuid_str); ++ ++ url = sec_dfl_url_create(state, client); ++ if (url == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ uuid_iobuf = sss_iobuf_init_readonly(state, ++ (uint8_t *) uuid_str, ++ UUID_STR_SIZE); ++ if (uuid_iobuf == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ subreq = sec_patch_send(state, ev, secdb, client, url, uuid_iobuf); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_set_default_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_set_default_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ ++ ret = sec_patch_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sec_patch request failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Set the default ccache\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_set_default_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++/* IN: HTTP GET $base/default */ ++/* OUT: uuid */ ++struct ccdb_sec_get_default_state { ++ uuid_t uuid; ++}; ++ ++static void ccdb_sec_get_default_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_get_default_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_get_default_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ const char *url; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_get_default_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting the default ccache\n"); ++ url = sec_dfl_url_create(state, client); ++ if (url == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ subreq = tcurl_http_send(state, ev, secdb->tctx, ++ TCURL_HTTP_GET, ++ SSSD_SECRETS_SOCKET, ++ url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_get_default_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_get_default_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ int http_code; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct ccdb_sec_get_default_state *state = tevent_req_data(req, ++ struct ccdb_sec_get_default_state); ++ struct sss_iobuf *outbuf; ++ size_t uuid_size; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, &outbuf); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Communication with the secrets responder failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code == 404) { ++ /* Return a NULL uuid */ ++ uuid_clear(state->uuid); ++ tevent_req_done(req); ++ return; ++ } else if (http_code != 200) { ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ uuid_size = sss_iobuf_get_len(outbuf); ++ if (uuid_size != UUID_STR_SIZE) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unexpected UUID size %zu\n", uuid_size); ++ tevent_req_error(req, EIO); ++ return; ++ } ++ ++ uuid_parse((const char *) sss_iobuf_get_data(outbuf), state->uuid); ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Got the default ccache\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_get_default_recv(struct tevent_req *req, ++ uuid_t uuid) ++{ ++ struct ccdb_sec_get_default_state *state = tevent_req_data(req, ++ struct ccdb_sec_get_default_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ uuid_copy(uuid, state->uuid); ++ return EOK; ++} ++ ++/* HTTP GET $base/ccache/ */ ++/* OUT: a list of */ ++struct ccdb_sec_list_state { ++ uuid_t *uuid_list; ++}; ++ ++static void ccdb_sec_list_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_list_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_list_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_list_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches\n"); ++ ++ subreq = sec_list_send(state, ev, secdb, client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_list_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_list_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_list_state *state = tevent_req_data(req, ++ struct ccdb_sec_list_state); ++ const char **sec_key_list; ++ size_t sec_key_list_len; ++ ++ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Communication with the secrets responder failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu ccaches\n", sec_key_list_len); ++ ++ state->uuid_list = talloc_array(state, uuid_t, sec_key_list_len + 1); ++ if (state->uuid_list == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ for (size_t i = 0; i < sec_key_list_len; i++) { ++ ret = sec_key_get_uuid(sec_key_list[i], ++ state->uuid_list[i]); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ } ++ /* Sentinel */ ++ uuid_clear(state->uuid_list[sec_key_list_len]); ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all caches done\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_list_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ uuid_t **_uuid_list) ++{ ++ struct ccdb_sec_list_state *state = tevent_req_data(req, ++ struct ccdb_sec_list_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list); ++ return EOK; ++} ++ ++struct ccdb_sec_getbyuuid_state { ++ struct kcm_ccache *cc; ++}; ++ ++ ++/* HTTP GET $base/ccache/ */ ++/* OUT: a list of */ ++/* for each item in list, compare with the uuid: portion */ ++/* HTTP GET $base/ccache/uuid:name */ ++/* return result */ ++static void ccdb_sec_getbyuuid_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_getbyuuid_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ errno_t ret; ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_getbyuuid_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_getbyuuid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by UUID\n"); ++ ++ subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_getbyuuid_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_getbyuuid_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_getbyuuid_state *state = tevent_req_data(req, ++ struct ccdb_sec_getbyuuid_state); ++ ++ ret = sec_get_ccache_recv(subreq, state, &state->cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_getbyuuid_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct ccdb_sec_getbyuuid_state *state = tevent_req_data(req, ++ struct ccdb_sec_getbyuuid_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++/* HTTP GET $base/ccache/ */ ++/* OUT: a list of */ ++/* for each item in list, compare with the :name portion */ ++/* HTTP GET $base/ccache/uuid:name */ ++/* return result */ ++struct ccdb_sec_getbyname_state { ++ struct kcm_ccache *cc; ++}; ++ ++static void ccdb_sec_getbyname_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_getbyname_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name) ++{ ++ errno_t ret; ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_getbyname_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ uuid_t null_uuid; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_getbyname_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ uuid_clear(null_uuid); ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by name\n"); ++ ++ subreq = sec_get_ccache_send(state, ev, secdb, client, name, null_uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_getbyname_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_getbyname_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_getbyname_state *state = tevent_req_data(req, ++ struct ccdb_sec_getbyname_state); ++ ++ ret = sec_get_ccache_recv(subreq, state, &state->cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_getbyname_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ccache **_cc) ++{ ++ struct ccdb_sec_getbyname_state *state = tevent_req_data(req, ++ struct ccdb_sec_getbyname_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_cc = talloc_steal(mem_ctx, state->cc); ++ return EOK; ++} ++ ++struct ccdb_sec_name_by_uuid_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ struct cli_creds *client; ++ ++ uuid_t uuid; ++ ++ const char *name; ++}; ++ ++static void ccdb_sec_name_by_uuid_done(struct tevent_req *subreq); ++ ++struct tevent_req *ccdb_sec_name_by_uuid_send(TALLOC_CTX *sec_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_name_by_uuid_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ errno_t ret; ++ ++ req = tevent_req_create(sec_ctx, &state, struct ccdb_sec_name_by_uuid_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ state->client = client; ++ uuid_copy(state->uuid, uuid); ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Translating UUID to name\n"); ++ ++ subreq = sec_list_send(state, state->ev, state->secdb, state->client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_name_by_uuid_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_name_by_uuid_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_name_by_uuid_state *state = tevent_req_data(req, ++ struct ccdb_sec_name_by_uuid_state); ++ const char **sec_key_list; ++ const char *name; ++ size_t sec_key_list_len; ++ size_t i; ++ ++ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ for (i = 0; i < sec_key_list_len; i++) { ++ if (sec_key_match_uuid(sec_key_list[i], state->uuid) == true) { ++ /* Match, copy name */ ++ name = sec_key_get_name(sec_key_list[i]); ++ if (name == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Malformed key, cannot get name\n"); ++ tevent_req_error(req, EINVAL); ++ return; ++ } ++ ++ state->name = talloc_strdup(state, name); ++ if (state->name == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n"); ++ tevent_req_done(req); ++ return; ++ } ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "No such UUID\n"); ++ tevent_req_error(req, ERR_NO_CREDS); ++ return; ++} ++ ++errno_t ccdb_sec_name_by_uuid_recv(struct tevent_req *req, ++ TALLOC_CTX *sec_ctx, ++ const char **_name) ++{ ++ struct ccdb_sec_name_by_uuid_state *state = tevent_req_data(req, ++ struct ccdb_sec_name_by_uuid_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_name = talloc_steal(sec_ctx, state->name); ++ return EOK; ++} ++ ++struct ccdb_sec_uuid_by_name_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ struct cli_creds *client; ++ ++ const char *name; ++ ++ uuid_t uuid; ++}; ++ ++static void ccdb_sec_uuid_by_name_done(struct tevent_req *subreq); ++ ++struct tevent_req *ccdb_sec_uuid_by_name_send(TALLOC_CTX *sec_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ const char *name) ++{ ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_uuid_by_name_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ errno_t ret; ++ ++ req = tevent_req_create(sec_ctx, &state, struct ccdb_sec_uuid_by_name_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ state->client = client; ++ state->name = name; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Translating name to UUID\n"); ++ ++ subreq = sec_list_send(state, state->ev, state->secdb, state->client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_uuid_by_name_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_uuid_by_name_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_uuid_by_name_state *state = tevent_req_data(req, ++ struct ccdb_sec_uuid_by_name_state); ++ const char **sec_key_list; ++ size_t sec_key_list_len; ++ size_t i; ++ ++ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ for (i = 0; i < sec_key_list_len; i++) { ++ if (sec_key_match_name(sec_key_list[i], state->name) == true) { ++ /* Match, copy UUID */ ++ ret = sec_key_get_uuid(sec_key_list[i], state->uuid); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Malformed key, cannot get UUID\n"); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by name\n"); ++ tevent_req_done(req); ++ return; ++ } ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "No such name\n"); ++ tevent_req_error(req, ERR_NO_CREDS); ++ return; ++} ++ ++errno_t ccdb_sec_uuid_by_name_recv(struct tevent_req *req, ++ TALLOC_CTX *sec_ctx, ++ uuid_t _uuid) ++{ ++ struct ccdb_sec_uuid_by_name_state *state = tevent_req_data(req, ++ struct ccdb_sec_uuid_by_name_state); ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ uuid_copy(_uuid, state->uuid); ++ return EOK; ++} ++ ++/* HTTP POST $base to create the container */ ++/* HTTP PUT $base to create the container. Since PUT errors out on duplicates, at least ++ * we fail consistently here and don't overwrite the ccache on concurrent requests ++ */ ++struct ccdb_sec_create_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ ++ const char *key_url; ++ struct sss_iobuf *ccache_payload; ++}; ++ ++static void ccdb_sec_container_done(struct tevent_req *subreq); ++static void ccdb_sec_ccache_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_create_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ struct kcm_ccache *cc) ++{ ++ struct tevent_req *subreq = NULL; ++ struct tevent_req *req = NULL; ++ struct ccdb_sec_create_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ errno_t ret; ++ const char *container_url; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_create_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Creating ccache storage for %s\n", cc->name); ++ ++ /* Do the encoding asap so that if we fail, we don't even attempt any ++ * writes */ ++ ret = kcm_ccache_to_sec_input(state, cc, client, &state->key_url, &state->ccache_payload); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert cache %s to JSON [%d]: %s\n", ++ cc->name, ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ container_url = sec_container_url_create(state, client); ++ if (container_url == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Creating the ccache container\n"); ++ subreq = tcurl_http_send(state, ev, secdb->tctx, ++ TCURL_HTTP_POST, ++ SSSD_SECRETS_SOCKET, ++ container_url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_container_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_container_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ int http_code; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct ccdb_sec_create_state *state = tevent_req_data(req, ++ struct ccdb_sec_create_state); ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Communication with the secrets responder failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ /* Conflict is not an error as multiple ccaches are under the same ++ * container */ ++ if (http_code == 409) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Container already exists, ignoring\n"); ++ } else if (http_code != 200) { ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to create the ccache container\n"); ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache container created\n"); ++ DEBUG(SSSDBG_TRACE_INTERNAL, "creating empty ccache payload\n"); ++ ++ subreq = tcurl_http_send(state, ++ state->ev, ++ state->secdb->tctx, ++ TCURL_HTTP_PUT, ++ SSSD_SECRETS_SOCKET, ++ state->key_url, ++ sec_headers, ++ state->ccache_payload, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_ccache_done, req); ++} ++ ++static void ccdb_sec_ccache_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ int http_code; ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct ccdb_sec_create_state *state = tevent_req_data(req, ++ struct ccdb_sec_create_state); ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Communication with the secrets responder failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code != 200) { ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n"); ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_create_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++struct ccdb_sec_mod_cred_state { ++ struct tevent_context *ev; ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ struct kcm_mod_ctx *mod_cc; ++ ++ struct ccdb_sec *secdb; ++}; ++ ++static void ccdb_sec_mod_cred_get_done(struct tevent_req *subreq); ++static void ccdb_sec_mod_cred_patch_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_mod_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct kcm_mod_ctx *mod_cc) ++{ ++ errno_t ret; ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_mod_cred_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_mod_cred_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->db =db; ++ state->client = client; ++ state->secdb = secdb; ++ state->mod_cc = mod_cc; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Modifying ccache\n"); ++ ++ subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, *ccdb_sec_mod_cred_get_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_mod_cred_get_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_mod_cred_state *state = tevent_req_data(req, ++ struct ccdb_sec_mod_cred_state); ++ struct kcm_ccache *cc; ++ const char *url; ++ struct sss_iobuf *payload; ++ ++ ret = sec_get_ccache_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (cc == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "No such ccache\n"); ++ tevent_req_error(req, ERR_NO_CREDS); ++ return; ++ } ++ ++ kcm_mod_cc(cc, state->mod_cc); ++ ++ ret = kcm_ccache_to_sec_input(state, cc, state->client, &url, &payload); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to marshall modified ccache to payload [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = sec_patch_send(state, ++ state->ev, ++ state->secdb, ++ state->client, ++ url, ++ payload); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_mod_cred_patch_done, req); ++} ++ ++static void ccdb_sec_mod_cred_patch_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ ++ ret = sec_patch_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sec_patch request failed [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache modified\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_mod_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++struct ccdb_sec_store_cred_state { ++ struct tevent_context *ev; ++ struct kcm_ccdb *db; ++ struct cli_creds *client; ++ struct sss_iobuf *cred_blob; ++ ++ struct ccdb_sec *secdb; ++}; ++ ++static void ccdb_sec_store_cred_get_done(struct tevent_req *subreq); ++static void ccdb_sec_store_cred_patch_done(struct tevent_req *subreq); ++ ++/* HTTP DEL/PUT $base/ccache/uuid:name */ ++static struct tevent_req *ccdb_sec_store_cred_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid, ++ struct sss_iobuf *cred_blob) ++{ ++ errno_t ret; ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_store_cred_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_store_cred_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->db =db; ++ state->client = client; ++ state->cred_blob = cred_blob; ++ state->secdb = secdb; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Storing creds in ccache\n"); ++ ++ subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, *ccdb_sec_store_cred_get_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_store_cred_get_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_store_cred_state *state = tevent_req_data(req, ++ struct ccdb_sec_store_cred_state); ++ struct kcm_ccache *cc; ++ const char *url; ++ struct sss_iobuf *payload; ++ ++ ret = sec_get_ccache_recv(subreq, state, &cc); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ ret = kcm_cc_store_cred_blob(cc, state->cred_blob); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot store credentials to ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ ret = kcm_ccache_to_sec_input(state, cc, state->client, &url, &payload); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to marshall modified ccache to payload [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = sec_patch_send(state, ++ state->ev, ++ state->secdb, ++ state->client, ++ url, ++ payload); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_store_cred_patch_done, req); ++} ++ ++static void ccdb_sec_store_cred_patch_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ ++ ret = sec_patch_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sec_patch request failed [%d]: %s\n", ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache creds stored\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_store_cred_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++/* HTTP DELETE $base/ccache/uuid:name */ ++struct ccdb_sec_delete_state { ++ struct tevent_context *ev; ++ struct ccdb_sec *secdb; ++ struct cli_creds *client; ++ uuid_t uuid; ++ ++ size_t sec_key_list_len; ++}; ++ ++static void ccdb_sec_delete_list_done(struct tevent_req *subreq); ++static void ccdb_sec_delete_cc_done(struct tevent_req *subreq); ++static void ccdb_sec_delete_container_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ccdb_sec_delete_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ccdb *db, ++ struct cli_creds *client, ++ uuid_t uuid) ++{ ++ errno_t ret; ++ struct tevent_req *req = NULL; ++ struct tevent_req *subreq = NULL; ++ struct ccdb_sec_delete_state *state = NULL; ++ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec); ++ ++ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_delete_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->secdb = secdb; ++ state->client = client; ++ uuid_copy(state->uuid, uuid); ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Deleting ccache\n"); ++ ++ subreq = sec_list_send(state, ev, secdb, client); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_delete_list_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void ccdb_sec_delete_list_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_delete_state *state = tevent_req_data(req, ++ struct ccdb_sec_delete_state); ++ const char **sec_key_list; ++ const char *sec_key; ++ const char *cc_url; ++ ++ ret = sec_list_recv(subreq, ++ state, ++ &sec_key_list, ++ &state->sec_key_list_len); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (sec_key_list == 0) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "No ccaches to delete\n"); ++ tevent_req_done(req); ++ return; ++ } ++ ++ sec_key = find_by_uuid(sec_key_list, state->uuid); ++ if (sec_key == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find ccache by UUID\n"); ++ tevent_req_done(req); ++ return; ++ } ++ ++ cc_url = sec_cc_url_create(state, state->client, sec_key); ++ if (cc_url == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ subreq = tcurl_http_send(state, state->ev, ++ state->secdb->tctx, ++ TCURL_HTTP_DELETE, ++ SSSD_SECRETS_SOCKET, ++ cc_url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_delete_cc_done, req); ++} ++ ++static void ccdb_sec_delete_cc_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_delete_state *state = tevent_req_data(req, ++ struct ccdb_sec_delete_state); ++ int http_code; ++ const char *container_url; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot delete ccache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code != 200) { ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (state->sec_key_list_len != 1) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "There are other ccaches, done\n"); ++ tevent_req_done(req); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing ccache container\n"); ++ ++ container_url = sec_container_url_create(state, state->client); ++ if (container_url == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ subreq = tcurl_http_send(state, state->ev, ++ state->secdb->tctx, ++ TCURL_HTTP_DELETE, ++ SSSD_SECRETS_SOCKET, ++ container_url, ++ sec_headers, ++ NULL, ++ SEC_TIMEOUT); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ tevent_req_set_callback(subreq, ccdb_sec_delete_container_done, req); ++} ++ ++static void ccdb_sec_delete_container_done(struct tevent_req *subreq) ++{ ++ errno_t ret; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct ccdb_sec_delete_state *state = tevent_req_data(req, ++ struct ccdb_sec_delete_state); ++ int http_code; ++ ++ ret = tcurl_http_recv(state, subreq, &http_code, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot delete ccache container [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ if (http_code != 200) { ++ ret = http2errno(http_code); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Removed ccache container\n"); ++ tevent_req_done(req); ++} ++ ++static errno_t ccdb_sec_delete_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ return EOK; ++} ++ ++const struct kcm_ccdb_ops ccdb_sec_ops = { ++ .init = ccdb_sec_init, ++ ++ .nextid_send = ccdb_sec_nextid_send, ++ .nextid_recv = ccdb_sec_nextid_recv, ++ ++ .set_default_send = ccdb_sec_set_default_send, ++ .set_default_recv = ccdb_sec_set_default_recv, ++ ++ .get_default_send = ccdb_sec_get_default_send, ++ .get_default_recv = ccdb_sec_get_default_recv, ++ ++ .list_send = ccdb_sec_list_send, ++ .list_recv = ccdb_sec_list_recv, ++ ++ .getbyname_send = ccdb_sec_getbyname_send, ++ .getbyname_recv = ccdb_sec_getbyname_recv, ++ ++ .getbyuuid_send = ccdb_sec_getbyuuid_send, ++ .getbyuuid_recv = ccdb_sec_getbyuuid_recv, ++ ++ .name_by_uuid_send = ccdb_sec_name_by_uuid_send, ++ .name_by_uuid_recv = ccdb_sec_name_by_uuid_recv, ++ ++ .uuid_by_name_send = ccdb_sec_uuid_by_name_send, ++ .uuid_by_name_recv = ccdb_sec_uuid_by_name_recv, ++ ++ .create_send = ccdb_sec_create_send, ++ .create_recv = ccdb_sec_create_recv, ++ ++ .mod_send = ccdb_sec_mod_send, ++ .mod_recv = ccdb_sec_mod_recv, ++ ++ .store_cred_send = ccdb_sec_store_cred_send, ++ .store_cred_recv = ccdb_sec_store_cred_recv, ++ ++ .delete_send = ccdb_sec_delete_send, ++ .delete_recv = ccdb_sec_delete_recv, ++}; +diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c +new file mode 100644 +index 0000000000000000000000000000000000000000..8eff2f501066c70a8730cd3d4dc41b92d7a03e4c +--- /dev/null ++++ b/src/tests/cmocka/test_kcm_json_marshalling.c +@@ -0,0 +1,234 @@ ++/* ++ Copyright (C) 2017 Red Hat ++ ++ SSSD tests: Test KCM JSON marshalling ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "util/util_creds.h" ++#include "responder/kcm/kcmsrv_ccache.h" ++#include "responder/kcm/kcmsrv_ccache_be.h" ++#include "tests/cmocka/common_mock.h" ++ ++#define TEST_REALM "TESTREALM" ++#define TEST_PRINC_COMPONENT "PRINC_NAME" ++ ++#define TEST_CREDS "TESTCREDS" ++ ++const struct kcm_ccdb_ops ccdb_mem_ops; ++const struct kcm_ccdb_ops ccdb_sec_ops; ++ ++struct kcm_marshalling_test_ctx { ++ krb5_context kctx; ++ krb5_principal princ; ++}; ++ ++static int setup_kcm_marshalling(void **state) ++{ ++ struct kcm_marshalling_test_ctx *test_ctx; ++ krb5_error_code kerr; ++ ++ test_ctx = talloc_zero(NULL, struct kcm_marshalling_test_ctx); ++ assert_non_null(test_ctx); ++ ++ kerr = krb5_init_context(&test_ctx->kctx); ++ assert_int_equal(kerr, 0); ++ ++ kerr = krb5_build_principal(test_ctx->kctx, ++ &test_ctx->princ, ++ sizeof(TEST_REALM)-1, TEST_REALM, ++ TEST_PRINC_COMPONENT, NULL); ++ assert_int_equal(kerr, 0); ++ ++ *state = test_ctx; ++ return 0; ++} ++ ++static int teardown_kcm_marshalling(void **state) ++{ ++ struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state, ++ struct kcm_marshalling_test_ctx); ++ assert_non_null(test_ctx); ++ ++ krb5_free_principal(test_ctx->kctx, test_ctx->princ); ++ krb5_free_context(test_ctx->kctx); ++ talloc_free(test_ctx); ++ return 0; ++} ++ ++static void assert_cc_name_equal(struct kcm_ccache *cc1, ++ struct kcm_ccache *cc2) ++{ ++ const char *name1, *name2; ++ ++ name1 = kcm_cc_get_name(cc1); ++ name2 = kcm_cc_get_name(cc2); ++ assert_string_equal(name1, name2); ++} ++ ++static void assert_cc_uuid_equal(struct kcm_ccache *cc1, ++ struct kcm_ccache *cc2) ++{ ++ uuid_t u1, u2; ++ errno_t ret; ++ ++ ret = kcm_cc_get_uuid(cc1, u1); ++ assert_int_equal(ret, EOK); ++ ret = kcm_cc_get_uuid(cc2, u2); ++ assert_int_equal(ret, EOK); ++ ret = uuid_compare(u1, u2); ++ assert_int_equal(ret, 0); ++} ++ ++static void assert_cc_princ_equal(struct kcm_ccache *cc1, ++ struct kcm_ccache *cc2) ++{ ++ krb5_principal p1; ++ krb5_principal p2; ++ char *name1; ++ char *name2; ++ krb5_error_code kerr; ++ ++ p1 = kcm_cc_get_client_principal(cc1); ++ p2 = kcm_cc_get_client_principal(cc1); ++ ++ kerr = krb5_unparse_name(NULL, p1, &name1); ++ assert_int_equal(kerr, 0); ++ kerr = krb5_unparse_name(NULL, p2, &name2); ++ assert_int_equal(kerr, 0); ++ ++ assert_string_equal(name1, name2); ++ krb5_free_unparsed_name(NULL, name1); ++ krb5_free_unparsed_name(NULL, name2); ++} ++ ++static void assert_cc_offset_equal(struct kcm_ccache *cc1, ++ struct kcm_ccache *cc2) ++{ ++ int32_t off1; ++ int32_t off2; ++ ++ off1 = kcm_cc_get_offset(cc1); ++ off2 = kcm_cc_get_offset(cc2); ++ assert_int_equal(off1, off2); ++} ++ ++static void assert_cc_equal(struct kcm_ccache *cc1, ++ struct kcm_ccache *cc2) ++{ ++ assert_cc_name_equal(cc1, cc2); ++ assert_cc_uuid_equal(cc1, cc2); ++ assert_cc_princ_equal(cc1, cc2); ++ assert_cc_offset_equal(cc1, cc2); ++} ++ ++static void test_kcm_ccache_marshall_unmarshall(void **state) ++{ ++ struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state, ++ struct kcm_marshalling_test_ctx); ++ errno_t ret; ++ struct cli_creds owner; ++ struct kcm_ccache *cc; ++ struct kcm_ccache *cc2; ++ const char *url; ++ struct sss_iobuf *payload; ++ const char *name; ++ const char *key; ++ uint8_t *data; ++ ++ owner.ucred.uid = getuid(); ++ owner.ucred.gid = getuid(); ++ ++ name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid()); ++ assert_non_null(name); ++ ++ ret = kcm_cc_new(test_ctx, ++ test_ctx->kctx, ++ &owner, ++ name, ++ test_ctx->princ, ++ &cc); ++ assert_int_equal(ret, EOK); ++ ++ ret = kcm_ccache_to_sec_input(test_ctx, ++ cc, ++ &owner, ++ &url, ++ &payload); ++ assert_int_equal(ret, EOK); ++ ++ key = strrchr(url, '/') + 1; ++ assert_non_null(key); ++ ++ data = sss_iobuf_get_data(payload); ++ assert_non_null(data); ++ ++ ret = sec_kv_to_ccache(test_ctx, ++ key, ++ (const char *) data, ++ &owner, ++ &cc2); ++ assert_int_equal(ret, EOK); ++ ++ assert_cc_equal(cc, cc2); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ poptContext pc; ++ int opt; ++ int rv; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall, ++ setup_kcm_marshalling, ++ teardown_kcm_marshalling), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++ /* Even though normally the tests should clean up after themselves ++ * they might not after a failed run. Remove the old db to be sure */ ++ tests_set_cwd(); ++ ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ ++ return rv; ++} +diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py +index ad1e4923bfe339cb040464757431d2ef3bf57ce1..11f80a1803b4ad9b8e8857bf9a8a244d4816f0a2 100644 +--- a/src/tests/intg/test_kcm.py ++++ b/src/tests/intg/test_kcm.py +@@ -27,7 +27,8 @@ import signal + import kdc + import krb5utils + import config +-from util import unindent, run_shell ++from util import unindent ++from test_secrets import create_sssd_secrets_fixture + + class KcmTestEnv(object): + def __init__(self, k5kdc, k5util): +@@ -107,15 +108,8 @@ def create_sssd_kcm_fixture(sock_path, request): + return kcm_pid + + +-@pytest.fixture +-def setup_for_kcm(request, kdc_instance): +- """ +- Just set up the local provider for tests and enable the KCM +- responder +- """ +- kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket") +- +- sssd_conf = unindent("""\ ++def create_sssd_conf(kcm_path, ccache_storage): ++ return unindent("""\ + [sssd] + domains = local + services = nss +@@ -125,8 +119,11 @@ def setup_for_kcm(request, kdc_instance): + + [kcm] + socket_path = {kcm_path} ++ ccache_storage = {ccache_storage} + """).format(**locals()) + ++ ++def common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf): + kcm_socket_include = unindent(""" + [libdefaults] + default_ccache_name = KCM: +@@ -142,11 +139,35 @@ def setup_for_kcm(request, kdc_instance): + return KcmTestEnv(kdc_instance, k5util) + + +-def test_kcm_init_list_destroy(setup_for_kcm): ++@pytest.fixture ++def setup_for_kcm_mem(request, kdc_instance): ++ """ ++ Just set up the local provider for tests and enable the KCM ++ responder ++ """ ++ kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket") ++ sssd_conf = create_sssd_conf(kcm_path, "memory") ++ return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf) ++ ++@pytest.fixture ++def setup_secrets(request): ++ create_sssd_secrets_fixture(request) ++ ++@pytest.fixture ++def setup_for_kcm_sec(request, kdc_instance): ++ """ ++ Just set up the local provider for tests and enable the KCM ++ responder ++ """ ++ kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket") ++ sssd_conf = create_sssd_conf(kcm_path, "secrets") ++ return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf) ++ ++ ++def kcm_init_list_destroy(testenv): + """ + Test that kinit, kdestroy and klist work with KCM + """ +- testenv = setup_for_kcm + testenv.k5kdc.add_principal("kcmtest", "Secret123") + + ok = testenv.k5util.has_principal("kcmtest@KCMTEST") +@@ -172,12 +193,22 @@ def test_kcm_init_list_destroy(setup_for_kcm): + assert nprincs == 0 + + +-def test_kcm_overwrite(setup_for_kcm): ++def test_kcm_mem_init_list_destroy(setup_for_kcm_mem): ++ testenv = setup_for_kcm_mem ++ kcm_init_list_destroy(testenv) ++ ++ ++def test_kcm_sec_init_list_destroy(setup_for_kcm_sec, ++ setup_secrets): ++ testenv = setup_for_kcm_sec ++ kcm_init_list_destroy(testenv) ++ ++ ++def kcm_overwrite(testenv): + """ + That that reusing a ccache reinitializes the cache and doesn't + add the same principal twice + """ +- testenv = setup_for_kcm + testenv.k5kdc.add_principal("kcmtest", "Secret123") + exp_ccache = {'kcmtest@KCMTEST': ['krbtgt/KCMTEST@KCMTEST']} + +@@ -192,12 +223,22 @@ def test_kcm_overwrite(setup_for_kcm): + assert exp_ccache == testenv.k5util.list_all_princs() + + +-def test_collection_init_list_destroy(setup_for_kcm): ++def test_kcm_mem_overwrite(setup_for_kcm_mem): ++ testenv = setup_for_kcm_mem ++ kcm_overwrite(testenv) ++ ++ ++def test_kcm_sec_overwrite(setup_for_kcm_sec, ++ setup_secrets): ++ testenv = setup_for_kcm_sec ++ kcm_overwrite(testenv) ++ ++ ++def collection_init_list_destroy(testenv): + """ + Test that multiple principals and service tickets can be stored + in a collection. + """ +- testenv = setup_for_kcm + testenv.k5kdc.add_principal("alice", "alicepw") + testenv.k5kdc.add_principal("bob", "bobpw") + testenv.k5kdc.add_principal("carol", "carolpw") +@@ -241,7 +282,11 @@ def test_collection_init_list_destroy(setup_for_kcm): + + out = testenv.k5util.kdestroy() + assert out == 0 +- assert testenv.k5util.default_principal() == 'bob@KCMTEST' ++ # If the default is removed, KCM just uses whetever is the first entry ++ # in the collection as the default. And sine the KCM back ends don't ++ # guarantee if they are FIFO or LIFO, just check for either alice or bob ++ assert testenv.k5util.default_principal() in \ ++ ['alice@KCMTEST', 'bob@KCMTEST'] + cc_coll = testenv.k5util.list_all_princs() + assert len(cc_coll) == 2 + assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST'] +@@ -255,11 +300,21 @@ def test_collection_init_list_destroy(setup_for_kcm): + #assert len(cc_coll) == 0 + + +-def test_kswitch(setup_for_kcm): ++def test_kcm_mem_collection_init_list_destroy(setup_for_kcm_mem): ++ testenv = setup_for_kcm_mem ++ collection_init_list_destroy(testenv) ++ ++ ++def test_kcm_sec_collection_init_list_destroy(setup_for_kcm_sec, ++ setup_secrets): ++ testenv = setup_for_kcm_sec ++ collection_init_list_destroy(testenv) ++ ++ ++def exercise_kswitch(testenv): + """ + Test switching between principals + """ +- testenv = setup_for_kcm + testenv.k5kdc.add_principal("alice", "alicepw") + testenv.k5kdc.add_principal("bob", "bobpw") + testenv.k5kdc.add_principal("host/somehostname") +@@ -301,12 +356,22 @@ def test_kswitch(setup_for_kcm): + 'host/differenthostname@KCMTEST']) + + +-def test_subsidiaries(setup_for_kcm): ++def test_kcm_mem_kswitch(setup_for_kcm_mem): ++ testenv = setup_for_kcm_mem ++ exercise_kswitch(testenv) ++ ++ ++def test_kcm_sec_kswitch(setup_for_kcm_sec, ++ setup_secrets): ++ testenv = setup_for_kcm_sec ++ exercise_kswitch(testenv) ++ ++ ++def exercise_subsidiaries(testenv): + """ + Test that subsidiary caches are usable and KCM: without specifying UID + can be used to identify the collection + """ +- testenv = setup_for_kcm + testenv.k5kdc.add_principal("alice", "alicepw") + testenv.k5kdc.add_principal("bob", "bobpw") + testenv.k5kdc.add_principal("host/somehostname") +@@ -346,11 +411,21 @@ def test_subsidiaries(setup_for_kcm): + 'host/differenthostname@KCMTEST']) + + +-def test_kdestroy_nocache(setup_for_kcm): ++def test_kcm_mem_subsidiaries(setup_for_kcm_mem): ++ testenv = setup_for_kcm_mem ++ exercise_subsidiaries(testenv) ++ ++ ++def test_kcm_sec_subsidiaries(setup_for_kcm_sec, ++ setup_secrets): ++ testenv = setup_for_kcm_sec ++ exercise_subsidiaries(testenv) ++ ++ ++def kdestroy_nocache(testenv): + """ + Destroying a non-existing ccache should not throw an error + """ +- testenv = setup_for_kcm + testenv.k5kdc.add_principal("alice", "alicepw") + out, _, _ = testenv.k5util.kinit("alice", "alicepw") + assert out == 0 +@@ -359,3 +434,14 @@ def test_kdestroy_nocache(setup_for_kcm): + assert out == 0 + out = testenv.k5util.kdestroy() + assert out == 0 ++ ++ ++def test_kcm_mem_kdestroy_nocache(setup_for_kcm_mem): ++ testenv = setup_for_kcm_mem ++ exercise_subsidiaries(testenv) ++ ++ ++def test_kcm_sec_kdestroy_nocache(setup_for_kcm_sec, ++ setup_secrets): ++ testenv = setup_for_kcm_sec ++ exercise_subsidiaries(testenv) +diff --git a/src/util/util_errors.c b/src/util/util_errors.c +index 23cfdf9c6116a2c8e569a041e8289b65a112fd08..60c2f439b3e39b1dbff353e429114cb5a3070052 100644 +--- a/src/util/util_errors.c ++++ b/src/util/util_errors.c +@@ -109,6 +109,8 @@ struct err_string error_to_str[] = { + { "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */ + { "End of credential cache reached" }, /* ERR_KCM_CC_END */ + { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */ ++ { "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */ ++ { "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */ + { "ERR_LAST" } /* ERR_LAST */ + }; + +diff --git a/src/util/util_errors.h b/src/util/util_errors.h +index 387d481616db1ed5e22b73fae82632a582fae946..4e9da814702e2cd46edc52fd5c2ae5f640602609 100644 +--- a/src/util/util_errors.h ++++ b/src/util/util_errors.h +@@ -131,6 +131,8 @@ enum sssd_errors { + ERR_KCM_OP_NOT_IMPLEMENTED, + ERR_KCM_CC_END, + ERR_KCM_WRONG_CCNAME_FORMAT, ++ ERR_JSON_ENCODING, ++ ERR_JSON_DECODING, + ERR_LAST /* ALWAYS LAST */ + }; + +-- +2.9.3 + diff --git a/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch b/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch new file mode 100644 index 0000000..2d65a32 --- /dev/null +++ b/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch @@ -0,0 +1,219 @@ +From 6236b14d20151053f5ccad1fc8ee9b669d4b0ffb Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 14 Mar 2017 11:17:05 +0100 +Subject: [PATCH 34/36] KCM: Make the secrets ccache back end configurable, + make secrets the default +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a new option 'ccache_storage' that allows to select either the +memory back end or the secrets back end. The secrets back end is the +default one and this option is even undocumented. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/confdb/confdb.h | 1 + + src/config/cfg_rules.ini | 1 + + src/responder/kcm/kcm.c | 49 ++++++++++++++++++++++++++++++++---- + src/responder/kcm/kcmsrv_ccache.c | 2 +- + src/responder/kcm/kcmsrv_ccache.h | 6 +---- + src/responder/kcm/kcmsrv_ccache_be.h | 1 + + src/responder/kcm/kcmsrv_pvt.h | 7 ++++++ + 7 files changed, 56 insertions(+), 11 deletions(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index c443e869a7a6782265b42c4ad122867c4e3dd8e0..fb60675ca8beb2c2a157bf021ed9cad362742988 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -234,6 +234,7 @@ + /* KCM Service */ + #define CONFDB_KCM_CONF_ENTRY "config/kcm" + #define CONFDB_KCM_SOCKET "socket_path" ++#define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */ + + struct confdb_ctx; + struct config_file_ctx; +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 5e789c51658c51c0af1338d23d6c0f30f40bf119..67a5d1f5ad447a942b437ffd04a7f5d7cfe77d7f 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -280,6 +280,7 @@ option = fd_limit + option = client_idle_timeout + option = description + option = socket_path ++option = ccache_storage + + [rule/allowed_domain_options] + validator = ini_allowed_options +diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c +index 2c12ef215ce3967df183e51c20590c5f439d278f..063c27b915b4b92f6259496feee891aa94a498b6 100644 +--- a/src/responder/kcm/kcm.c ++++ b/src/responder/kcm/kcm.c +@@ -47,6 +47,37 @@ static int kcm_responder_ctx_destructor(void *ptr) + return 0; + } + ++static errno_t kcm_get_ccdb_be(struct kcm_ctx *kctx) ++{ ++ errno_t ret; ++ char *str_db; ++ ++ ret = confdb_get_string(kctx->rctx->cdb, ++ kctx->rctx, ++ kctx->rctx->confdb_service_path, ++ CONFDB_KCM_DB, ++ "secrets", ++ &str_db); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get the KCM database type [%d]: %s\n", ++ ret, strerror(ret)); ++ return ret; ++ } ++ ++ DEBUG(SSSDBG_CONF_SETTINGS, "KCM database type: %s\n", str_db); ++ if (strcasecmp(str_db, "memory") == 0) { ++ kctx->cc_be = CCDB_BE_MEMORY; ++ return EOK; ++ } else if (strcasecmp(str_db, "secrets") == 0) { ++ kctx->cc_be = CCDB_BE_SECRETS; ++ return EOK; ++ } ++ ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected KCM database type %s\n", str_db); ++ return EOK; ++} ++ + static int kcm_get_config(struct kcm_ctx *kctx) + { + int ret; +@@ -88,14 +119,21 @@ static int kcm_get_config(struct kcm_ctx *kctx) + &sock_name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, +- "Cannot get the client idle timeout [%d]: %s\n", ++ "Cannot get KCM socket path [%d]: %s\n", + ret, strerror(ret)); + goto done; + } + kctx->rctx->sock_name = sock_name; + ++ ret = kcm_get_ccdb_be(kctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get KCM ccache DB [%d]: %s\n", ++ ret, strerror(ret)); ++ goto done; ++ } ++ + ret = EOK; +- + done: + return ret; + } +@@ -111,7 +149,8 @@ static int kcm_data_destructor(void *ptr) + } + + static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx, +- struct tevent_context *ev) ++ struct tevent_context *ev, ++ enum kcm_ccdb_be cc_be) + { + struct kcm_resp_ctx *kcm_data; + krb5_error_code kret; +@@ -122,7 +161,7 @@ static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx, + return NULL; + } + +- kcm_data->db = kcm_ccdb_init(kcm_data, ev, CCDB_BE_MEMORY); ++ kcm_data->db = kcm_ccdb_init(kcm_data, ev, cc_be); + if (kcm_data->db == NULL) { + talloc_free(kcm_data); + return NULL; +@@ -176,7 +215,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, + goto fail; + } + +- kctx->kcm_data = kcm_data_setup(kctx, ev); ++ kctx->kcm_data = kcm_data_setup(kctx, ev, kctx->cc_be); + if (kctx->kcm_data == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error initializing responder data\n"); +diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c +index 2ae120269b0c62275ba2acdff6d6daa8b7077708..a22184e0f2b1300f3678bb343b6a110bf144a36b 100644 +--- a/src/responder/kcm/kcmsrv_ccache.c ++++ b/src/responder/kcm/kcmsrv_ccache.c +@@ -244,7 +244,7 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx, + break; + case CCDB_BE_SECRETS: + DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n"); +- /* Not implemented yet */ ++ ccdb->ops = &ccdb_sec_ops; + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database\n"); +diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h +index 18c8c47ad4ecb24521a85a1833b239c7a2a8bb45..36c481c5335d557318f0ed0204d93e533b4b6c41 100644 +--- a/src/responder/kcm/kcmsrv_ccache.h ++++ b/src/responder/kcm/kcmsrv_ccache.h +@@ -29,6 +29,7 @@ + #include "util/util.h" + #include "util/sss_iobuf.h" + #include "util/util_creds.h" ++#include "responder/kcm/kcmsrv_pvt.h" + + #define UUID_BYTES 16 + #define UUID_STR_SIZE 37 +@@ -113,11 +114,6 @@ errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc, + struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc); + struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd); + +-enum kcm_ccdb_be { +- CCDB_BE_MEMORY, +- CCDB_BE_SECRETS, +-}; +- + /* An opaque database that contains all the ccaches */ + struct kcm_ccdb; + +diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h +index 1bd2b6981e227675866e82e0a5389445cac4df66..a0796c298bae15a01adf612a6195a494ba6b4d23 100644 +--- a/src/responder/kcm/kcmsrv_ccache_be.h ++++ b/src/responder/kcm/kcmsrv_ccache_be.h +@@ -200,5 +200,6 @@ struct kcm_ccdb_ops { + }; + + extern const struct kcm_ccdb_ops ccdb_mem_ops; ++extern const struct kcm_ccdb_ops ccdb_sec_ops; + + #endif /* _KCMSRV_CCACHE_BE_ */ +diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h +index a29680246c1e616da75e1bbff951ce2fad66fb65..74f30c00014105ed533744779b02c5d42523722d 100644 +--- a/src/responder/kcm/kcmsrv_pvt.h ++++ b/src/responder/kcm/kcmsrv_pvt.h +@@ -49,6 +49,12 @@ struct kcm_resp_ctx { + struct kcm_ccdb *db; + }; + ++/* Supported ccache back ends */ ++enum kcm_ccdb_be { ++ CCDB_BE_MEMORY, ++ CCDB_BE_SECRETS, ++}; ++ + /* + * responder context that contains both the responder data, + * like the ccaches and the sssd-specific stuff like the +@@ -58,6 +64,7 @@ struct kcm_ctx { + struct resp_ctx *rctx; + int fd_limit; + char *socket_path; ++ enum kcm_ccdb_be cc_be; + + struct kcm_resp_ctx *kcm_data; + }; +-- +2.9.3 + diff --git a/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch b/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch new file mode 100644 index 0000000..338c394 --- /dev/null +++ b/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch @@ -0,0 +1,909 @@ +From 688e8d8ffe331a1dd75a78002bf212277f2d7664 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Mar 2017 13:25:11 +0100 +Subject: [PATCH 35/36] KCM: Queue requests by the same UID +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In order to avoid race conditions, we queue requests towards the KCM +responder coming from the same client UID. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 21 ++- + src/responder/kcm/kcm.c | 7 + + src/responder/kcm/kcmsrv_cmd.c | 10 +- + src/responder/kcm/kcmsrv_op_queue.c | 264 ++++++++++++++++++++++++++ + src/responder/kcm/kcmsrv_ops.c | 44 ++++- + src/responder/kcm/kcmsrv_ops.h | 1 + + src/responder/kcm/kcmsrv_pvt.h | 20 ++ + src/tests/cmocka/test_kcm_queue.c | 365 ++++++++++++++++++++++++++++++++++++ + 8 files changed, 721 insertions(+), 11 deletions(-) + create mode 100644 src/responder/kcm/kcmsrv_op_queue.c + create mode 100644 src/tests/cmocka/test_kcm_queue.c + +diff --git a/Makefile.am b/Makefile.am +index e9eaa312c91e3aee40bcf13c90a0ad8c683045d5..91afdd669aa11a3cc316588d3b51d7e8e9c91cb8 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -304,7 +304,10 @@ non_interactive_cmocka_based_tests += test_inotify + endif # HAVE_INOTIFY + + if BUILD_KCM +-non_interactive_cmocka_based_tests += test_kcm_json ++non_interactive_cmocka_based_tests += \ ++ test_kcm_json \ ++ test_kcm_queue \ ++ $(NULL) + endif # BUILD_KCM + + if BUILD_SAMBA +@@ -1501,6 +1504,7 @@ sssd_kcm_SOURCES = \ + src/responder/kcm/kcmsrv_ccache_json.c \ + src/responder/kcm/kcmsrv_ccache_secrets.c \ + src/responder/kcm/kcmsrv_ops.c \ ++ src/responder/kcm/kcmsrv_op_queue.c \ + src/util/sss_sockets.c \ + src/util/sss_krb5.c \ + src/util/sss_iobuf.c \ +@@ -3402,6 +3406,21 @@ test_kcm_json_LDADD = \ + $(SSSD_INTERNAL_LTLIBS) \ + libsss_test_common.la \ + $(NULL) ++ ++test_kcm_queue_SOURCES = \ ++ src/tests/cmocka/test_kcm_queue.c \ ++ src/responder/kcm/kcmsrv_op_queue.c \ ++ $(NULL) ++test_kcm_queue_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++test_kcm_queue_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(SSSD_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ $(NULL) ++ + endif # BUILD_KCM + + endif # HAVE_CMOCKA +diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c +index 063c27b915b4b92f6259496feee891aa94a498b6..3ee978066c589a5cc38b0ae358f741d389d00e7a 100644 +--- a/src/responder/kcm/kcm.c ++++ b/src/responder/kcm/kcm.c +@@ -133,6 +133,13 @@ static int kcm_get_config(struct kcm_ctx *kctx) + goto done; + } + ++ kctx->qctx = kcm_ops_queue_create(kctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot create KCM request queue [%d]: %s\n", ++ ret, strerror(ret)); ++ goto done; ++ } + ret = EOK; + done: + return ret; +diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c +index 537e88953fd1a190a9a73bcdd430d8e0db8f9291..81015de4a91617de3dca444cde95b636c8d5c0d1 100644 +--- a/src/responder/kcm/kcmsrv_cmd.c ++++ b/src/responder/kcm/kcmsrv_cmd.c +@@ -353,14 +353,18 @@ struct kcm_req_ctx { + + static void kcm_cmd_request_done(struct tevent_req *req); + +-static errno_t kcm_cmd_dispatch(struct kcm_req_ctx *req_ctx) ++static errno_t kcm_cmd_dispatch(struct kcm_ctx *kctx, ++ struct kcm_req_ctx *req_ctx) + { + struct tevent_req *req; + struct cli_ctx *cctx; + + cctx = req_ctx->cctx; + +- req = kcm_cmd_send(req_ctx, cctx->ev, req_ctx->kctx->kcm_data, ++ req = kcm_cmd_send(req_ctx, ++ cctx->ev, ++ kctx->qctx, ++ req_ctx->kctx->kcm_data, + req_ctx->cctx->creds, + &req_ctx->op_io.request, + req_ctx->op_io.op); +@@ -505,7 +509,7 @@ static void kcm_recv(struct cli_ctx *cctx) + /* do not read anymore, client is done sending */ + TEVENT_FD_NOT_READABLE(cctx->cfde); + +- ret = kcm_cmd_dispatch(req); ++ ret = kcm_cmd_dispatch(kctx, req); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to dispatch KCM operation [%d]: %s\n", +diff --git a/src/responder/kcm/kcmsrv_op_queue.c b/src/responder/kcm/kcmsrv_op_queue.c +new file mode 100644 +index 0000000000000000000000000000000000000000..f6c425dd5b64877c8b7401e488dd6565157fc9b5 +--- /dev/null ++++ b/src/responder/kcm/kcmsrv_op_queue.c +@@ -0,0 +1,264 @@ ++/* ++ SSSD ++ ++ KCM Server - the KCM operations wait queue ++ ++ Copyright (C) Red Hat, 2017 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "util/util.h" ++#include "util/util_creds.h" ++#include "responder/kcm/kcmsrv_pvt.h" ++ ++#define QUEUE_HASH_SIZE 32 ++ ++struct kcm_ops_queue_entry { ++ struct tevent_req *req; ++ uid_t uid; ++ ++ hash_table_t *wait_queue_hash; ++ ++ struct kcm_ops_queue_entry *head; ++ struct kcm_ops_queue_entry *next; ++ struct kcm_ops_queue_entry *prev; ++}; ++ ++struct kcm_ops_queue_ctx { ++ /* UID: dlist of kcm_ops_queue_entry */ ++ hash_table_t *wait_queue_hash; ++}; ++ ++/* ++ * Per-UID wait queue ++ * ++ * They key in the hash table is the UID of the peer. The value of each ++ * hash table entry is a linked list of kcm_ops_queue_entry structures ++ * which primarily hold the tevent request being queued. ++ */ ++struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx) ++{ ++ errno_t ret; ++ struct kcm_ops_queue_ctx *queue_ctx; ++ ++ queue_ctx = talloc_zero(mem_ctx, struct kcm_ops_queue_ctx); ++ if (queue_ctx == NULL) { ++ return NULL; ++ } ++ ++ ret = sss_hash_create_ex(mem_ctx, QUEUE_HASH_SIZE, ++ &queue_ctx->wait_queue_hash, 0, 0, 0, 0, ++ NULL, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_hash_create failed [%d]: %s\n", ret, sss_strerror(ret)); ++ talloc_free(queue_ctx); ++ return NULL; ++ } ++ ++ return queue_ctx; ++} ++ ++static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry) ++{ ++ int ret; ++ struct kcm_ops_queue_entry *next_entry; ++ hash_key_t key; ++ ++ if (entry == NULL) { ++ return 1; ++ } ++ ++ /* Take the next entry from the queue */ ++ next_entry = entry->next; ++ ++ /* Remove the current entry from the queue */ ++ DLIST_REMOVE(entry->head, entry); ++ ++ if (next_entry == NULL) { ++ key.type = HASH_KEY_ULONG; ++ key.ul = entry->uid; ++ ++ /* If this was the last entry, remove the key (the UID) from the ++ * hash table to signal the queue is empty ++ */ ++ ret = hash_delete(entry->wait_queue_hash, &key); ++ if (ret != HASH_SUCCESS) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to remove wait queue for user %"SPRIuid"\n", ++ entry->uid); ++ return 1; ++ } ++ return 0; ++ } ++ ++ /* Otherwise, mark the current head as done to run the next request */ ++ tevent_req_done(next_entry->req); ++ return 0; ++} ++ ++static errno_t kcm_op_queue_add(hash_table_t *wait_queue_hash, ++ struct kcm_ops_queue_entry *entry, ++ uid_t uid) ++{ ++ errno_t ret; ++ hash_key_t key; ++ hash_value_t value; ++ struct kcm_ops_queue_entry *head = NULL; ++ ++ key.type = HASH_KEY_ULONG; ++ key.ul = uid; ++ ++ ret = hash_lookup(wait_queue_hash, &key, &value); ++ switch (ret) { ++ case HASH_SUCCESS: ++ /* The key with this UID already exists. Its value is request queue ++ * for the UID, so let's just add the current request to the end ++ * of the queue and wait for the previous requests to finish ++ */ ++ if (value.type != HASH_VALUE_PTR) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n"); ++ return EINVAL; ++ } ++ ++ head = talloc_get_type(value.ptr, struct kcm_ops_queue_entry); ++ if (head == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid queue pointer\n"); ++ return EINVAL; ++ } ++ ++ entry->head = head; ++ DLIST_ADD_END(head, entry, struct kcm_ops_queue_entry *); ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Waiting in queue\n"); ++ ret = EAGAIN; ++ break; ++ ++ case HASH_ERROR_KEY_NOT_FOUND: ++ /* No request for this UID yet. Enqueue this request in case ++ * another one comes in and return EOK to run the current request ++ * immediatelly ++ */ ++ entry->head = entry; ++ ++ value.type = HASH_VALUE_PTR; ++ value.ptr = entry; ++ ++ ret = hash_enter(wait_queue_hash, &key, &value); ++ if (ret != HASH_SUCCESS) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n"); ++ return EIO; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Added a first request to the queue, running immediately\n"); ++ ret = EOK; ++ break; ++ ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n"); ++ return EIO; ++ } ++ ++ talloc_steal(wait_queue_hash, entry); ++ talloc_set_destructor(entry, kcm_op_queue_entry_destructor); ++ return ret; ++} ++ ++struct kcm_op_queue_state { ++ struct kcm_ops_queue_entry *entry; ++}; ++ ++/* ++ * Enqueue a request. ++ * ++ * If the request queue /for the given ID/ is empty, that is, if this ++ * request is the first one in the queue, run the request immediatelly. ++ * ++ * Otherwise just add it to the queue and wait until the previous request ++ * finishes and only at that point mark the current request as done, which ++ * will trigger calling the recv function and allow the request to continue. ++ */ ++struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ops_queue_ctx *qctx, ++ struct cli_creds *client) ++{ ++ errno_t ret; ++ struct tevent_req *req; ++ struct kcm_op_queue_state *state; ++ uid_t uid; ++ ++ uid = cli_creds_get_uid(client); ++ ++ req = tevent_req_create(mem_ctx, &state, struct kcm_op_queue_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ ++ state->entry = talloc_zero(state, struct kcm_ops_queue_entry); ++ if (state->entry == NULL) { ++ ret = ENOMEM; ++ goto immediate; ++ } ++ state->entry->req = req; ++ state->entry->uid = uid; ++ state->entry->wait_queue_hash = qctx->wait_queue_hash; ++ ++ DEBUG(SSSDBG_FUNC_DATA, ++ "Adding request by %"SPRIuid" to the wait queue\n", uid); ++ ++ ret = kcm_op_queue_add(qctx->wait_queue_hash, state->entry, uid); ++ if (ret == EOK) { ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Wait queue was empty, running immediately\n"); ++ goto immediate; ++ } else if (ret != EAGAIN) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot enqueue request [%d]: %s\n", ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Waiting our turn in the queue\n"); ++ return req; ++ ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++/* ++ * The queue recv function is called when this request is 'activated'. The queue ++ * entry should be allocated on the same memory context as the enqueued request ++ * to trigger freeing the kcm_ops_queue_entry structure destructor when the ++ * parent request is done and its tevent_req freed. This would in turn unblock ++ * the next request in the queue ++ */ ++errno_t kcm_op_queue_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ops_queue_entry **_entry) ++{ ++ struct kcm_op_queue_state *state = tevent_req_data(req, ++ struct kcm_op_queue_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *_entry = talloc_steal(mem_ctx, state->entry); ++ return EOK; ++} +diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c +index 50e8cc635424e15d53e3c8d122c5525044f59c8a..2feaf51f227ce9d90f706229ce7ac201b282dc6f 100644 +--- a/src/responder/kcm/kcmsrv_ops.c ++++ b/src/responder/kcm/kcmsrv_ops.c +@@ -67,17 +67,21 @@ struct kcm_op { + + struct kcm_cmd_state { + struct kcm_op *op; ++ struct tevent_context *ev; + ++ struct kcm_ops_queue_entry *queue_entry; + struct kcm_op_ctx *op_ctx; + struct sss_iobuf *reply; + + uint32_t op_ret; + }; + ++static void kcm_cmd_queue_done(struct tevent_req *subreq); + static void kcm_cmd_done(struct tevent_req *subreq); + + struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, ++ struct kcm_ops_queue_ctx *qctx, + struct kcm_resp_ctx *kcm_data, + struct cli_creds *client, + struct kcm_data *input, +@@ -93,6 +97,7 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, + return NULL; + } + state->op = op; ++ state->ev = ev; + + if (op == NULL) { + ret = EINVAL; +@@ -154,18 +159,43 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, + goto immediate; + } + +- subreq = op->fn_send(state, ev, state->op_ctx); ++ subreq = kcm_op_queue_send(state, ev, qctx, client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } ++ tevent_req_set_callback(subreq, kcm_cmd_queue_done, req); ++ return req; ++ ++immediate: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static void kcm_cmd_queue_done(struct tevent_req *subreq) ++{ ++ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); ++ struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state); ++ errno_t ret; ++ ++ /* When this request finishes, it frees the queue_entry which unblocks ++ * other requests by the same UID ++ */ ++ ret = kcm_op_queue_recv(subreq, state, &state->queue_entry); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot acquire queue slot\n"); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = state->op->fn_send(state, state->ev, state->op_ctx); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } + tevent_req_set_callback(subreq, kcm_cmd_done, req); +- return req; +- +-immediate: +- tevent_req_error(req, ret); +- tevent_req_post(req, ev); +- return req; + } + + static void kcm_cmd_done(struct tevent_req *subreq) +diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h +index 8e6feaf56a10b73c8b6375aea9ef26c392b5b492..67d9f86026bf949548471f2280c130ebefd2f865 100644 +--- a/src/responder/kcm/kcmsrv_ops.h ++++ b/src/responder/kcm/kcmsrv_ops.h +@@ -34,6 +34,7 @@ const char *kcm_opt_name(struct kcm_op *op); + + struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, ++ struct kcm_ops_queue_ctx *qctx, + struct kcm_resp_ctx *kcm_data, + struct cli_creds *client, + struct kcm_data *input, +diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h +index 74f30c00014105ed533744779b02c5d42523722d..f081a6bf0c6e40d2f8a83b07f9bbc2abacff359d 100644 +--- a/src/responder/kcm/kcmsrv_pvt.h ++++ b/src/responder/kcm/kcmsrv_pvt.h +@@ -25,6 +25,7 @@ + #include "config.h" + + #include ++#include + #include "responder/common/responder.h" + + /* +@@ -65,6 +66,7 @@ struct kcm_ctx { + int fd_limit; + char *socket_path; + enum kcm_ccdb_be cc_be; ++ struct kcm_ops_queue_ctx *qctx; + + struct kcm_resp_ctx *kcm_data; + }; +@@ -78,4 +80,22 @@ int kcm_connection_setup(struct cli_ctx *cctx); + */ + krb5_error_code sss2krb5_error(errno_t err); + ++/* We enqueue all requests by the same UID to avoid concurrency issues ++ * especially when performing multiple round-trips to sssd-secrets. In ++ * future, we should relax the queue to allow multiple read-only operations ++ * if no write operations are in progress. ++ */ ++struct kcm_ops_queue_entry; ++ ++struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx); ++ ++struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ops_queue_ctx *qctx, ++ struct cli_creds *client); ++ ++errno_t kcm_op_queue_recv(struct tevent_req *req, ++ TALLOC_CTX *mem_ctx, ++ struct kcm_ops_queue_entry **_entry); ++ + #endif /* __KCMSRV_PVT_H__ */ +diff --git a/src/tests/cmocka/test_kcm_queue.c b/src/tests/cmocka/test_kcm_queue.c +new file mode 100644 +index 0000000000000000000000000000000000000000..ba0d2405629960df5c623848f3207b7c80fa948d +--- /dev/null ++++ b/src/tests/cmocka/test_kcm_queue.c +@@ -0,0 +1,365 @@ ++/* ++ Copyright (C) 2017 Red Hat ++ ++ SSSD tests: Test KCM wait queue ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "util/util.h" ++#include "util/util_creds.h" ++#include "tests/cmocka/common_mock.h" ++#include "responder/kcm/kcmsrv_pvt.h" ++ ++#define INVALID_ID -1 ++#define FAST_REQ_ID 0 ++#define SLOW_REQ_ID 1 ++ ++#define FAST_REQ_DELAY 1 ++#define SLOW_REQ_DELAY 2 ++ ++struct timed_request_state { ++ struct tevent_context *ev; ++ struct kcm_ops_queue_ctx *qctx; ++ struct cli_creds *client; ++ int delay; ++ int req_id; ++ ++ struct kcm_ops_queue_entry *queue_entry; ++}; ++ ++static void timed_request_start(struct tevent_req *subreq); ++static void timed_request_done(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval current_time, ++ void *pvt); ++ ++static struct tevent_req *timed_request_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct kcm_ops_queue_ctx *qctx, ++ struct cli_creds *client, ++ int delay, ++ int req_id) ++{ ++ struct tevent_req *req; ++ struct tevent_req *subreq; ++ struct timed_request_state *state; ++ ++ req = tevent_req_create(mem_ctx, &state, struct timed_request_state); ++ if (req == NULL) { ++ return NULL; ++ } ++ state->ev = ev; ++ state->qctx = qctx; ++ state->client = client; ++ state->delay = delay; ++ state->req_id = req_id; ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Request %p with delay %d\n", req, delay); ++ ++ subreq = kcm_op_queue_send(state, ev, qctx, client); ++ if (subreq == NULL) { ++ return NULL; ++ } ++ tevent_req_set_callback(subreq, timed_request_start, req); ++ ++ return req; ++} ++ ++static void timed_request_start(struct tevent_req *subreq) ++{ ++ struct timeval tv; ++ struct tevent_timer *timeout = NULL; ++ struct tevent_req *req = tevent_req_callback_data(subreq, ++ struct tevent_req); ++ struct timed_request_state *state = tevent_req_data(req, ++ struct timed_request_state); ++ errno_t ret; ++ ++ ret = kcm_op_queue_recv(subreq, state, &state->queue_entry); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tv = tevent_timeval_current_ofs(state->delay, 0); ++ timeout = tevent_add_timer(state->ev, state, tv, timed_request_done, req); ++ if (timeout == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ return; ++} ++ ++static void timed_request_done(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval current_time, ++ void *pvt) ++{ ++ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); ++ DEBUG(SSSDBG_TRACE_ALL, "Request %p done\n", req); ++ tevent_req_done(req); ++} ++ ++static errno_t timed_request_recv(struct tevent_req *req, ++ int *req_id) ++{ ++ struct timed_request_state *state = tevent_req_data(req, ++ struct timed_request_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ *req_id = state->req_id; ++ return EOK; ++} ++ ++struct test_ctx { ++ struct kcm_ops_queue_ctx *qctx; ++ struct tevent_context *ev; ++ ++ int *req_ids; ++ ++ int num_requests; ++ int finished_requests; ++ bool done; ++ errno_t error; ++}; ++ ++static int setup_kcm_queue(void **state) ++{ ++ struct test_ctx *tctx; ++ ++ tctx = talloc_zero(NULL, struct test_ctx); ++ assert_non_null(tctx); ++ ++ tctx->ev = tevent_context_init(tctx); ++ assert_non_null(tctx->ev); ++ ++ tctx->qctx = kcm_ops_queue_create(tctx); ++ assert_non_null(tctx->qctx); ++ ++ *state = tctx; ++ return 0; ++} ++ ++static int teardown_kcm_queue(void **state) ++{ ++ struct test_ctx *tctx = talloc_get_type(*state, struct test_ctx); ++ talloc_free(tctx); ++ return 0; ++} ++ ++static void test_kcm_queue_done(struct tevent_req *req) ++{ ++ struct test_ctx *test_ctx = tevent_req_callback_data(req, ++ struct test_ctx); ++ int req_id = INVALID_ID; ++ ++ test_ctx->error = timed_request_recv(req, &req_id); ++ talloc_zfree(req); ++ if (test_ctx->error != EOK) { ++ test_ctx->done = true; ++ return; ++ } ++ ++ if (test_ctx->req_ids[test_ctx->finished_requests] != req_id) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Request %d finished, expected %d\n", ++ req_id, test_ctx->req_ids[test_ctx->finished_requests]); ++ test_ctx->error = EIO; ++ test_ctx->done = true; ++ return; ++ } ++ ++ test_ctx->finished_requests++; ++ if (test_ctx->finished_requests == test_ctx->num_requests) { ++ test_ctx->done = true; ++ return; ++ } ++} ++ ++/* ++ * Just make sure that a single pass through the queue works ++ */ ++static void test_kcm_queue_single(void **state) ++{ ++ struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); ++ struct tevent_req *req; ++ struct cli_creds client; ++ static int req_ids[] = { 0 }; ++ ++ client.ucred.uid = getuid(); ++ client.ucred.gid = getgid(); ++ ++ req = timed_request_send(test_ctx, ++ test_ctx->ev, ++ test_ctx->qctx, ++ &client, 1, 0); ++ assert_non_null(req); ++ tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); ++ ++ test_ctx->num_requests = 1; ++ test_ctx->req_ids = req_ids; ++ ++ while (test_ctx->done == false) { ++ tevent_loop_once(test_ctx->ev); ++ } ++ assert_int_equal(test_ctx->error, EOK); ++} ++ ++/* ++ * Test that multiple requests from the same ID wait for one another ++ */ ++static void test_kcm_queue_multi_same_id(void **state) ++{ ++ struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); ++ struct tevent_req *req; ++ struct cli_creds client; ++ /* The slow request will finish first because request from ++ * the same ID are serialized ++ */ ++ static int req_ids[] = { SLOW_REQ_ID, FAST_REQ_ID }; ++ ++ client.ucred.uid = getuid(); ++ client.ucred.gid = getgid(); ++ ++ req = timed_request_send(test_ctx, ++ test_ctx->ev, ++ test_ctx->qctx, ++ &client, ++ SLOW_REQ_DELAY, ++ SLOW_REQ_ID); ++ assert_non_null(req); ++ tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); ++ ++ req = timed_request_send(test_ctx, ++ test_ctx->ev, ++ test_ctx->qctx, ++ &client, ++ FAST_REQ_DELAY, ++ FAST_REQ_ID); ++ assert_non_null(req); ++ tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); ++ ++ test_ctx->num_requests = 2; ++ test_ctx->req_ids = req_ids; ++ ++ while (test_ctx->done == false) { ++ tevent_loop_once(test_ctx->ev); ++ } ++ assert_int_equal(test_ctx->error, EOK); ++} ++ ++/* ++ * Test that multiple requests from different IDs don't wait for one ++ * another and can run concurrently ++ */ ++static void test_kcm_queue_multi_different_id(void **state) ++{ ++ struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); ++ struct tevent_req *req; ++ struct cli_creds client; ++ /* In this test, the fast request will finish sooner because ++ * both requests are from different IDs, allowing them to run ++ * concurrently ++ */ ++ static int req_ids[] = { FAST_REQ_ID, SLOW_REQ_ID }; ++ ++ client.ucred.uid = getuid(); ++ client.ucred.gid = getgid(); ++ ++ req = timed_request_send(test_ctx, ++ test_ctx->ev, ++ test_ctx->qctx, ++ &client, ++ SLOW_REQ_DELAY, ++ SLOW_REQ_ID); ++ assert_non_null(req); ++ tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); ++ ++ client.ucred.uid = getuid() + 1; ++ client.ucred.gid = getgid() + 1; ++ ++ req = timed_request_send(test_ctx, ++ test_ctx->ev, ++ test_ctx->qctx, ++ &client, ++ FAST_REQ_DELAY, ++ FAST_REQ_ID); ++ assert_non_null(req); ++ tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); ++ ++ test_ctx->num_requests = 2; ++ test_ctx->req_ids = req_ids; ++ ++ while (test_ctx->done == false) { ++ tevent_loop_once(test_ctx->ev); ++ } ++ assert_int_equal(test_ctx->error, EOK); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ poptContext pc; ++ int opt; ++ int rv; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_kcm_queue_single, ++ setup_kcm_queue, ++ teardown_kcm_queue), ++ cmocka_unit_test_setup_teardown(test_kcm_queue_multi_same_id, ++ setup_kcm_queue, ++ teardown_kcm_queue), ++ cmocka_unit_test_setup_teardown(test_kcm_queue_multi_different_id, ++ setup_kcm_queue, ++ teardown_kcm_queue), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++ /* Even though normally the tests should clean up after themselves ++ * they might not after a failed run. Remove the old db to be sure */ ++ tests_set_cwd(); ++ ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ ++ return rv; ++} +-- +2.9.3 + diff --git a/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch b/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch new file mode 100644 index 0000000..a466a17 --- /dev/null +++ b/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch @@ -0,0 +1,55 @@ +From 7e6a8e7a6c37122fce8781e5f8e82458905960b3 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Mar 2017 14:26:54 +0100 +Subject: [PATCH 36/36] KCM: Idle-terminate the responder if the secrets back + end is used +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Existing with memory database would be fatal as we keep the ccaches in +memory then, but if the ccaches are stored in sssd-secrets, we can just +exit on idle. + +Reviewed-by: Michal Židek +Reviewed-by: Simo Sorce +--- + src/config/cfg_rules.ini | 1 + + src/responder/kcm/kcm.c | 9 +++++++++ + 2 files changed, 10 insertions(+) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 67a5d1f5ad447a942b437ffd04a7f5d7cfe77d7f..933ebccd828189d923d2186753dfbc0b5c0814ce 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -281,6 +281,7 @@ option = client_idle_timeout + option = description + option = socket_path + option = ccache_storage ++option = responder_idle_timeout + + [rule/allowed_domain_options] + validator = ini_allowed_options +diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c +index 3ee978066c589a5cc38b0ae358f741d389d00e7a..2202f96381a2622a2c5433e281172287b325f960 100644 +--- a/src/responder/kcm/kcm.c ++++ b/src/responder/kcm/kcm.c +@@ -133,6 +133,15 @@ static int kcm_get_config(struct kcm_ctx *kctx) + goto done; + } + ++ if (kctx->cc_be == CCDB_BE_SECRETS) { ++ ret = responder_setup_idle_timeout_config(kctx->rctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot set up idle responder timeout\n"); ++ /* Not fatal */ ++ } ++ } ++ + kctx->qctx = kcm_ops_queue_create(kctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, +-- +2.9.3 + diff --git a/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch b/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch new file mode 100644 index 0000000..bdf9ef7 --- /dev/null +++ b/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch @@ -0,0 +1,30 @@ +From bb7c93869d53a412ce2537180752158861755ac4 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 27 Mar 2017 11:59:01 +0200 +Subject: [PATCH 37/54] CONFIGURE: Fix fallback if pkg-config for uuid is + missing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +--- + src/external/libuuid.m4 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/external/libuuid.m4 b/src/external/libuuid.m4 +index 55411a2118bd787c9d50ba61f9cb791e1c76088d..323521c9224e443f40a15b417038d2dcea9b66f3 100644 +--- a/src/external/libuuid.m4 ++++ b/src/external/libuuid.m4 +@@ -4,7 +4,7 @@ AC_SUBST(UUID_CFLAGS) + PKG_CHECK_MODULES([UUID], [uuid], [found_uuid=yes], [found_uuid=no]) + + SSS_AC_EXPAND_LIB_DIR() +-AS_IF([test x"$found_uuid" = xyes], ++AS_IF([test x"$found_uuid" != xyes], + [AC_CHECK_HEADERS([uuid/uuid.h], + [AC_CHECK_LIB([uuid], + [uuid_generate], +-- +2.9.3 + diff --git a/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch b/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch new file mode 100644 index 0000000..5c7c3be --- /dev/null +++ b/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch @@ -0,0 +1,52 @@ +From 076bd32668f7ea194389ddd526ea81f9bf12fb0e Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 28 Mar 2017 12:18:13 +0200 +Subject: [PATCH 38/54] intg: fix configure failure with strict cflags +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The warning -Wstrict-prototypes is a part of AM_CFLAGS which was appended +for CFLAGS in make target intgcheck-prepare. And combination with +strict CFLAGS in environment variable (e.g. -Werror) caused failures. + +sh$ CFLAGS="-Werror" make intgcheck-prepare + +checking for gcc... gcc +checking whether the C compiler works... no +configure: error: in `/home/build/sssd/ci-build-debug/intg/bld': +configure: error: C compiler cannot create executables + +configure:3719: checking whether the C compiler works +configure:3741: gcc -g3 -O2 -Werror -D_FILE_OFFSET_BITS=64 + -D_LARGEFILE_SOURCE -Wall -Wshadow -Wstrict-prototypes + -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings + -Wundef -Werror-implicit-function-declaration + -Winit-self -Wmissing-include-dirs -fno-strict-aliasing + -std=gnu99 -DKCM_PEER_UID=1000 conftest.c >&5 +conftest.c:11:1: error: function declaration isn't a prototype [-Werror=strict-prototypes] + main () + ^~~~ +cc1: all warnings being treated as errors + +Reviewed-by: Pavel Březina +--- + Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index 91afdd669aa11a3cc316588d3b51d7e8e9c91cb8..359feddef298b0013c726409b7ba8b86504abf09 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -3486,7 +3486,7 @@ intgcheck-prepare: + --without-semanage \ + --enable-files-domain \ + $(INTGCHECK_CONFIGURE_FLAGS) \ +- CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \ ++ CFLAGS="$$CFLAGS -DKCM_PEER_UID=$$(id -u)"; \ + $(MAKE) $(AM_MAKEFLAGS) ; \ + : Force single-thread install to workaround concurrency issues; \ + $(MAKE) $(AM_MAKEFLAGS) -j1 install; \ +-- +2.9.3 + diff --git a/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch b/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch new file mode 100644 index 0000000..d10c770 --- /dev/null +++ b/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch @@ -0,0 +1,54 @@ +From c49fc8fded9ed87e37189bf877f04ef462974420 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 27 Mar 2017 14:44:29 +0200 +Subject: [PATCH 39/54] intg: Remove bashism from intgcheck-prepare + +env variable UID is not defined in all shells (eg. dash) +We also need to move invocation of "id -u" before nss_wraper +is enabled otherwise we would get root instead of real user. + +=================================== FAILURES =================================== +________________________ test_kcm_mem_init_list_destroy ________________________ +Traceback (most recent call last): + File "/home/build/sssd/src/tests/intg/test_kcm.py", line 198, in test_kcm_mem_init_list_destroy + kcm_init_list_destroy(testenv) + File "/home/build/sssd/src/tests/intg/test_kcm.py", line 183, in kcm_init_list_destroy + exp_ccname = testenv.ccname() + File "/home/build/sssd/src/tests/intg/test_kcm.py", line 45, in ccname + my_uid = self.my_uid() + File "/home/build/sssd/src/tests/intg/test_kcm.py", line 41, in my_uid + return int(s_myuid) +ValueError: invalid literal for int() with base 10: '' + +And we already use different approach in top level Makefile.am +3488) $(INTGCHECK_CONFIGURE_FLAGS) \ +3489) CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \ +3490) $(MAKE) $(AM_MAKEFLAGS) ; \ + +Reviewed-by: Jakub Hrozek +--- + src/tests/intg/Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 8526beace09b15c99aa27ac98d5038d1980f6a71..8566106e9017a8d3c9e7a3898a3a886e2966e346 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -76,6 +76,7 @@ intgcheck-installed: config.py passwd group + PATH="$(abs_builddir):$(abs_srcdir):$$PATH" \ + PYTHONPATH="$(abs_builddir):$(abs_srcdir)" \ + LDB_MODULES_PATH="$(DESTDIR)$(ldblibdir)" \ ++ NON_WRAPPED_UID=$$(id -u) \ + LD_PRELOAD="$$nss_wrapper $$uid_wrapper" \ + NSS_WRAPPER_PASSWD="$(abs_builddir)/passwd" \ + NSS_WRAPPER_GROUP="$(abs_builddir)/group" \ +@@ -83,6 +84,5 @@ intgcheck-installed: config.py passwd group + NSS_WRAPPER_MODULE_FN_PREFIX="sss" \ + UID_WRAPPER=1 \ + UID_WRAPPER_ROOT=1 \ +- NON_WRAPPED_UID=$$(echo $$UID) \ + fakeroot $(PYTHON2) $(PYTEST) -v --tb=native $(INTGCHECK_PYTEST_ARGS) . + rm -f $(DESTDIR)$(logpath)/* +-- +2.9.3 + diff --git a/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch b/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch new file mode 100644 index 0000000..0dc6e0c --- /dev/null +++ b/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch @@ -0,0 +1,127 @@ +From ddfa743159541de498816764c06bf4b13fb923f7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 28 Mar 2017 18:33:46 +0200 +Subject: [PATCH 40/54] UTIL: Introduce subdomain_create_conf_path() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is a utility function that replaces the create_subdom_conf_path(). +Differently than the latter, it only takes one parameter and is going to +be used in a few different places (thus adding it to util.h). + +Reviewed-by: Fabiano Fidêncio +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukas Slebodnik +--- + src/providers/ad/ad_common.c | 7 ------- + src/providers/ad/ad_common.h | 4 ---- + src/providers/ad/ad_subdomains.c | 4 +--- + src/providers/ipa/ipa_subdomains_server.c | 4 +--- + src/util/domain_info_utils.c | 15 +++++++++++++++ + src/util/util.h | 3 +++ + 6 files changed, 20 insertions(+), 17 deletions(-) + +diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c +index ec952d3bb4587516ea26fd27c212d5620e2f3dda..f893b748a2ddcff1eab6e8d919d2aa950b825446 100644 +--- a/src/providers/ad/ad_common.c ++++ b/src/providers/ad/ad_common.c +@@ -33,13 +33,6 @@ errno_t ad_set_search_bases(struct sdap_options *id_opts); + static errno_t ad_set_sdap_options(struct ad_options *ad_opts, + struct sdap_options *id_opts); + +-char *create_subdom_conf_path(TALLOC_CTX *mem_ctx, +- const char *conf_path, +- const char *subdom_name) +-{ +- return talloc_asprintf(mem_ctx, "%s/%s", conf_path, subdom_name); +-} +- + static struct sdap_options * + ad_create_default_sdap_options(TALLOC_CTX *mem_ctx) + { +diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h +index e02b932cd2da737254de8417d5c82fcdcf14e8d7..2981550f6c390929501ec8942e861b16ea0a5cb0 100644 +--- a/src/providers/ad/ad_common.h ++++ b/src/providers/ad/ad_common.h +@@ -99,10 +99,6 @@ struct ad_options { + struct be_nsupdate_ctx *dyndns_ctx; + }; + +-char *create_subdom_conf_path(TALLOC_CTX *mem_ctx, +- const char *conf_path, +- const char *subdom_name); +- + errno_t + ad_get_common_options(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index 156ecab4272029d69c8b596eff041498a7524ce4..eecae9c9ca82ad67874c13a3c7b7c617d6232d5c 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -171,9 +171,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + return EINVAL; + } + +- subdom_conf_path = create_subdom_conf_path(id_ctx, +- be_ctx->conf_path, +- subdom->name); ++ subdom_conf_path = subdomain_create_conf_path(id_ctx, subdom); + if (subdom_conf_path == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "subdom_conf_path failed\n"); + return ENOMEM; +diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c +index ae3baf036e4278fb67d86b42742fb7e80b46724e..e8ee30392d84f84e30bcdaa3d2110ba130b1ad73 100644 +--- a/src/providers/ipa/ipa_subdomains_server.c ++++ b/src/providers/ipa/ipa_subdomains_server.c +@@ -176,9 +176,7 @@ static struct ad_options *ipa_ad_options_new(struct be_ctx *be_ctx, + forest_realm = subdom->forest_root->realm; + forest = subdom->forest_root->forest; + +- subdom_conf_path = create_subdom_conf_path(id_ctx, +- be_ctx->conf_path, +- subdom->name); ++ subdom_conf_path = subdomain_create_conf_path(id_ctx, subdom); + if (subdom_conf_path == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "subdom_conf_path failed\n"); + return NULL; +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index 6ef6bcfb8c078a360673b6bdd2364fc2918cb324..a7f118842aa8ba870143b2f2b425a3e3c0ea5a78 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -870,3 +870,18 @@ bool is_email_from_domain(const char *email, struct sss_domain_info *dom) + + return false; + } ++ ++char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *subdomain) ++{ ++ if (!IS_SUBDOMAIN(subdomain)) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "The domain \"%s\" is not a subdomain.\n", ++ subdomain->name); ++ return NULL; ++ } ++ ++ return talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL "/%s", ++ subdomain->parent->name, ++ subdomain->name); ++} +diff --git a/src/util/util.h b/src/util/util.h +index a2dc89b8ddb999437eda551ac17af28672d8759c..82760940269ad8883e725e3a5cf463486c9cfd36 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -551,6 +551,9 @@ find_domain_by_object_name(struct sss_domain_info *domain, + bool subdomain_enumerates(struct sss_domain_info *parent, + const char *sd_name); + ++char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *subdomain); ++ + errno_t sssd_domain_init(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *domain_name, +-- +2.9.3 + diff --git a/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch b/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch new file mode 100644 index 0000000..f9a0fe3 --- /dev/null +++ b/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch @@ -0,0 +1,531 @@ +From 887b53d8833ab91835cb3afbdadcbf9d091dafcd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Thu, 23 Mar 2017 13:14:56 +0100 +Subject: [PATCH 41/54] SUBDOMAINS: Allow use_fully_qualified_names for + subdomains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Allow option use_fully_qualified_names in subdomain section. +This option was recently added to subdomain_inherit. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3337 + +Reviewed-by: Fabiano Fidêncio +--- + src/db/sysdb.h | 3 +- + src/db/sysdb_private.h | 3 +- + src/db/sysdb_subdomains.c | 63 +++++++++++++++++++++++++-- + src/man/sssd.conf.5.xml | 3 +- + src/providers/ad/ad_subdomains.c | 3 +- + src/providers/ipa/ipa_subdomains.c | 10 +++-- + src/responder/common/responder_get_domains.c | 9 ++-- + src/tests/cmocka/test_fqnames.c | 2 +- + src/tests/cmocka/test_ipa_subdomains_server.c | 2 +- + src/tests/cmocka/test_nss_srv.c | 6 ++- + src/tests/cmocka/test_sysdb_subdomains.c | 25 ++++++----- + src/tests/sysdb-tests.c | 14 +++--- + src/tools/common/sss_tools.c | 2 +- + src/tools/sss_cache.c | 2 +- + 14 files changed, 107 insertions(+), 40 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 0cbb2c5c02355e9e9a4e73b075f92d16e4855045..6762b51bee02911fb97d5d393fad2495504ee5ad 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -494,7 +494,8 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb, + uint32_t trust_direction, + struct ldb_message_element *upn_suffixes); + +-errno_t sysdb_update_subdomains(struct sss_domain_info *domain); ++errno_t sysdb_update_subdomains(struct sss_domain_info *domain, ++ struct confdb_ctx *confdb); + + errno_t sysdb_master_domain_update(struct sss_domain_info *domain); + +diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h +index bfd24799950ab3b31d57df11b8f91c0b2572f13a..dfddd2dda9e593bd02d52dee7d06f520a11bbdf6 100644 +--- a/src/db/sysdb_private.h ++++ b/src/db/sysdb_private.h +@@ -191,7 +191,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + bool enumerate, + const char *forest, + const char **upn_suffixes, +- uint32_t trust_direction); ++ uint32_t trust_direction, ++ struct confdb_ctx *confdb); + + /* Helper functions to deal with the timestamp cache should not be used + * outside the sysdb itself. The timestamp cache should be completely +diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c +index 01f49763b712769f4f74df47961526e5b1514cd4..916dbba153d8c08837425f6fd29a20f5a6aa9fc9 100644 +--- a/src/db/sysdb_subdomains.c ++++ b/src/db/sysdb_subdomains.c +@@ -23,6 +23,10 @@ + #include "util/util.h" + #include "db/sysdb_private.h" + ++static errno_t ++check_subdom_config_file(struct confdb_ctx *confdb, ++ struct sss_domain_info *subdomain); ++ + struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + struct sss_domain_info *parent, + const char *name, +@@ -33,10 +37,12 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + bool enumerate, + const char *forest, + const char **upn_suffixes, +- uint32_t trust_direction) ++ uint32_t trust_direction, ++ struct confdb_ctx *confdb) + { + struct sss_domain_info *dom; + bool inherit_option; ++ errno_t ret; + + DEBUG(SSSDBG_TRACE_FUNC, + "Creating [%s] as subdomain of [%s]!\n", name, parent->name); +@@ -160,6 +166,17 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + } + dom->sysdb = parent->sysdb; + ++ if (confdb != NULL) { ++ /* If confdb was provided, also check for sssd.conf */ ++ ret = check_subdom_config_file(confdb, dom); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to read subdomain configuration [%d]: %s", ++ ret, sss_strerror(ret)); ++ goto fail; ++ } ++ } ++ + return dom; + + fail: +@@ -167,6 +184,45 @@ fail: + return NULL; + } + ++static errno_t ++check_subdom_config_file(struct confdb_ctx *confdb, ++ struct sss_domain_info *subdomain) ++{ ++ char *sd_conf_path; ++ TALLOC_CTX *tmp_ctx; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ sd_conf_path = subdomain_create_conf_path(tmp_ctx, subdomain); ++ if (sd_conf_path == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ /* use_fully_qualified_names */ ++ ret = confdb_get_bool(confdb, sd_conf_path, CONFDB_DOMAIN_FQ, ++ true, &subdomain->fqnames); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get %s option for the subdomain: %s\n", ++ CONFDB_DOMAIN_FQ, subdomain->name); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_CONF_SETTINGS, "%s/%s has value %s\n", ++ sd_conf_path, CONFDB_DOMAIN_FQ, ++ subdomain->fqnames ? "TRUE" : "FALSE"); ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ + static bool is_forest_root(struct sss_domain_info *d) + { + if (d->forest == NULL) { +@@ -232,7 +288,8 @@ static void link_forest_roots(struct sss_domain_info *domain) + } + } + +-errno_t sysdb_update_subdomains(struct sss_domain_info *domain) ++errno_t sysdb_update_subdomains(struct sss_domain_info *domain, ++ struct confdb_ctx *confdb) + { + int i; + errno_t ret; +@@ -451,7 +508,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain) + if (dom == NULL) { + dom = new_subdomain(domain, domain, name, realm, + flat, id, mpg, enumerate, forest, +- upn_suffixes, trust_direction); ++ upn_suffixes, trust_direction, confdb); + if (dom == NULL) { + ret = ENOMEM; + goto done; +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 284402bc00d37c6c33bf195d2bd719300f265851..1c27742cf0c1b6ffad23ab5b044bf4a168ed8f69 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -2780,7 +2780,8 @@ subdomain_inherit = ldap_purge_cache_timeout + ldap_service_search_base, + ad_server, + ad_backup_server, +- ad_site. ++ ad_site, ++ use_fully_qualified_names + + For more details about these options see their individual description + in the manual page. +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index eecae9c9ca82ad67874c13a3c7b7c617d6232d5c..bc659b2cb0a02723437d24d0021ec3592381e84c 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -656,7 +656,8 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx) + /* Just continue */ + } + +- ret = sysdb_update_subdomains(subdoms_ctx->be_ctx->domain); ++ ret = sysdb_update_subdomains(subdoms_ctx->be_ctx->domain, ++ subdoms_ctx->be_ctx->cdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n"); + return ret; +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index 7537550606ef09c0b87a80932c75aa4f93c0efab..a07b88fe2f499353293ba90345552413c9792f4b 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -126,7 +126,7 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx) + return ret; + } + +- ret = sysdb_update_subdomains(ctx->be_ctx->domain); ++ ret = sysdb_update_subdomains(ctx->be_ctx->domain, ctx->be_ctx->cdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n"); + return ret; +@@ -780,7 +780,8 @@ done: + static errno_t ipa_apply_view(struct sss_domain_info *domain, + struct ipa_id_ctx *ipa_id_ctx, + const char *view_name, +- bool read_at_init) ++ bool read_at_init, ++ struct confdb_ctx *confdb) + { + const char *current = ipa_id_ctx->view_name; + struct sysdb_ctx *sysdb = domain->sysdb; +@@ -876,7 +877,7 @@ static errno_t ipa_apply_view(struct sss_domain_info *domain, + goto done; + } + +- ret = sysdb_update_subdomains(domain); ++ ret = sysdb_update_subdomains(domain, confdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed " + "[%d]: %s\n", ret, sss_strerror(ret)); +@@ -1654,7 +1655,8 @@ static void ipa_subdomains_view_name_done(struct tevent_req *subreq) + + ret = ipa_apply_view(state->sd_ctx->be_ctx->domain, + state->sd_ctx->ipa_id_ctx, view_name, +- state->sd_ctx->view_read_at_init); ++ state->sd_ctx->view_read_at_init, ++ state->sd_ctx->be_ctx->cdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set view [%d]: %s\n", + ret, sss_strerror(ret)); +diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c +index 0f39d107dad6c458785b1b8d708e60d7c34e3901..0f9c01214631200f9687635f6302fa5c07e8a1fe 100644 +--- a/src/responder/common/responder_get_domains.c ++++ b/src/responder/common/responder_get_domains.c +@@ -126,7 +126,8 @@ get_next_domain_recv(TALLOC_CTX *mem_ctx, + } + + /* ====== Iterate over all domains, searching for their subdomains ======= */ +-static errno_t process_subdomains(struct sss_domain_info *dom); ++static errno_t process_subdomains(struct sss_domain_info *dom, ++ struct confdb_ctx *confdb); + static void set_time_of_last_request(struct resp_ctx *rctx); + static errno_t check_last_request(struct resp_ctx *rctx, const char *hint); + +@@ -234,7 +235,7 @@ sss_dp_get_domains_process(struct tevent_req *subreq) + goto fail; + } + +- ret = process_subdomains(state->dom); ++ ret = process_subdomains(state->dom, state->rctx->cdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "process_subdomains failed, " + "trying next domain.\n"); +@@ -270,7 +271,7 @@ fail: + } + + static errno_t +-process_subdomains(struct sss_domain_info *domain) ++process_subdomains(struct sss_domain_info *domain, struct confdb_ctx *confdb) + { + int ret; + +@@ -288,7 +289,7 @@ process_subdomains(struct sss_domain_info *domain) + /* Retrieve all subdomains of this domain from sysdb + * and create their struct sss_domain_info representations + */ +- ret = sysdb_update_subdomains(domain); ++ ret = sysdb_update_subdomains(domain, confdb); + if (ret != EOK) { + DEBUG(SSSDBG_FUNC_DATA, "sysdb_update_subdomains failed.\n"); + goto done; +diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c +index 19788248a39774bb4509363145ac4ce0815b7d28..0ed42a597b7787635c4971b4f1c3d9976949ccd2 100644 +--- a/src/tests/cmocka/test_fqnames.c ++++ b/src/tests/cmocka/test_fqnames.c +@@ -309,7 +309,7 @@ static int parse_name_test_setup(void **state) + * discovered + */ + test_ctx->subdom = new_subdomain(dom, dom, SUBDOMNAME, NULL, SUBFLATNAME, +- NULL, false, false, NULL, NULL, 0); ++ NULL, false, false, NULL, NULL, 0, NULL); + assert_non_null(test_ctx->subdom); + + check_leaks_push(test_ctx); +diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c +index 123cf11c01ef4687eecad31a9d73120a87c643e1..ca48425aca69e58358f5fd37e4b8238bfa9efe15 100644 +--- a/src/tests/cmocka/test_ipa_subdomains_server.c ++++ b/src/tests/cmocka/test_ipa_subdomains_server.c +@@ -263,7 +263,7 @@ static void add_test_subdomains(struct trust_test_ctx *test_ctx, + direction, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + } +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 50714715cc80338640f2a77ecbe17bd5e0d6e911..3d7e0382197401cb2264671712152fe0709296b6 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -3206,7 +3206,8 @@ static int nss_subdom_test_setup(void **state) + + subdomain = new_subdomain(nss_test_ctx, nss_test_ctx->tctx->dom, + testdom[0], testdom[1], testdom[2], testdom[3], +- false, false, NULL, NULL, 0); ++ false, false, NULL, NULL, 0, ++ nss_test_ctx->tctx->confdb); + assert_non_null(subdomain); + + ret = sysdb_subdomain_store(nss_test_ctx->tctx->sysdb, +@@ -3214,7 +3215,8 @@ static int nss_subdom_test_setup(void **state) + false, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(nss_test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(nss_test_ctx->tctx->dom, ++ nss_test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + nss_test_ctx->subdom = subdomain; +diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c +index 49f44998a06740d1df70ac354ee741824acd8f50..84bcdc17b39dbc8822097c2006f157a09ea5e466 100644 +--- a/src/tests/cmocka/test_sysdb_subdomains.c ++++ b/src/tests/cmocka/test_sysdb_subdomains.c +@@ -103,7 +103,7 @@ static void test_sysdb_subdomain_create(void **state) + false, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_non_null(test_ctx->tctx->dom->subdomains); +@@ -115,7 +115,7 @@ static void test_sysdb_subdomain_create(void **state) + false, false, NULL, 1, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_non_null(test_ctx->tctx->dom->subdomains->next); +@@ -133,7 +133,7 @@ static void test_sysdb_subdomain_create(void **state) + false, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_int_equal(test_ctx->tctx->dom->subdomains->trust_direction, 1); +@@ -145,7 +145,7 @@ static void test_sysdb_subdomain_create(void **state) + ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, dom1[0]); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_int_equal(sss_domain_get_state(test_ctx->tctx->dom->subdomains), +@@ -235,11 +235,11 @@ static void test_sysdb_link_forest_root_ipa(void **state) + 0, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Also update dom2 */ +- ret = sysdb_update_subdomains(test_ctx->tctx->dom->next); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + sub = find_domain_by_name(test_ctx->tctx->dom, dom1[0], true); +@@ -315,11 +315,11 @@ static void test_sysdb_link_forest_root_ad(void **state) + 0, NULL); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Also update dom2 */ +- ret = sysdb_update_subdomains(test_ctx->tctx->dom->next); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_non_null(test_ctx->tctx->dom->forest_root); +@@ -395,14 +395,15 @@ static void test_sysdb_link_forest_member_ad(void **state) + ret = sysdb_master_domain_update(test_ctx->tctx->dom); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Also update dom2 */ + ret = sysdb_master_domain_update(test_ctx->tctx->dom->next); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(test_ctx->tctx->dom->next); ++ ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, ++ test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Checks */ +@@ -472,7 +473,7 @@ static void test_sysdb_link_ad_multidom(void **state) + ret = sysdb_master_domain_update(main_dom1); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(main_dom1); ++ ret = sysdb_update_subdomains(main_dom1, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_add_info(main_dom2, +@@ -492,7 +493,7 @@ static void test_sysdb_link_ad_multidom(void **state) + ret = sysdb_master_domain_update(main_dom2); + assert_int_equal(ret, EOK); + +- ret = sysdb_update_subdomains(main_dom2); ++ ret = sysdb_update_subdomains(main_dom2, NULL); + assert_int_equal(ret, EOK); + + main_dom1 = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM1_NAME, true); +diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c +index 5bdd631fbfa1b4463fb169e5f07b65fb2c784096..1767dc3c734c6b2e5f74564debd603e2442f491b 100644 +--- a/src/tests/sysdb-tests.c ++++ b/src/tests/sysdb-tests.c +@@ -1395,7 +1395,7 @@ START_TEST (test_sysdb_get_user_attr_subdomain) + /* Create subdomain */ + subdomain = new_subdomain(test_ctx, test_ctx->domain, + "test.sub", "TEST.SUB", "test", "S-3", +- false, false, NULL, NULL, 0); ++ false, false, NULL, NULL, 0, NULL); + fail_if(subdomain == NULL, "Failed to create new subdomain."); + + ret = sss_names_init_from_args(test_ctx, +@@ -5821,14 +5821,14 @@ START_TEST(test_sysdb_subdomain_store_user) + + subdomain = new_subdomain(test_ctx, test_ctx->domain, + testdom[0], testdom[1], testdom[2], testdom[3], +- false, false, NULL, NULL, 0); ++ false, false, NULL, NULL, 0, NULL); + fail_unless(subdomain != NULL, "Failed to create new subdomin."); + ret = sysdb_subdomain_store(test_ctx->sysdb, + testdom[0], testdom[1], testdom[2], testdom[3], + false, false, NULL, 0, NULL); + fail_if(ret != EOK, "Could not set up the test (test subdom)"); + +- ret = sysdb_update_subdomains(test_ctx->domain); ++ ret = sysdb_update_subdomains(test_ctx->domain, NULL); + fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]", + ret, strerror(ret)); + +@@ -5900,14 +5900,14 @@ START_TEST(test_sysdb_subdomain_user_ops) + + subdomain = new_subdomain(test_ctx, test_ctx->domain, + testdom[0], testdom[1], testdom[2], testdom[3], +- false, false, NULL, NULL, 0); ++ false, false, NULL, NULL, 0, NULL); + fail_unless(subdomain != NULL, "Failed to create new subdomin."); + ret = sysdb_subdomain_store(test_ctx->sysdb, + testdom[0], testdom[1], testdom[2], testdom[3], + false, false, NULL, 0, NULL); + fail_if(ret != EOK, "Could not set up the test (test subdom)"); + +- ret = sysdb_update_subdomains(test_ctx->domain); ++ ret = sysdb_update_subdomains(test_ctx->domain, NULL); + fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]", + ret, strerror(ret)); + +@@ -5973,14 +5973,14 @@ START_TEST(test_sysdb_subdomain_group_ops) + + subdomain = new_subdomain(test_ctx, test_ctx->domain, + testdom[0], testdom[1], testdom[2], testdom[3], +- false, false, NULL, NULL, 0); ++ false, false, NULL, NULL, 0, NULL); + fail_unless(subdomain != NULL, "Failed to create new subdomin."); + ret = sysdb_subdomain_store(test_ctx->sysdb, + testdom[0], testdom[1], testdom[2], testdom[3], + false, false, NULL, 0, NULL); + fail_if(ret != EOK, "Could not set up the test (test subdom)"); + +- ret = sysdb_update_subdomains(test_ctx->domain); ++ ret = sysdb_update_subdomains(test_ctx->domain, NULL); + fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]", + ret, strerror(ret)); + +diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c +index 0f4f46894130daf722641f25a4cdfaae220252cc..97a3caab3bec88c5727eea2f08b200f1d3b23f0c 100644 +--- a/src/tools/common/sss_tools.c ++++ b/src/tools/common/sss_tools.c +@@ -154,7 +154,7 @@ static errno_t sss_tool_domains_init(TALLOC_CTX *mem_ctx, + } + + /* Update list of subdomains for this domain */ +- ret = sysdb_update_subdomains(dom); ++ ret = sysdb_update_subdomains(dom, confdb); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to update subdomains for domain %s.\n", +diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c +index 59e49a8aa92e3a08ec80e0597304f1a4af0a02be..8a40b38c07f7e76cde5b98e5916816581fea7973 100644 +--- a/src/tools/sss_cache.c ++++ b/src/tools/sss_cache.c +@@ -158,7 +158,7 @@ int main(int argc, const char *argv[]) + dinfo = get_next_domain(dinfo, SSS_GND_DESCEND)) { + if (!IS_SUBDOMAIN(dinfo)) { + /* Update list of subdomains for this domain */ +- ret = sysdb_update_subdomains(dinfo); ++ ret = sysdb_update_subdomains(dinfo, tctx->confdb); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to update subdomains for domain %s.\n", dinfo->name); +-- +2.9.3 + diff --git a/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch b/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch new file mode 100644 index 0000000..583b606 --- /dev/null +++ b/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch @@ -0,0 +1,172 @@ +From 73b3b167e86a87263563aa8aac2b45cdf3668765 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 1 Mar 2017 08:34:57 +0000 +Subject: [PATCH 42/54] CACHE_REQ: Descend into subdomains on lookups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Let's make all plugins, but the "host_by_name", to descend into the +subdomains on lookups. + +This patch basically prepares the field for the coming up patches that +will allow group/user resolution in all domains (or a subset of the +domains) to be possible by only using the short names without the domain +component. + +The "host_by_name" plugin was not changed as it's a specific IPA plugin +and won't find anything on its subdomains. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +Reviewed-by: Sumit Bose +--- + src/responder/common/cache_req/plugins/cache_req_enum_svc.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_group_by_filter.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_group_by_name.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_object_by_name.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_svc_by_name.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_svc_by_port.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_user_by_filter.c | 2 +- + src/responder/common/cache_req/plugins/cache_req_user_by_name.c | 2 +- + 10 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c +index 2c4917cde750c9063d898c16d3a58ca8c179bc70..28dea33c601f500b9c7af0de3eb9e1c342f03522 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c +@@ -68,7 +68,7 @@ const struct cache_req_plugin cache_req_enum_svc = { + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = NULL, +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c +index 88e1137a3976308aaf404b684c6d88cc43708bca..6ce6ae0d63967ac50b813a47ac938251619948da 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c +@@ -123,7 +123,7 @@ const struct cache_req_plugin cache_req_group_by_filter = { + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_group_by_filter_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c +index be1eb9bd8552156d777e934b0be397b0e66df7cc..af6f23ccfd68f952027462ba3e74ed7219d04651 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c +@@ -186,7 +186,7 @@ const struct cache_req_plugin cache_req_group_by_name = { + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_group_by_name_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c +index 10fb67cbf6e78cfae33bc7208585cb80ea6a9bc4..307b65a24282838b99c472b50a71f06865aed3f0 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c +@@ -201,7 +201,7 @@ const struct cache_req_plugin cache_req_initgroups_by_name = { + .allow_missing_fqn = false, + .allow_switch_to_upn = true, + .upn_equivalent = CACHE_REQ_INITGROUPS_BY_UPN, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_initgroups_by_name_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +index bc6fc9a8f476f97cc4bc5004bc19ba35258a2b6d..e49d6d84a41ce8dabf18c87373826f8e7b684bda 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +@@ -120,7 +120,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = { + .allow_missing_fqn = true, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_netgroup_by_name_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c +index 2b2caeea172b23b1b1b226def5d926e26c5c0090..74d2b3dea287e890b38e4d5bb176ad2dc6337b7e 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c +@@ -196,7 +196,7 @@ const struct cache_req_plugin cache_req_object_by_name = { + .allow_missing_fqn = false, + .allow_switch_to_upn = true, + .upn_equivalent = CACHE_REQ_USER_BY_UPN, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = cache_req_object_by_name_well_known, + .prepare_domain_data_fn = cache_req_object_by_name_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c +index cbb186df04c7ca7c02dceb98bd5700c984285a4d..ef13f097a8ae78ec9db5b7f6e14924b511578b34 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c +@@ -144,7 +144,7 @@ const struct cache_req_plugin cache_req_svc_by_name = { + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_svc_by_name_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c +index 1da23d4505a1dad3b2425a996134f8298c03518a..afa2eeeda12794de26e798aee4b88900bc87ed93 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c ++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c +@@ -117,7 +117,7 @@ const struct cache_req_plugin cache_req_svc_by_port = { + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_svc_by_port_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c +index ee7e69399e318b9835f1623bddc635bf09aa7a1c..eb71b42dad3a805298df0c8425409d571befb31b 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c +@@ -123,7 +123,7 @@ const struct cache_req_plugin cache_req_user_by_filter = { + .allow_missing_fqn = false, + .allow_switch_to_upn = false, + .upn_equivalent = CACHE_REQ_SENTINEL, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_user_by_filter_prepare_domain_data, +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c +index 4289f5fd4c79f0e512f0249abe4422589fa800a0..0670febdce2d51e0373045570dd07f56255db7bc 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c +@@ -191,7 +191,7 @@ const struct cache_req_plugin cache_req_user_by_name = { + .allow_missing_fqn = false, + .allow_switch_to_upn = true, + .upn_equivalent = CACHE_REQ_USER_BY_UPN, +- .get_next_domain_flags = 0, ++ .get_next_domain_flags = SSS_GND_DESCEND, + + .is_well_known_fn = NULL, + .prepare_domain_data_fn = cache_req_user_by_name_prepare_domain_data, +-- +2.9.3 + diff --git a/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch b/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch new file mode 100644 index 0000000..e142203 --- /dev/null +++ b/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch @@ -0,0 +1,35 @@ +From 5e3ea50bba1fee75cc0788bcd77ae4991828608e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 1 Mar 2017 13:21:19 +0000 +Subject: [PATCH 43/54] NSS/TESTS: Fix subdomains attribution +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Reviewed-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/tests/cmocka/test_nss_srv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 3d7e0382197401cb2264671712152fe0709296b6..cbe0dccdc1d883eae1a9621f12997ef43d05178e 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -3219,7 +3219,7 @@ static int nss_subdom_test_setup(void **state) + nss_test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + +- nss_test_ctx->subdom = subdomain; ++ nss_test_ctx->subdom = nss_test_ctx->tctx->dom->subdomains; + return 0; + } + +-- +2.9.3 + diff --git a/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch b/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch new file mode 100644 index 0000000..9eb42a4 --- /dev/null +++ b/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch @@ -0,0 +1,274 @@ +From f318eff5277d783972ef0d585ff05c473db44714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 1 Mar 2017 20:46:10 +0000 +Subject: [PATCH 44/54] NSS/TESTS: Improve setup/teardown for subdomains tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch basically makes the getgrnam_members_subdom(), +getgrnam_mix_dom(), getgrnam_mix_dom_fqdn() and getgrnam_mix_subdom() +more independent of each other. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/tests/cmocka/test_nss_srv.c | 182 +++++++++++++++++++++++++++++++++------- + 1 file changed, 150 insertions(+), 32 deletions(-) + +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index cbe0dccdc1d883eae1a9621f12997ef43d05178e..b468204fb1729618830513322f0d901c4c801e94 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -418,6 +418,26 @@ static errno_t store_user(struct nss_test_ctx *ctx, + return ret; + } + ++static errno_t delete_user(struct nss_test_ctx *ctx, ++ struct sss_domain_info *dom, ++ struct passwd *user) ++{ ++ errno_t ret; ++ char *fqname; ++ ++ fqname = sss_create_internal_fqname(ctx, ++ user->pw_name, ++ dom->name); ++ if (fqname == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sysdb_delete_user(dom, fqname, user->pw_uid); ++ ++ talloc_free(fqname); ++ return ret; ++} ++ + static errno_t set_user_attr(struct nss_test_ctx *ctx, + struct sss_domain_info *dom, + struct passwd *user, +@@ -491,6 +511,27 @@ static errno_t store_group(struct nss_test_ctx *ctx, + return ret; + } + ++static errno_t delete_group(struct nss_test_ctx *ctx, ++ struct sss_domain_info *dom, ++ struct group *group) ++{ ++ errno_t ret; ++ char *fqname; ++ ++ fqname = sss_create_internal_fqname(ctx, ++ group->gr_name, ++ dom->name); ++ ++ if (fqname == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sysdb_delete_group(dom, fqname, group->gr_gid); ++ ++ talloc_free(fqname); ++ return ret; ++} ++ + static void assert_groups_equal(struct group *expected, + struct group *gr, const int nmem) + { +@@ -540,6 +581,42 @@ static errno_t store_group_member(struct nss_test_ctx *ctx, + return ret; + } + ++static errno_t remove_group_member(struct nss_test_ctx *ctx, ++ const char *shortname_group, ++ struct sss_domain_info *group_dom, ++ const char *shortname_member, ++ struct sss_domain_info *member_dom, ++ enum sysdb_member_type type) ++{ ++ errno_t ret; ++ char *group_fqname = NULL; ++ char *member_fqname = NULL; ++ ++ group_fqname = sss_create_internal_fqname(ctx, ++ shortname_group, ++ group_dom->name); ++ if (group_fqname == NULL) { ++ return ENOMEM; ++ } ++ ++ member_fqname = sss_create_internal_fqname(ctx, ++ shortname_member, ++ member_dom->name); ++ if (member_fqname == NULL) { ++ talloc_free(group_fqname); ++ return ENOMEM; ++ } ++ ++ ret = sysdb_remove_group_member(group_dom, ++ group_fqname, ++ member_fqname, ++ type, ++ false); ++ ++ talloc_free(group_fqname); ++ talloc_free(member_fqname); ++ return ret; ++} + + /* ====================== The tests =============================== */ + struct passwd getpwnam_usr = { +@@ -1599,34 +1676,6 @@ void test_nss_getgrnam_members_subdom(void **state) + { + errno_t ret; + +- ret = store_group(nss_test_ctx, nss_test_ctx->subdom, +- &testsubdomgroup, 0); +- assert_int_equal(ret, EOK); +- +- ret = store_user(nss_test_ctx, nss_test_ctx->subdom, +- &submember1, NULL, 0); +- assert_int_equal(ret, EOK); +- +- ret = store_user(nss_test_ctx, nss_test_ctx->subdom, +- &submember2, NULL, 0); +- assert_int_equal(ret, EOK); +- +- ret = store_group_member(nss_test_ctx, +- testsubdomgroup.gr_name, +- nss_test_ctx->subdom, +- submember1.pw_name, +- nss_test_ctx->subdom, +- SYSDB_MEMBER_USER); +- assert_int_equal(ret, EOK); +- +- ret = store_group_member(nss_test_ctx, +- testsubdomgroup.gr_name, +- nss_test_ctx->subdom, +- submember2.pw_name, +- nss_test_ctx->subdom, +- SYSDB_MEMBER_USER); +- assert_int_equal(ret, EOK); +- + mock_input_user_or_group("testsubdomgroup@"TEST_SUBDOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); +@@ -1757,6 +1806,14 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state) + { + errno_t ret; + ++ ret = store_group_member(nss_test_ctx, ++ testgroup_members.gr_name, ++ nss_test_ctx->tctx->dom, ++ submember1.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ + nss_test_ctx->tctx->dom->fqnames = true; + + mock_input_user_or_group("testgroup_members@"TEST_DOM_NAME); +@@ -3220,6 +3277,35 @@ static int nss_subdom_test_setup(void **state) + assert_int_equal(ret, EOK); + + nss_test_ctx->subdom = nss_test_ctx->tctx->dom->subdomains; ++ ++ ret = store_group(nss_test_ctx, nss_test_ctx->subdom, ++ &testsubdomgroup, 0); ++ assert_int_equal(ret, EOK); ++ ++ ret = store_user(nss_test_ctx, nss_test_ctx->subdom, ++ &submember1, NULL, 0); ++ assert_int_equal(ret, EOK); ++ ++ ret = store_user(nss_test_ctx, nss_test_ctx->subdom, ++ &submember2, NULL, 0); ++ assert_int_equal(ret, EOK); ++ ++ ret = store_group_member(nss_test_ctx, ++ testsubdomgroup.gr_name, ++ nss_test_ctx->subdom, ++ submember1.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ ++ ret = store_group_member(nss_test_ctx, ++ testsubdomgroup.gr_name, ++ nss_test_ctx->subdom, ++ submember2.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ + return 0; + } + +@@ -3241,6 +3327,38 @@ static int nss_test_teardown(void **state) + return 0; + } + ++static int nss_subdom_test_teardown(void **state) ++{ ++ errno_t ret; ++ ++ ret = remove_group_member(nss_test_ctx, ++ testsubdomgroup.gr_name, ++ nss_test_ctx->subdom, ++ submember2.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ ++ ret = remove_group_member(nss_test_ctx, ++ testsubdomgroup.gr_name, ++ nss_test_ctx->subdom, ++ submember1.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ ++ ret = delete_user(nss_test_ctx, nss_test_ctx->subdom, &submember2); ++ assert_int_equal(ret, EOK); ++ ++ ret = delete_user(nss_test_ctx, nss_test_ctx->subdom, &submember1); ++ assert_int_equal(ret, EOK); ++ ++ ret = delete_group(nss_test_ctx, nss_test_ctx->subdom, &testsubdomgroup); ++ assert_int_equal(ret, EOK); ++ ++ return nss_test_teardown(state); ++} ++ + struct passwd testbysid = { + .pw_name = discard_const("testsiduser"), + .pw_uid = 12345, +@@ -3904,16 +4022,16 @@ int main(int argc, const char *argv[]) + nss_fqdn_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom, + nss_subdom_test_setup, +- nss_test_teardown), ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom, + nss_subdom_test_setup, +- nss_test_teardown), ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn, + nss_subdom_test_setup, +- nss_test_teardown), ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom, + nss_subdom_test_setup, +- nss_test_teardown), ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_space, + nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_space_sub, +-- +2.9.3 + diff --git a/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch b/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch new file mode 100644 index 0000000..08acdce --- /dev/null +++ b/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch @@ -0,0 +1,355 @@ +From 510971d2abc3b76799048cd608511d693f5c3edc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 1 Mar 2017 08:33:06 +0000 +Subject: [PATCH 45/54] NSS/TESTS: Include searches for non-fqnames members of + a subdomain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Let's extend the NSS tests in order to also test looking up users, from +a subdomain, by their short names (non fully qualified names). + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/tests/cmocka/test_nss_srv.c | 246 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 211 insertions(+), 35 deletions(-) + +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index b468204fb1729618830513322f0d901c4c801e94..ede72b341b60842ad470df2794aa90ea9797e999 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -1648,16 +1648,29 @@ static int test_nss_getgrnam_members_check_subdom(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, submember1.pw_name); +- assert_non_null(exp_members[0]); +- exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, submember2.pw_name); +- assert_non_null(exp_members[1]); ++ if (nss_test_ctx->subdom->fqnames) { ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); + +- expected.gr_name = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, testsubdomgroup.gr_name); +- assert_non_null(expected.gr_name); ++ exp_members[1] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember2.pw_name); ++ assert_non_null(exp_members[1]); ++ ++ expected.gr_name = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ testsubdomgroup.gr_name); ++ assert_non_null(expected.gr_name); ++ } else { ++ exp_members[0] = submember1.pw_name; ++ exp_members[1] = submember2.pw_name; ++ expected.gr_name = testsubdomgroup.gr_name; ++ } + + assert_int_equal(status, EOK); + +@@ -1692,6 +1705,29 @@ void test_nss_getgrnam_members_subdom(void **state) + assert_int_equal(ret, EOK); + } + ++void test_nss_getgrnam_members_subdom_nonfqnames(void **state) ++{ ++ errno_t ret; ++ ++ nss_test_ctx->subdom->fqnames = false; ++ ++ mock_input_user_or_group("testsubdomgroup"); ++ mock_account_recv_simple(); ++ will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); ++ will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ /* Query for that group, call a callback when command finishes */ ++ set_cmd_cb(test_nss_getgrnam_members_check_subdom); ++ ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM, ++ nss_test_ctx->nss_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(nss_test_ctx->tctx); ++ ++ assert_int_equal(ret, EOK); ++} ++ + static int test_nss_getgrnam_check_mix_dom(uint32_t status, + uint8_t *body, size_t blen) + { +@@ -1710,9 +1746,15 @@ static int test_nss_getgrnam_check_mix_dom(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, submember1.pw_name); +- assert_non_null(exp_members[0]); ++ if (nss_test_ctx->subdom->fqnames) { ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); ++ } else { ++ exp_members[0] = submember1.pw_name; ++ } + exp_members[1] = testmember1.pw_name; + exp_members[2] = testmember2.pw_name; + +@@ -1756,6 +1798,35 @@ void test_nss_getgrnam_mix_dom(void **state) + assert_int_equal(ret, EOK); + } + ++void test_nss_getgrnam_mix_dom_nonfqnames(void **state) ++{ ++ errno_t ret; ++ ++ nss_test_ctx->subdom->fqnames = false; ++ ++ ret = store_group_member(nss_test_ctx, ++ testgroup_members.gr_name, ++ nss_test_ctx->tctx->dom, ++ submember1.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_user_or_group("testgroup_members"); ++ will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); ++ will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ /* Query for that group, call a callback when command finishes */ ++ set_cmd_cb(test_nss_getgrnam_check_mix_dom); ++ ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM, ++ nss_test_ctx->nss_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(nss_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ + static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status, + uint8_t *body, size_t blen) + { +@@ -1773,21 +1844,33 @@ static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, submember1.pw_name); +- assert_non_null(exp_members[0]); +- exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names, +- nss_test_ctx->tctx->dom, testmember1.pw_name); +- assert_non_null(exp_members[1]); +- exp_members[2] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names, +- nss_test_ctx->tctx->dom, testmember2.pw_name); +- assert_non_null(exp_members[2]); ++ if (nss_test_ctx->subdom->fqnames) { ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); ++ } else { ++ exp_members[0] = submember1.pw_name; ++ } ++ if (nss_test_ctx->tctx->dom->fqnames) { ++ exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names, ++ nss_test_ctx->tctx->dom, testmember1.pw_name); ++ assert_non_null(exp_members[1]); ++ exp_members[2] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names, ++ nss_test_ctx->tctx->dom, testmember2.pw_name); ++ assert_non_null(exp_members[2]); + +- expected.gr_name = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->tctx->dom->names, +- nss_test_ctx->tctx->dom, +- testgroup_members.gr_name); +- assert_non_null(expected.gr_name); ++ expected.gr_name = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->tctx->dom->names, ++ nss_test_ctx->tctx->dom, ++ testgroup_members.gr_name); ++ assert_non_null(expected.gr_name); ++ } else { ++ exp_members[1] = testmember1.pw_name; ++ exp_members[2] = testmember2.pw_name; ++ expected.gr_name = testgroup_members.gr_name; ++ } + + assert_int_equal(status, EOK); + +@@ -1834,6 +1917,40 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state) + assert_int_equal(ret, EOK); + } + ++void test_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state) ++{ ++ errno_t ret; ++ ++ ret = store_group_member(nss_test_ctx, ++ testgroup_members.gr_name, ++ nss_test_ctx->tctx->dom, ++ submember1.pw_name, ++ nss_test_ctx->subdom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ ++ nss_test_ctx->tctx->dom->fqnames = false; ++ nss_test_ctx->subdom->fqnames = false; ++ ++ ++ mock_input_user_or_group("testgroup_members"); ++ will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); ++ will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ /* Query for that group, call a callback when command finishes */ ++ set_cmd_cb(test_nss_getgrnam_check_mix_dom_fqdn); ++ ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM, ++ nss_test_ctx->nss_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(nss_test_ctx->tctx); ++ ++ /* Restore FQDN settings */ ++ nss_test_ctx->tctx->dom->fqnames = false; ++ assert_int_equal(ret, EOK); ++} ++ + static int test_nss_getgrnam_check_mix_subdom(uint32_t status, + uint8_t *body, size_t blen) + { +@@ -1851,20 +1968,37 @@ static int test_nss_getgrnam_check_mix_subdom(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, submember1.pw_name); +- assert_non_null(exp_members[0]); +- exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, submember2.pw_name); +- assert_non_null(exp_members[1]); ++ if (nss_test_ctx->subdom->fqnames) { ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); ++ ++ exp_members[1] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember2.pw_name); ++ assert_non_null(exp_members[1]); ++ } else { ++ exp_members[0] = submember1.pw_name; ++ exp_members[1] = submember2.pw_name; ++ } ++ + /* Important: this member is from a non-qualified domain, so his name will + * not be qualified either + */ + exp_members[2] = testmember1.pw_name; + +- expected.gr_name = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, testsubdomgroup.gr_name); +- assert_non_null(expected.gr_name); ++ if (nss_test_ctx->subdom->fqnames) { ++ expected.gr_name = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ testsubdomgroup.gr_name); ++ assert_non_null(expected.gr_name); ++ } else { ++ expected.gr_name = testsubdomgroup.gr_name; ++ } + + assert_int_equal(status, EOK); + +@@ -1906,6 +2040,36 @@ void test_nss_getgrnam_mix_subdom(void **state) + assert_int_equal(ret, EOK); + } + ++void test_nss_getgrnam_mix_subdom_nonfqnames(void **state) ++{ ++ errno_t ret; ++ ++ nss_test_ctx->subdom->fqnames = false; ++ ++ ret = store_group_member(nss_test_ctx, ++ testsubdomgroup.gr_name, ++ nss_test_ctx->subdom, ++ testmember1.pw_name, ++ nss_test_ctx->tctx->dom, ++ SYSDB_MEMBER_USER); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_user_or_group("testsubdomgroup"); ++ mock_account_recv_simple(); ++ will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); ++ will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ /* Query for that group, call a callback when command finishes */ ++ set_cmd_cb(test_nss_getgrnam_check_mix_subdom); ++ ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM, ++ nss_test_ctx->nss_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(nss_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ + struct group space_group = { + .gr_gid = 2123, + .gr_name = discard_const("space group"), +@@ -4023,15 +4187,27 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom, + nss_subdom_test_setup, + nss_subdom_test_teardown), ++ cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom_nonfqnames, ++ nss_subdom_test_setup, ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom, + nss_subdom_test_setup, + nss_subdom_test_teardown), ++ cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_nonfqnames, ++ nss_subdom_test_setup, ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn, + nss_subdom_test_setup, + nss_subdom_test_teardown), ++ cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn_nonfqnames, ++ nss_subdom_test_setup, ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom, + nss_subdom_test_setup, + nss_subdom_test_teardown), ++ cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom_nonfqnames, ++ nss_subdom_test_setup, ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_space, + nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_space_sub, +-- +2.9.3 + diff --git a/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch b/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch new file mode 100644 index 0000000..b2a6305 --- /dev/null +++ b/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch @@ -0,0 +1,289 @@ +From b601cae66c441163a00f73c64d00a29e0840d44e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Fri, 24 Mar 2017 15:29:23 +0100 +Subject: [PATCH 46/54] SYSDB: Add methods to deal with the domain's resolution + order +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In the following-up patches those newly introduced methods will be used +to deal with the domainResolutionOrder attribute. + +The sysdb_update_domain_resolution_order() method is purposely not +checking whether a value has changed or not before writing to sysdb and +while may not be optimal, the readability of the code has increased a +lot by keeping it as simple as possible. + +Tests for these new methods are part of the next commit. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + Makefile.am | 2 + + src/db/sysdb.h | 2 + + src/db/sysdb_domain_resolution_order.c | 169 +++++++++++++++++++++++++++++++++ + src/db/sysdb_domain_resolution_order.h | 37 ++++++++ + 4 files changed, 210 insertions(+) + create mode 100644 src/db/sysdb_domain_resolution_order.c + create mode 100644 src/db/sysdb_domain_resolution_order.h + +diff --git a/Makefile.am b/Makefile.am +index 359feddef298b0013c726409b7ba8b86504abf09..8052150be32d89813764e9bc436dfcb211a738d6 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -736,6 +736,7 @@ dist_noinst_HEADERS = \ + src/db/sysdb_private.h \ + src/db/sysdb_services.h \ + src/db/sysdb_ssh.h \ ++ src/db/sysdb_domain_resolution_order.h \ + src/confdb/confdb.h \ + src/confdb/confdb_private.h \ + src/confdb/confdb_setup.h \ +@@ -995,6 +996,7 @@ libsss_util_la_SOURCES = \ + src/db/sysdb_idmap.c \ + src/db/sysdb_gpo.c \ + src/db/sysdb_certmap.c \ ++ src/db/sysdb_domain_resolution_order.c \ + src/monitor/monitor_sbus.c \ + src/providers/dp_auth_util.c \ + src/providers/dp_pam_data_util.c \ +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 6762b51bee02911fb97d5d393fad2495504ee5ad..42d2857ed7765c17e7d84b0da93ed07758fbe012 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -184,6 +184,8 @@ + #define SYSDB_OVERRIDE_GROUP_CLASS "groupOverride" + #define SYSDB_OVERRIDE_DN "overrideDN" + #define SYSDB_OVERRIDE_OBJECT_DN "overrideObjectDN" ++#define SYSDB_USE_DOMAIN_RESOLUTION_ORDER "useDomainResolutionOrder" ++#define SYSDB_DOMAIN_RESOLUTION_ORDER "domainResolutionOrder" + + #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)" + +diff --git a/src/db/sysdb_domain_resolution_order.c b/src/db/sysdb_domain_resolution_order.c +new file mode 100644 +index 0000000000000000000000000000000000000000..63774461a1e9f3dc863220d418e29e06d6e6e6df +--- /dev/null ++++ b/src/db/sysdb_domain_resolution_order.c +@@ -0,0 +1,169 @@ ++/* ++ Authors: ++ Fabiano Fidêncio ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++ ++#include "db/sysdb.h" ++#include "db/sysdb_private.h" ++ ++static errno_t ++sysdb_get_domain_resolution_order_string_attr(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ struct ldb_dn *dn, ++ const char *const *attrs, ++ const char **_attr) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_result *res; ++ const char *attr; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, ++ NULL); ++ if (ret != LDB_SUCCESS) { ++ ret = EIO; ++ goto done; ++ } ++ ++ if (res->count > 1) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Base search returned [%d] results, expected 1.\n", res->count); ++ ret = EINVAL; ++ goto done; ++ } else if (res->count == 0) { ++ ret = ENOENT; ++ goto done; ++ } else { ++ /* res->count == 1 */ ++ attr = ldb_msg_find_attr_as_string(res->msgs[0], attrs[0], NULL); ++ if (attr == NULL) { ++ ret = ENOENT; ++ goto done; ++ } ++ } ++ ++ *_attr = talloc_steal(mem_ctx, attr); ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++errno_t ++sysdb_get_domain_resolution_order(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ struct ldb_dn *dn, ++ const char **_domain_resolution_order) ++{ ++ TALLOC_CTX *tmp_ctx; ++ const char *domain_resolution_order = NULL; ++ const char *attrs[] = { SYSDB_DOMAIN_RESOLUTION_ORDER, NULL }; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sysdb_get_domain_resolution_order_string_attr( ++ tmp_ctx, sysdb, dn, attrs, &domain_resolution_order); ++ if (ret != EOK && ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_get_domain_resolution_order_string_attr() failed " ++ "[%d]: [%s]", ++ ret, sss_strerror(ret)); ++ goto done; ++ } else if (ret == ENOENT) { ++ *_domain_resolution_order = NULL; ++ goto done; ++ } else { ++ /* ret == EOK */ ++ *_domain_resolution_order = talloc_steal(mem_ctx, ++ domain_resolution_order); ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++errno_t ++sysdb_update_domain_resolution_order(struct sysdb_ctx *sysdb, ++ struct ldb_dn *dn, ++ const char *domain_resolution_order) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_message *msg; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ msg = ldb_msg_new(tmp_ctx); ++ if (msg == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ msg->dn = dn; ++ ++ ret = ldb_msg_add_empty(msg, SYSDB_DOMAIN_RESOLUTION_ORDER, ++ LDB_FLAG_MOD_REPLACE, NULL); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ if (domain_resolution_order != NULL) { ++ ret = ldb_msg_add_string(msg, SYSDB_DOMAIN_RESOLUTION_ORDER, ++ domain_resolution_order); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ } ++ ++ ret = ldb_modify(sysdb->ldb, msg); ++ if (ret != LDB_SUCCESS) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "ldb_modify()_failed: [%s][%d][%s]\n", ++ ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb)); ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} +diff --git a/src/db/sysdb_domain_resolution_order.h b/src/db/sysdb_domain_resolution_order.h +new file mode 100644 +index 0000000000000000000000000000000000000000..45d2ea63f6bc14cd3184994530846ee6f762d4d0 +--- /dev/null ++++ b/src/db/sysdb_domain_resolution_order.h +@@ -0,0 +1,37 @@ ++/* ++ Authors: ++ Fabiano Fidêncio ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef _SYSDB_DOMAIN_RESOLUTION_ORDER_H_ ++#define _SYSDB_DOMAIN_RESOLUTION_ORDER_H_ ++ ++#include "db/sysdb.h" ++ ++errno_t ++sysdb_get_domain_resolution_order(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ struct ldb_dn *dn, ++ const char **_domain_resolution_order); ++ ++errno_t ++sysdb_update_domain_resolution_order(struct sysdb_ctx *sysdb, ++ struct ldb_dn *dn, ++ const char *domain_resolution_order); ++ ++#endif /* _SYSDB_DOMAIN_RESOLUTION_ORDER_H_ */ +-- +2.9.3 + diff --git a/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch b/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch new file mode 100644 index 0000000..459a50f --- /dev/null +++ b/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch @@ -0,0 +1,259 @@ +From 22a10ea2b6b8a56fc040d852867040dce067548a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Fri, 24 Mar 2017 23:15:04 +0100 +Subject: [PATCH 47/54] SYSDB/TESTS: Add tests for the domain's resolution + order methods +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce a new and small set of tests for these new helper methods that +are going to be used in different parts of the code in the follow-up +patches. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + Makefile.am | 16 ++ + .../cmocka/test_sysdb_domain_resolution_order.c | 190 +++++++++++++++++++++ + 2 files changed, 206 insertions(+) + create mode 100644 src/tests/cmocka/test_sysdb_domain_resolution_order.c + +diff --git a/Makefile.am b/Makefile.am +index 8052150be32d89813764e9bc436dfcb211a738d6..450785bf4c482cce1e1440f1336879150537888e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -263,6 +263,7 @@ if HAVE_CMOCKA + test_sysdb_certmap \ + test_sysdb_sudo \ + test_sysdb_utils \ ++ test_sysdb_domain_resolution_order \ + test_wbc_calls \ + test_be_ptask \ + test_copy_ccache \ +@@ -2875,6 +2876,21 @@ test_sysdb_utils_LDADD = \ + libsss_test_common.la \ + $(NULL) + ++test_sysdb_domain_resolution_order_SOURCES = \ ++ src/tests/cmocka/test_sysdb_domain_resolution_order.c \ ++ $(NULL) ++test_sysdb_domain_resolution_order_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++test_sysdb_domain_resolution_order_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(LDB_LIBS) \ ++ $(POPT_LIBS) \ ++ $(TALLOC_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ $(NULL) ++ + test_wbc_calls_SOURCES = \ + src/tests/cmocka/test_wbc_calls.c \ + src/sss_client/idmap/sss_nss_idmap.c \ +diff --git a/src/tests/cmocka/test_sysdb_domain_resolution_order.c b/src/tests/cmocka/test_sysdb_domain_resolution_order.c +new file mode 100644 +index 0000000000000000000000000000000000000000..59a85ce431be9ac27c1e8e6b5e4e5f8300af549e +--- /dev/null ++++ b/src/tests/cmocka/test_sysdb_domain_resolution_order.c +@@ -0,0 +1,190 @@ ++/* ++ SSSD ++ ++ sysdb_domain_resolution_order - Tests for domain resolution order calls ++ ++ Authors: ++ Fabiano Fidêncio ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests/cmocka/common_mock.h" ++#include "tests/common.h" ++#include "db/sysdb_domain_resolution_order.h" ++#include "db/sysdb_private.h" /* for sysdb->ldb member */ ++ ++#define TESTS_PATH "tp_" BASE_FILE_STEM ++#define TEST_CONF_DB "test_sysdb_domain_resolution_order.ldb" ++ ++#define TEST_DOM_NAME "test_sysdb_domain_resolution_order" ++ ++#define TEST_ID_PROVIDER "ldap" ++ ++struct domain_resolution_order_test_ctx { ++ struct sss_test_ctx *tctx; ++}; ++ ++static int test_sysdb_domain_resolution_order_setup(void **state) ++{ ++ struct domain_resolution_order_test_ctx *test_ctx; ++ ++ assert_true(leak_check_setup()); ++ ++ test_ctx = talloc_zero(global_talloc_context, ++ struct domain_resolution_order_test_ctx); ++ assert_non_null(test_ctx); ++ ++ test_dom_suite_setup(TESTS_PATH); ++ ++ test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, ++ TEST_CONF_DB, TEST_DOM_NAME, ++ TEST_ID_PROVIDER, NULL); ++ assert_non_null(test_ctx->tctx); ++ ++ *state = test_ctx; ++ return 0; ++} ++ ++static int test_sysdb_domain_resolution_order_teardown(void **state) ++{ ++ struct domain_resolution_order_test_ctx *test_ctx = ++ talloc_get_type(*state, struct domain_resolution_order_test_ctx); ++ ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); ++ talloc_free(test_ctx); ++ assert_true(leak_check_teardown()); ++ return 0; ++} ++ ++static void test_sysdb_domain_resolution_order_ops(void **state) ++{ ++ errno_t ret; ++ struct domain_resolution_order_test_ctx *test_ctx = ++ talloc_get_type(*state, struct domain_resolution_order_test_ctx); ++ const char *domains_in = NULL; ++ const char *domains_out = NULL; ++ struct ldb_dn *dn; ++ ++ dn = ldb_dn_new_fmt(test_ctx, test_ctx->tctx->dom->sysdb->ldb, ++ SYSDB_DOM_BASE, test_ctx->tctx->dom->name); ++ ++ /* Adding domainResolutionOrder for the first time */ ++ domains_in = "foo:bar:foobar"; ++ ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, ++ dn, domains_in); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_domain_resolution_order(test_ctx, ++ test_ctx->tctx->dom->sysdb, dn, ++ &domains_out); ++ assert_int_equal(ret, EOK); ++ assert_true(strcmp(domains_in, domains_out) == 0); ++ ++ /* Setting the domainResolutionOrder to ":" ... ++ * ++ * It means, the domainResolutionOrder is set, but if there's another ++ * domainResolutionOrder with lower precedence those must be ignored. ++ */ ++ domains_in = ":"; ++ ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, ++ dn, domains_in); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_domain_resolution_order(test_ctx, ++ test_ctx->tctx->dom->sysdb, dn, ++ &domains_out); ++ assert_int_equal(ret, EOK); ++ assert_true(strcmp(domains_in, domains_out) == 0); ++ ++ /* Changing the domainResolutionOrder */ ++ domains_in = "bar:foobar:foo"; ++ ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, ++ dn, domains_in); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_domain_resolution_order(test_ctx, ++ test_ctx->tctx->dom->sysdb, dn, ++ &domains_out); ++ assert_int_equal(ret, EOK); ++ assert_true(strcmp(domains_out, domains_out) == 0); ++ ++ /* Removing the domainResolutionOrder attribute */ ++ domains_in = NULL; ++ ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, ++ dn, domains_in); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_get_domain_resolution_order(test_ctx, ++ test_ctx->tctx->dom->sysdb, dn, ++ &domains_out); ++ assert_int_equal(ret, ENOENT); ++ assert_true(domains_out == NULL); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ int rv; ++ int no_cleanup = 0; ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, ++ _("Do not delete the test database after a test run"), NULL }, ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_sysdb_domain_resolution_order_ops, ++ test_sysdb_domain_resolution_order_setup, ++ test_sysdb_domain_resolution_order_teardown), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++ tests_set_cwd(); ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE); ++ test_dom_suite_setup(TESTS_PATH); ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ ++ if (rv == 0 && no_cleanup == 0) { ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE); ++ } ++ return rv; ++} +-- +2.9.3 + diff --git a/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch b/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch new file mode 100644 index 0000000..3dcbc82 --- /dev/null +++ b/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch @@ -0,0 +1,369 @@ +From 4ff821a9a37cb43f9c34faef4b5ccbdc8dc6a7e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 22 Mar 2017 13:40:20 +0100 +Subject: [PATCH 48/54] IPA: Get ipaDomainsResolutionOrder from ipaConfig +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +ipaDomainsResolutionOrder provides a list of domains that have to be +looked up firstly during cache_req searches. + +This commit only fetches this list from the server and stores its value +at sysdb so we can make use of it later on this patch series. + +There are no tests for newly introduced sysdb methods are those are +basically only calling sysdb_update_domain_resolution_order(), +sysdb_get_domain_resolution_order() and +sysdb_get_use_domain_resolution_order() which are have tests written +for. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/db/sysdb.h | 11 +++ + src/db/sysdb_subdomains.c | 67 +++++++++++++++ + src/providers/ipa/ipa_subdomains.c | 168 ++++++++++++++++++++++++++++++++++--- + 3 files changed, 234 insertions(+), 12 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 42d2857ed7765c17e7d84b0da93ed07758fbe012..75a07d4d2effb028ec654342113f8478e1eba10e 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -489,6 +489,17 @@ int sysdb_transaction_cancel(struct sysdb_ctx *sysdb); + /* functions related to subdomains */ + errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name); + ++errno_t sysdb_domain_get_domain_resolution_order( ++ TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ const char *domain_name, ++ const char **_domain_resolution_order); ++ ++errno_t sysdb_domain_update_domain_resolution_order( ++ struct sysdb_ctx *sysdb, ++ const char *domain_name, ++ const char *domain_resolution_order); ++ + errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb, + const char *name, const char *realm, + const char *flat_name, const char *domain_id, +diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c +index 916dbba153d8c08837425f6fd29a20f5a6aa9fc9..e2a4f7bb1fcdf20b6b7e04efc7f396d1c3d08f0f 100644 +--- a/src/db/sysdb_subdomains.c ++++ b/src/db/sysdb_subdomains.c +@@ -22,6 +22,7 @@ + + #include "util/util.h" + #include "db/sysdb_private.h" ++#include "db/sysdb_domain_resolution_order.h" + + static errno_t + check_subdom_config_file(struct confdb_ctx *confdb, +@@ -1210,3 +1211,69 @@ done: + talloc_free(tmp_ctx); + return ret; + } ++ ++errno_t ++sysdb_domain_get_domain_resolution_order(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ const char *domain_name, ++ const char **_domain_resolution_order) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_dn *dn; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_get_domain_resolution_order(mem_ctx, sysdb, dn, ++ _domain_resolution_order); ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++errno_t ++sysdb_domain_update_domain_resolution_order(struct sysdb_ctx *sysdb, ++ const char *domain_name, ++ const char *domain_resolution_order) ++{ ++ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_dn *dn; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_update_domain_resolution_order(sysdb, dn, ++ domain_resolution_order); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_update_domain_resolution_order() failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index a07b88fe2f499353293ba90345552413c9792f4b..01a0ce812d861b24565d2f71f27d6b8ceb2965bc 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -29,6 +29,7 @@ + #include "providers/ipa/ipa_common.h" + #include "providers/ipa/ipa_id.h" + #include "providers/ipa/ipa_opts.h" ++#include "providers/ipa/ipa_config.h" + + #include + +@@ -51,6 +52,8 @@ + + #define IPA_ASSIGNED_ID_VIEW "ipaAssignedIDView" + ++#define IPA_DOMAIN_RESOLUTION_ORDER "ipaDomainResolutionOrder" ++ + /* do not refresh more often than every 5 seconds for now */ + #define IPA_SUBDOMAIN_REFRESH_LIMIT 5 + +@@ -1681,6 +1684,117 @@ static errno_t ipa_subdomains_view_name_recv(struct tevent_req *req) + return EOK; + } + ++struct ipa_domain_resolution_order_state { ++ struct sss_domain_info *domain; ++}; ++ ++static void ipa_domain_resolution_order_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++ipa_domain_resolution_order_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ipa_subdomains_ctx *sd_ctx, ++ struct sdap_handle *sh) ++{ ++ struct ipa_domain_resolution_order_state *state; ++ struct tevent_req *subreq; ++ struct tevent_req *req; ++ const char *attrs[] = {IPA_DOMAIN_RESOLUTION_ORDER, NULL}; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct ipa_domain_resolution_order_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->domain = sd_ctx->be_ctx->domain; ++ ++ subreq = ipa_get_config_send(state, ev, sh, sd_ctx->sdap_id_ctx->opts, ++ state->domain->name, attrs); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediately; ++ } ++ ++ tevent_req_set_callback(subreq, ipa_domain_resolution_order_done, req); ++ ++ return req; ++ ++immediately: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static void ipa_domain_resolution_order_done(struct tevent_req *subreq) ++{ ++ struct ipa_domain_resolution_order_state *state; ++ struct tevent_req *req; ++ struct sysdb_attrs *config = NULL; ++ const char *domain_resolution_order = NULL; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ipa_domain_resolution_order_state); ++ ++ ret = ipa_get_config_recv(subreq, state, &config); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get the domains' resolution order configuration " ++ "from the server [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ if (config != NULL) { ++ ret = sysdb_attrs_get_string(config, IPA_DOMAIN_RESOLUTION_ORDER, ++ &domain_resolution_order); ++ if (ret != EOK && ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get the domains' resolution order configuration " ++ "value [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } else if (ret == ENOENT) { ++ domain_resolution_order = NULL; ++ } ++ } ++ ++ ret = sysdb_domain_update_domain_resolution_order( ++ state->domain->sysdb, state->domain->name, ++ domain_resolution_order); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_domain_update_resolution_order() [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static errno_t ipa_domain_resolution_order_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ return EOK; ++} + + struct ipa_subdomains_refresh_state { + struct tevent_context *ev; +@@ -1695,6 +1809,7 @@ static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq); ++static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq); + + static struct tevent_req * + ipa_subdomains_refresh_send(TALLOC_CTX *mem_ctx, +@@ -1916,7 +2031,6 @@ static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq) + { + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; +- int dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); +@@ -1924,24 +2038,55 @@ static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq) + + ret = ipa_subdomains_view_name_recv(subreq); + talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unable to get view name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ subreq = ipa_domain_resolution_order_send(state, state->ev, state->sd_ctx, ++ sdap_id_op_handle(state->sdap_op)); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ tevent_req_set_callback(subreq, ++ ipa_domain_refresh_resolution_order_done, ++ req); ++} ++ ++static void ++ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq) ++{ ++ struct ipa_subdomains_refresh_state *state; ++ struct tevent_req *req; ++ int dp_error; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ipa_subdomains_refresh_state); ++ ++ ret = ipa_domain_resolution_order_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Unable to get the domains order resolution [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ + ret = sdap_id_op_done(state->sdap_op, ret, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ipa_subdomains_refresh_retry(req); +- if (ret != EOK) { +- goto done; +- } +- return; + } else if (dp_error == DP_ERR_OFFLINE) { + ret = ERR_OFFLINE; +- goto done; +- } else if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get view name " +- "[%d]: %s\n", ret, sss_strerror(ret)); +- goto done; + } + +-done: + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Unable to refresh subdomains [%d]: %s\n", + ret, sss_strerror(ret)); +@@ -1949,7 +2094,6 @@ done: + return; + } + +- DEBUG(SSSDBG_TRACE_FUNC, "Subdomains refreshed.\n"); + tevent_req_done(req); + } + +-- +2.9.3 + diff --git a/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch b/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch new file mode 100644 index 0000000..71624c8 --- /dev/null +++ b/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch @@ -0,0 +1,54 @@ +From 33190863b66f90cac410b7a9b9cc95e4f9891013 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Fri, 24 Mar 2017 08:08:58 +0100 +Subject: [PATCH 49/54] IPA_SUBDOMAINS: Rename _refresh_view() to + _refresh_view_name() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This method got renamed in order to match better with what it does +currently. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/providers/ipa/ipa_subdomains.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index 01a0ce812d861b24565d2f71f27d6b8ceb2965bc..bf6f6ab1fa8bfff7ea102dd219c9ddbbab065b2b 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -1808,7 +1808,7 @@ static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq); +-static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq); ++static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq); + static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq); + + static struct tevent_req * +@@ -2023,11 +2023,12 @@ static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq) + return; + } + +- tevent_req_set_callback(subreq, ipa_subdomains_refresh_view_done, req); ++ tevent_req_set_callback(subreq, ipa_subdomains_refresh_view_name_done, ++ req); + return; + } + +-static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq) ++static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq) + { + struct ipa_subdomains_refresh_state *state; + struct tevent_req *req; +-- +2.9.3 + diff --git a/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch b/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch new file mode 100644 index 0000000..a1da431 --- /dev/null +++ b/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch @@ -0,0 +1,347 @@ +From d36c2acde1f29865c2cefedebc214ba48bb227e7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Fri, 24 Mar 2017 17:46:04 +0100 +Subject: [PATCH 50/54] IPA: Get ipaDomainsResolutionOrder from IPA ID View +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +ipaDomainsResolutionOrder provides a list of domains that have to be +looked up firstly during cache_req searches. + +This commit only fetches this list from the server and stores its value +at sysdb so we can make use of it later on this patch series. + +There are no tests for newly introduced sysdb methods are those are +basically only calling sysdb_update_domain_resolution_order(), +sysdb_get_domain_resolution_order() and +sysdb_get_use_domain_resolution_order() which are have tests written +for. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/db/sysdb.h | 9 ++ + src/db/sysdb_views.c | 66 ++++++++++++++ + src/providers/ipa/ipa_subdomains.c | 182 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 257 insertions(+) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 75a07d4d2effb028ec654342113f8478e1eba10e..62c561be9452a284a8ddf8ebb45720265852c8b0 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -533,6 +533,15 @@ errno_t sysdb_update_view_name(struct sysdb_ctx *sysdb, const char *view_name); + errno_t sysdb_get_view_name(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + char **view_name); + ++errno_t sysdb_update_view_domain_resolution_order( ++ struct sysdb_ctx *sysdb, ++ const char *domain_resolution_order); ++ ++errno_t sysdb_get_view_domain_resolution_order( ++ TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ const char **_domain_resolution_order); ++ + static inline bool is_default_view(const char *view_name) + { + /* NULL is treated as default */ +diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c +index 1c416dd14049237e9f35d52f154035e3ff861469..20db9b06183d68b33bb19f498513d7f5cf84b1cf 100644 +--- a/src/db/sysdb_views.c ++++ b/src/db/sysdb_views.c +@@ -22,6 +22,9 @@ + #include "util/util.h" + #include "util/cert.h" + #include "db/sysdb_private.h" ++#include "db/sysdb_domain_resolution_order.h" ++ ++#define SYSDB_VIEWS_BASE "cn=views,cn=sysdb" + + /* In general is should not be possible that there is a view container without + * a view name set. But to be on the safe side we return both information +@@ -179,6 +182,69 @@ done: + return ret; + } + ++errno_t ++sysdb_get_view_domain_resolution_order(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ const char **_domain_resolution_order) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_dn *dn; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_VIEWS_BASE); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_get_domain_resolution_order(mem_ctx, sysdb, dn, ++ _domain_resolution_order); ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++errno_t ++sysdb_update_view_domain_resolution_order(struct sysdb_ctx *sysdb, ++ const char *domain_resolution_order) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_dn *dn; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_VIEWS_BASE); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_update_domain_resolution_order(sysdb, dn, ++ domain_resolution_order); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_update_domain_resolution_order() failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ + errno_t sysdb_delete_view_tree(struct sysdb_ctx *sysdb, const char *view_name) + { + struct ldb_dn *dn; +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index bf6f6ab1fa8bfff7ea102dd219c9ddbbab065b2b..ef348adf4a36e870f44387bd700f5c2beea3bfd6 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -1684,6 +1684,151 @@ static errno_t ipa_subdomains_view_name_recv(struct tevent_req *req) + return EOK; + } + ++struct ipa_subdomains_view_domain_resolution_order_state { ++ struct sss_domain_info *domain; ++ const char *view_name; ++}; ++ ++static void ++ipa_subdomains_view_domain_resolution_order_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++ipa_subdomains_view_domain_resolution_order_send( ++ TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct ipa_subdomains_ctx *sd_ctx, ++ struct sdap_handle *sh) ++{ ++ struct ipa_subdomains_view_domain_resolution_order_state *state; ++ struct tevent_req *subreq; ++ struct tevent_req *req; ++ const char *attrs[] = { IPA_DOMAIN_RESOLUTION_ORDER, NULL }; ++ char *ldap_basedn; ++ char *base; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct ipa_subdomains_view_domain_resolution_order_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->domain = sd_ctx->be_ctx->domain; ++ state->view_name = sd_ctx->ipa_id_ctx->view_name; ++ ++ ret = domain_to_basedn(state, sd_ctx->be_ctx->domain->name, &ldap_basedn); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n"); ++ goto immediately; ++ } ++ ++ base = talloc_asprintf(state, "cn=%s,cn=views,cn=accounts,%s", ++ sd_ctx->ipa_id_ctx->view_name, ldap_basedn); ++ if (base == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ ret = ENOMEM; ++ goto immediately; ++ } ++ ++ subreq = sdap_get_generic_send( ++ state, ev, sd_ctx->sdap_id_ctx->opts, sh, ++ base, LDAP_SCOPE_BASE, NULL, attrs, NULL, 0, ++ dp_opt_get_int(sd_ctx->sdap_id_ctx->opts->basic, ++ SDAP_ENUM_SEARCH_TIMEOUT), ++ false); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto immediately; ++ } ++ ++ tevent_req_set_callback(subreq, ipa_subdomains_view_domain_resolution_order_done, ++ req); ++ ++ return req; ++ ++immediately: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static void ++ipa_subdomains_view_domain_resolution_order_done(struct tevent_req *subreq) ++{ ++ struct ipa_subdomains_view_domain_resolution_order_state *state; ++ struct tevent_req *req; ++ size_t reply_count; ++ struct sysdb_attrs **reply; ++ const char *domain_resolution_order; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, ++ struct ipa_subdomains_view_domain_resolution_order_state); ++ ++ ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to get view name [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ if (reply_count > 1) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "More than one object returned.\n"); ++ ret = EINVAL; ++ goto done; ++ } else if (reply_count == 0) { ++ domain_resolution_order = NULL; ++ } else { ++ /* reply_count == 1 */ ++ ret = sysdb_attrs_get_string(reply[0], IPA_DOMAIN_RESOLUTION_ORDER, ++ &domain_resolution_order); ++ if (ret != EOK && ret != ENOENT) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get the view domains' resolution order " ++ "configuration value for view [%s] [%d]: %s\n", ++ state->view_name, ret, sss_strerror(ret)); ++ goto done; ++ } else if (ret == ENOENT) { ++ domain_resolution_order = NULL; ++ } ++ } ++ ++ ret = sysdb_update_view_domain_resolution_order(state->domain->sysdb, ++ domain_resolution_order); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_update_view_domain_resolution_order() [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static errno_t ++ipa_subdomains_view_domain_resolution_order_recv(struct tevent_req *req) ++{ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ return EOK; ++} ++ + struct ipa_domain_resolution_order_state { + struct sss_domain_info *domain; + }; +@@ -1809,6 +1954,8 @@ static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq); + static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq); ++static void ipa_subdomains_refresh_view_domain_resolution_order_done( ++ struct tevent_req *subreq); + static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq); + + static struct tevent_req * +@@ -2047,6 +2194,41 @@ static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq) + return; + } + ++ subreq = ipa_subdomains_view_domain_resolution_order_send( ++ state, ++ state->ev, ++ state->sd_ctx, ++ sdap_id_op_handle(state->sdap_op)); ++ if (subreq == NULL) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ tevent_req_set_callback(subreq, ++ ipa_subdomains_refresh_view_domain_resolution_order_done, ++ req); ++} ++ ++static void ++ipa_subdomains_refresh_view_domain_resolution_order_done(struct tevent_req *subreq) ++{ ++ struct ipa_subdomains_refresh_state *state; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ipa_subdomains_refresh_state); ++ ++ ret = ipa_subdomains_view_domain_resolution_order_recv(subreq); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unable to get view domain_resolution order [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ + subreq = ipa_domain_resolution_order_send(state, state->ev, state->sd_ctx, + sdap_id_op_handle(state->sdap_op)); + if (subreq == NULL) { +-- +2.9.3 + diff --git a/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch b/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch new file mode 100644 index 0000000..b75b73d --- /dev/null +++ b/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch @@ -0,0 +1,37 @@ +From 8c7c97d1b3af8c99af43dcaff7ae1d9315a03835 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 21 Mar 2017 20:56:38 +0100 +Subject: [PATCH 51/54] DLINKLIST: Add DLIST_FOR_EACH_SAFE macro +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This macro, as DLIST_FOR_EACH, iterates over the whole list. The main +difference between both is that in the _SAFE version the pointer to the +next list node is stored, allowing us to delete the current node safely. + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/util/dlinklist.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/util/dlinklist.h b/src/util/dlinklist.h +index 4f6aef830e914c22654970081263d43461c1750f..017c60468e66dbec15724d5f4832da412f42136b 100644 +--- a/src/util/dlinklist.h ++++ b/src/util/dlinklist.h +@@ -147,4 +147,9 @@ do { \ + #define DLIST_FOR_EACH(p, list) \ + for ((p) = (list); (p) != NULL; (p) = (p)->next) + ++#define DLIST_FOR_EACH_SAFE(p, q, list) \ ++ for ((p) = (list), (q) = (p) != NULL ? (p)->next : NULL; \ ++ (p) != NULL; \ ++ (p) = (q), (q) = (p) != NULL ? (p)->next : NULL) ++ + #endif /* _DLINKLIST_H */ +-- +2.9.3 + diff --git a/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch b/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch new file mode 100644 index 0000000..4086ace --- /dev/null +++ b/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch @@ -0,0 +1,795 @@ +From 5091507c13dfdbde29aa75d6e90eda9ddaa89cff Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Sun, 26 Mar 2017 00:27:50 +0100 +Subject: [PATCH 52/54] CACHE_REQ: Make use of domainResolutionOrder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +domainResolutionOrder has been introduced in the previous commits and +allows the admin to set up a specific order which the domains will be +resolved during a lookup and with this patch we can take advantage of +this. + +In order to have it working a new structure has been added +(struct domain_resolution_order) to the responder context and will be +used by the cache_req to perform the lookups based on this list. + +As the ipaDomainResolutionOrder may be set globally on IPA or per View, +SSSD does respect the following precedence order: View > Globally. + +The way the list is built is quite simple, basically having the domains +present on ipaDomainResolutionOrder as the first domains (in that +specific order) and then appending the remaining domains to this list. +The final result is a completely flat list with all the domains +respecting the specified order (it's important to remember that the +domains not specified won't follow any specific order, they're just +"random" based on the domains list present in the responder context. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + Makefile.am | 3 + + src/responder/common/cache_req/cache_req.c | 89 +++++++----- + src/responder/common/cache_req/cache_req_domain.c | 166 ++++++++++++++++++++++ + src/responder/common/cache_req/cache_req_domain.h | 46 ++++++ + src/responder/common/responder.h | 5 + + src/responder/common/responder_common.c | 153 ++++++++++++++++++++ + src/responder/common/responder_get_domains.c | 14 ++ + src/tests/cmocka/common_mock_resp.c | 6 + + src/tests/cmocka/common_mock_resp_dp.c | 7 + + src/tests/cmocka/test_nss_srv.c | 4 + + src/tests/cwrap/Makefile.am | 1 + + 11 files changed, 457 insertions(+), 37 deletions(-) + create mode 100644 src/responder/common/cache_req/cache_req_domain.c + create mode 100644 src/responder/common/cache_req/cache_req_domain.h + +diff --git a/Makefile.am b/Makefile.am +index 450785bf4c482cce1e1440f1336879150537888e..573b37c52fdeab1add4ea057e1e1844ea4d348a5 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -528,6 +528,7 @@ SSSD_CACHE_REQ_OBJ = \ + src/responder/common/cache_req/cache_req_result.c \ + src/responder/common/cache_req/cache_req_search.c \ + src/responder/common/cache_req/cache_req_data.c \ ++ src/responder/common/cache_req/cache_req_domain.c \ + src/responder/common/cache_req/plugins/cache_req_common.c \ + src/responder/common/cache_req/plugins/cache_req_enum_users.c \ + src/responder/common/cache_req/plugins/cache_req_enum_groups.c \ +@@ -689,6 +690,7 @@ dist_noinst_HEADERS = \ + src/responder/common/iface/responder_iface.h \ + src/responder/common/iface/responder_iface_generated.h \ + src/responder/common/cache_req/cache_req.h \ ++ src/responder/common/cache_req/cache_req_domain.h \ + src/responder/common/cache_req/cache_req_plugin.h \ + src/responder/common/cache_req/cache_req_private.h \ + src/responder/common/data_provider/rdp.h \ +@@ -2199,6 +2201,7 @@ responder_socket_access_tests_SOURCES = \ + src/responder/common/responder_common.c \ + src/responder/common/responder_packet.c \ + src/responder/common/responder_cmd.c \ ++ src/responder/common/cache_req/cache_req_domain.c \ + src/responder/common/data_provider/rdp_message.c \ + src/responder/common/data_provider/rdp_client.c \ + $(SSSD_RESPONDER_IFACE_OBJ) \ +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index aca150d69b398ceb1a52e5cd6a87d35dbc87020b..483126396f8addbad744ae03bfc739801cd0c18b 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -24,6 +24,7 @@ + #include + + #include "util/util.h" ++#include "responder/common/responder.h" + #include "responder/common/cache_req/cache_req_private.h" + #include "responder/common/cache_req/cache_req_plugin.h" + +@@ -316,7 +317,7 @@ struct cache_req_search_domains_state { + struct cache_req *cr; + + /* work data */ +- struct sss_domain_info *domain; ++ struct cache_req_domain *cr_domain; + struct sss_domain_info *selected_domain; + struct cache_req_result **results; + size_t num_results; +@@ -330,13 +331,14 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req); + + static void cache_req_search_domains_done(struct tevent_req *subreq); + +-struct tevent_req *cache_req_search_domains_send(TALLOC_CTX *mem_ctx, +- struct tevent_context *ev, +- struct cache_req *cr, +- struct sss_domain_info *domain, +- bool check_next, +- bool bypass_cache, +- bool bypass_dp) ++struct tevent_req * ++cache_req_search_domains_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct cache_req *cr, ++ struct cache_req_domain *cr_domain, ++ bool check_next, ++ bool bypass_cache, ++ bool bypass_dp) + { + struct tevent_req *req; + struct cache_req_search_domains_state *state = NULL; +@@ -352,7 +354,7 @@ struct tevent_req *cache_req_search_domains_send(TALLOC_CTX *mem_ctx, + state->ev = ev; + state->cr = cr; + +- state->domain = domain; ++ state->cr_domain = cr_domain; + state->check_next = check_next; + state->dp_success = true; + state->bypass_cache = bypass_cache; +@@ -378,6 +380,7 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req) + struct cache_req_search_domains_state *state; + struct tevent_req *subreq; + struct cache_req *cr; ++ struct sss_domain_info *domain; + uint32_t next_domain_flag; + bool is_domain_valid; + bool allow_no_fqn; +@@ -389,11 +392,21 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req) + next_domain_flag = cr->plugin->get_next_domain_flags; + allow_no_fqn = cr->plugin->allow_missing_fqn; + +- while (state->domain != NULL) { ++ while (state->cr_domain != NULL) { ++ domain = state->cr_domain->domain; ++ /* As the cr_domain list is a flatten version of the domains ++ * list, we have to ensure to only go through the subdomains in ++ * case it's specified in the plugin to do so. ++ */ ++ if (next_domain_flag == 0 && IS_SUBDOMAIN(domain)) { ++ state->cr_domain = state->cr_domain->next; ++ continue; ++ } ++ + /* Check if this domain is valid for this request. */ +- is_domain_valid = cache_req_validate_domain(cr, state->domain); ++ is_domain_valid = cache_req_validate_domain(cr, domain); + if (!is_domain_valid) { +- state->domain = get_next_domain(state->domain, next_domain_flag); ++ state->cr_domain = state->cr_domain->next; + continue; + } + +@@ -401,18 +414,18 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req) + * qualified names on domain less search. We do not descend into + * subdomains here since those are implicitly qualified. + */ +- if (state->check_next && !allow_no_fqn && state->domain->fqnames) { +- state->domain = get_next_domain(state->domain, 0); ++ if (state->check_next && !allow_no_fqn && domain->fqnames) { ++ state->cr_domain = state->cr_domain->next; + continue; + } + +- state->selected_domain = state->domain; ++ state->selected_domain = domain; + +- if (state->domain == NULL) { ++ if (domain == NULL) { + break; + } + +- ret = cache_req_set_domain(cr, state->domain); ++ ret = cache_req_set_domain(cr, domain); + if (ret != EOK) { + return ret; + } +@@ -427,8 +440,7 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req) + + /* we will continue with the following domain the next time */ + if (state->check_next) { +- state->domain = get_next_domain(state->domain, +- cr->plugin->get_next_domain_flags); ++ state->cr_domain = state->cr_domain->next; + } + + return EAGAIN; +@@ -625,11 +637,12 @@ static void cache_req_input_parsed(struct tevent_req *subreq); + static errno_t cache_req_select_domains(struct tevent_req *req, + const char *domain_name); + +-static errno_t cache_req_search_domains(struct tevent_req *req, +- struct sss_domain_info *domain, +- bool check_next, +- bool bypass_cache, +- bool bypass_dp); ++static errno_t ++cache_req_search_domains(struct tevent_req *req, ++ struct cache_req_domain *oredered_domain, ++ bool check_next, ++ bool bypass_cache, ++ bool bypass_dp); + + static void cache_req_done(struct tevent_req *subreq); + +@@ -778,7 +791,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req, + const char *domain_name) + { + struct cache_req_state *state = NULL; +- struct sss_domain_info *domain; ++ struct cache_req_domain *cr_domain; + bool check_next; + bool bypass_cache; + bool bypass_dp; +@@ -798,29 +811,30 @@ static errno_t cache_req_select_domains(struct tevent_req *req, + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing a single domain search\n"); + +- domain = responder_get_domain(state->cr->rctx, domain_name); +- if (domain == NULL) { ++ cr_domain = cache_req_domain_get_domain_by_name( ++ state->cr->rctx->cr_domains, domain_name); ++ if (cr_domain == NULL) { + return ERR_DOMAIN_NOT_FOUND; + } +- + check_next = false; + } else { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing a multi-domain search\n"); + +- domain = state->cr->rctx->domains; ++ cr_domain = state->cr->rctx->cr_domains; + check_next = true; + } + +- return cache_req_search_domains(req, domain, check_next, ++ return cache_req_search_domains(req, cr_domain, check_next, + bypass_cache, bypass_dp); + } + +-static errno_t cache_req_search_domains(struct tevent_req *req, +- struct sss_domain_info *domain, +- bool check_next, +- bool bypass_cache, +- bool bypass_dp) ++static errno_t ++cache_req_search_domains(struct tevent_req *req, ++ struct cache_req_domain *cr_domain, ++ bool check_next, ++ bool bypass_cache, ++ bool bypass_dp) + { + struct tevent_req *subreq; + struct cache_req_state *state = NULL; +@@ -832,8 +846,9 @@ static errno_t cache_req_search_domains(struct tevent_req *req, + bypass_cache ? "bypass" : "check", + bypass_dp ? "bypass" : "check"); + +- subreq = cache_req_search_domains_send(state, state->ev, state->cr, domain, +- check_next, bypass_cache, bypass_dp); ++ subreq = cache_req_search_domains_send(state, state->ev, state->cr, ++ cr_domain, check_next, ++ bypass_cache, bypass_dp); + if (subreq == NULL) { + return ENOMEM; + } +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +new file mode 100644 +index 0000000000000000000000000000000000000000..bbabd695f1c6b6c29b7e61f571382ab9adfb0ea2 +--- /dev/null ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -0,0 +1,166 @@ ++/* ++ Authors: ++ Fabiano Fidêncio ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "responder/common/cache_req/cache_req_domain.h" ++ ++struct cache_req_domain * ++cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, ++ const char *name) ++{ ++ struct cache_req_domain *dom; ++ struct cache_req_domain *ret = NULL; ++ ++ DLIST_FOR_EACH(dom, domains) { ++ if (sss_domain_get_state(dom->domain) == DOM_DISABLED) { ++ continue; ++ } ++ ++ if (strcasecmp(dom->domain->name, name) == 0 || ++ (dom->domain->flat_name != NULL && ++ strcasecmp(dom->domain->flat_name, name) == 0)) { ++ ret = dom; ++ break; ++ } ++ } ++ ++ if (ret == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unknown domains [%s].\n", name); ++ } ++ ++ return ret; ++} ++ ++void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains) ++{ ++ struct cache_req_domain *p, *q, *r; ++ ++ DLIST_FOR_EACH_SAFE(p, q, *cr_domains) { ++ r = p; ++ DLIST_REMOVE(*cr_domains, p); ++ talloc_zfree(r); ++ } ++ ++ *cr_domains = NULL; ++} ++ ++static struct cache_req_domain * ++cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, ++ char **resolution_order) ++{ ++ struct cache_req_domain *cr_domains = NULL; ++ struct cache_req_domain *cr_domain; ++ struct sss_domain_info *dom; ++ char *name; ++ int flag = SSS_GND_ALL_DOMAINS; ++ int i; ++ errno_t ret; ++ ++ if (resolution_order != NULL) { ++ for (i = 0; resolution_order[i] != NULL; i++) { ++ name = resolution_order[i]; ++ for (dom = domains; dom; dom = get_next_domain(dom, flag)) { ++ if (strcasecmp(name, dom->name) != 0) { ++ continue; ++ } ++ ++ cr_domain = talloc_zero(mem_ctx, struct cache_req_domain); ++ if (cr_domain == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ cr_domain->domain = dom; ++ ++ DLIST_ADD_END(cr_domains, cr_domain, ++ struct cache_req_domain *); ++ break; ++ } ++ } ++ } ++ ++ for (dom = domains; dom; dom = get_next_domain(dom, flag)) { ++ if (string_in_list(dom->name, resolution_order, false)) { ++ continue; ++ } ++ ++ cr_domain = talloc_zero(mem_ctx, struct cache_req_domain); ++ if (cr_domain == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ cr_domain->domain = dom; ++ ++ DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ cache_req_domain_list_zfree(&cr_domains); ++ } ++ ++ return cr_domains; ++} ++ ++struct cache_req_domain * ++cache_req_domain_new_list_from_domain_resolution_order( ++ TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, ++ const char *domain_resolution_order) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct cache_req_domain *cr_domains = NULL; ++ char **list = NULL; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return NULL; ++ } ++ ++ if (domain_resolution_order != NULL) { ++ if (strcmp(domain_resolution_order, ":") != 0) { ++ ret = split_on_separator(tmp_ctx, domain_resolution_order, ':', ++ true, true, &list, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "split_on_separator() failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ } ++ } ++ ++ cr_domains = cache_req_domain_new_list_from_string_list(mem_ctx, domains, ++ list); ++ if (cr_domains == NULL) { ++ ret = ENOMEM; ++ DEBUG(SSSDBG_OP_FAILURE, ++ "cache_req_domain_new_list_from_domain_resolution_order() " ++ "failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++done: ++ talloc_free(tmp_ctx); ++ return cr_domains; ++} +diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h +new file mode 100644 +index 0000000000000000000000000000000000000000..41c50e8c293d7b032cb2f05482c40e93e4f723dc +--- /dev/null ++++ b/src/responder/common/cache_req/cache_req_domain.h +@@ -0,0 +1,46 @@ ++/* ++ Authors: ++ Fabiano Fidêncio ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef _CACHE_REQ_DOMAIN_H_ ++#define _CACHE_REQ_DOMAIN_H_ ++ ++#include "responder/common/responder.h" ++ ++struct cache_req_domain { ++ struct sss_domain_info *domain; ++ ++ struct cache_req_domain *prev; ++ struct cache_req_domain *next; ++}; ++ ++struct cache_req_domain * ++cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, ++ const char *name); ++ ++struct cache_req_domain * ++cache_req_domain_new_list_from_domain_resolution_order( ++ TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, ++ const char *domain_resolution_order); ++ ++void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains); ++ ++ ++#endif /* _CACHE_REQ_DOMAIN_H_ */ +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index 4d1048a1e0c9be2cad91317d51baf670ecb3307e..29e3f95caf484f43307c9c28d4abd3f50f360a95 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -37,6 +37,7 @@ + #include "sbus/sssd_dbus.h" + #include "responder/common/negcache.h" + #include "sss_client/sss_cli.h" ++#include "responder/common/cache_req/cache_req_domain.h" + + extern hash_table_t *dp_requests; + +@@ -113,6 +114,8 @@ struct resp_ctx { + int domains_timeout; + int client_idle_timeout; + ++ struct cache_req_domain *cr_domains; ++ + time_t last_request_time; + int idle_timeout; + struct tevent_timer *idle; +@@ -387,4 +390,6 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx, + bool name_is_upn, + const char *orig_name); + ++errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx); ++ + #endif /* __SSS_RESPONDER_H__ */ +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 76f43609651217e537ffa515aaf5b5caa98a2e90..1792a4c3771fa326c7cca31e1981dce315c03758 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -1453,3 +1453,156 @@ fail: + return ret; + + } ++ ++/* ====== Helper functions for the domain resolution order ======= */ ++static struct cache_req_domain * ++sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, ++ struct sysdb_ctx *sysdb) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct cache_req_domain *cr_domains = NULL; ++ const char *domain_resolution_order = NULL; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return NULL; ++ } ++ ++ ret = sysdb_get_view_domain_resolution_order(tmp_ctx, sysdb, ++ &domain_resolution_order); ++ if (ret != EOK && ret != ENOENT) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "sysdb_get_view_cache_req_domain() failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ /* Using mem_ctx (which is rctx) directly here to avoid copying ++ * this memory around. */ ++ cr_domains = cache_req_domain_new_list_from_domain_resolution_order( ++ mem_ctx, domains, domain_resolution_order); ++ if (cr_domains == NULL) { ++ ret = ENOMEM; ++ DEBUG(SSSDBG_DEFAULT, ++ "cache_req_domain_new_list_from_domain_resolution_order() " ++ "failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++done: ++ talloc_free(tmp_ctx); ++ return cr_domains; ++} ++ ++static struct cache_req_domain * ++sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, ++ struct sysdb_ctx *sysdb, ++ const char *domain) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct cache_req_domain *cr_domains = NULL; ++ const char *domain_resolution_order = NULL; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return NULL; ++ } ++ ++ ret = sysdb_domain_get_domain_resolution_order(tmp_ctx, sysdb, domain, ++ &domain_resolution_order); ++ ++ if (ret != EOK && ret != ENOENT) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "sysdb_domain_get_cache_req_domain() failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ /* Using mem_ctx (which is rctx) directly here to avoid copying ++ * this memory around. */ ++ cr_domains = cache_req_domain_new_list_from_domain_resolution_order( ++ mem_ctx, domains, domain_resolution_order); ++ if (cr_domains == NULL) { ++ DEBUG(SSSDBG_DEFAULT, ++ "cache_req_domain_new_list_from_domain_resolution_order() " ++ "failed [%d]: [%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++done: ++ talloc_free(tmp_ctx); ++ return cr_domains; ++} ++ ++errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) ++{ ++ struct cache_req_domain *cr_domains = NULL; ++ struct sss_domain_info *dom; ++ errno_t ret; ++ ++ for (dom = rctx->domains; dom != NULL; dom = dom->next) { ++ if (dom->provider != NULL && strcmp(dom->provider, "ipa") == 0) { ++ break; ++ } ++ } ++ ++ if (dom == NULL) { ++ cr_domains = cache_req_domain_new_list_from_domain_resolution_order( ++ rctx, rctx->domains, NULL); ++ if (cr_domains == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to flatten the list of domains.\n"); ++ } ++ goto done; ++ } ++ ++ if (dom->has_views) { ++ cr_domains = sss_resp_new_cr_domains_from_ipa_id_view(rctx, ++ rctx->domains, ++ dom->sysdb); ++ if (cr_domains == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Failed to use ipaDomainResolutionOrder set for the " ++ "view \"%s\".\n" ++ "Trying to fallback to use ipaDomainOrderResolution " ++ "set in ipaConfig for the domain: %s.\n", ++ dom->view_name, dom->name); ++ } else { ++ goto done; ++ } ++ } ++ ++ cr_domains = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains, ++ dom->sysdb, ++ dom->name); ++ if (cr_domains == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Failed to use ipaDomainResolutionOrder set in ipaConfig " ++ "for the domain: \"%s\".\n" ++ "No ipaDomainResolutionOrder will be followed.\n", ++ dom->name); ++ } else { ++ goto done; ++ } ++ ++ cr_domains = cache_req_domain_new_list_from_domain_resolution_order( ++ rctx, rctx->domains, NULL); ++ if (cr_domains == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to flatten the list of domains.\n"); ++ goto done; ++ } ++ ++done: ++ ret = cr_domains != NULL ? EOK : ENOMEM; ++ ++ cache_req_domain_list_zfree(&rctx->cr_domains); ++ rctx->cr_domains = cr_domains; ++ ++ return ret; ++} +diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c +index 0f9c01214631200f9687635f6302fa5c07e8a1fe..8c90b7773e248e1dd6d846c5050e1931fc50c786 100644 +--- a/src/responder/common/responder_get_domains.c ++++ b/src/responder/common/responder_get_domains.c +@@ -192,6 +192,13 @@ struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, + + if (state->dom == NULL) { + /* All domains were local */ ++ ret = sss_resp_populate_cr_domains(state->rctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_resp_populate_cr_domains() failed [%d]: [%s]\n", ++ ret, sss_strerror(ret)); ++ goto immediately; ++ } + ret = EOK; + goto immediately; + } +@@ -253,6 +260,13 @@ sss_dp_get_domains_process(struct tevent_req *subreq) + if (state->dom == NULL) { + /* All domains were local */ + set_time_of_last_request(state->rctx); ++ ret = sss_resp_populate_cr_domains(state->rctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_resp_populate_cr_domains() failed [%d]: [%s]\n", ++ ret, sss_strerror(ret)); ++ goto fail; ++ } + tevent_req_done(req); + return; + } +diff --git a/src/tests/cmocka/common_mock_resp.c b/src/tests/cmocka/common_mock_resp.c +index 88808b1b9394b7a9ee7e58b30b4fbd9d467493d3..175101fc51fd395d792b1fccaecdb327caef2b64 100644 +--- a/src/tests/cmocka/common_mock_resp.c ++++ b/src/tests/cmocka/common_mock_resp.c +@@ -51,6 +51,12 @@ mock_rctx(TALLOC_CTX *mem_ctx, + rctx->ev = ev; + rctx->domains = domains; + rctx->pvt_ctx = pvt_ctx; ++ if (domains != NULL) { ++ ret = sss_resp_populate_cr_domains(rctx); ++ if (ret != EOK) { ++ return NULL; ++ } ++ } + return rctx; + } + +diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c +index 5db5255ab61231870982c4b78a39504ae8954bcd..4b38a38e6f53499132f9fe14a0ec0af157cf85ca 100644 +--- a/src/tests/cmocka/common_mock_resp_dp.c ++++ b/src/tests/cmocka/common_mock_resp_dp.c +@@ -21,6 +21,7 @@ + */ + + #include "util/util.h" ++#include "responder/common/responder.h" + #include "tests/cmocka/common_mock_resp.h" + + /* Mock DP requests that finish immediatelly and return +@@ -165,6 +166,12 @@ sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, + bool force, + const char *hint) + { ++ errno_t ret; ++ ret = sss_resp_populate_cr_domains(rctx); ++ if (ret != EOK) { ++ return NULL; ++ } ++ + return test_req_succeed_send(mem_ctx, rctx->ev); + } + +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index ede72b341b60842ad470df2794aa90ea9797e999..2f526660cbbbf2443dbae4e213c1336feb6c661e 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -3440,6 +3440,10 @@ static int nss_subdom_test_setup(void **state) + nss_test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + ++ ret = sss_resp_populate_cr_domains(nss_test_ctx->rctx); ++ assert_int_equal(ret, EOK); ++ assert_non_null(nss_test_ctx->rctx->cr_domains); ++ + nss_test_ctx->subdom = nss_test_ctx->tctx->dom->subdomains; + + ret = store_group(nss_test_ctx, nss_test_ctx->subdom, +diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am +index 4a4090df9296aadde308249f533e7ba246e92f93..c99ebde5f0fc18d1283392cbb307434579d5d811 100644 +--- a/src/tests/cwrap/Makefile.am ++++ b/src/tests/cwrap/Makefile.am +@@ -41,6 +41,7 @@ SSSD_CACHE_REQ_OBJ = \ + ../../../src/responder/common/cache_req/cache_req_result.c \ + ../../../src/responder/common/cache_req/cache_req_search.c \ + ../../../src/responder/common/cache_req/cache_req_data.c \ ++ ../../../src/responder/common/cache_req/cache_req_domain.c \ + ../../../src/responder/common/cache_req/plugins/cache_req_common.c \ + ../../../src/responder/common/cache_req/plugins/cache_req_enum_users.c \ + ../../../src/responder/common/cache_req/plugins/cache_req_enum_groups.c \ +-- +2.9.3 + diff --git a/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch b/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch new file mode 100644 index 0000000..0db08c7 --- /dev/null +++ b/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch @@ -0,0 +1,81 @@ +From 3a3b761bc89aa860ca7e6af323c3e0425306014c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Sun, 26 Mar 2017 01:49:53 +0100 +Subject: [PATCH 53/54] UTIL: Expose replace_char() as sss_replace_char() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This method is going to be used in the follow-up patch for replacing ',' +by ':' so we can keep the domain resolution order option consitent with +the way it's set on IPA side and still keep consistent with the way +lists are represented on sssd.conf file. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/util/string_utils.c | 12 ++++++------ + src/util/util.h | 5 +++++ + 2 files changed, 11 insertions(+), 6 deletions(-) + +diff --git a/src/util/string_utils.c b/src/util/string_utils.c +index 872b7e29e55e8628085affd07f3363019aae5ee9..1215ec96a57089a13f455812adf5a0b0812afa6d 100644 +--- a/src/util/string_utils.c ++++ b/src/util/string_utils.c +@@ -22,10 +22,10 @@ + + #include "util/util.h" + +-static char *replace_char(TALLOC_CTX *mem_ctx, +- const char *in, +- const char match, +- const char sub) ++char *sss_replace_char(TALLOC_CTX *mem_ctx, ++ const char *in, ++ const char match, ++ const char sub) + { + char *p; + char *out; +@@ -63,7 +63,7 @@ char * sss_replace_space(TALLOC_CTX *mem_ctx, + return talloc_strdup(mem_ctx, orig_name); + } + +- return replace_char(mem_ctx, orig_name, ' ', subst); ++ return sss_replace_char(mem_ctx, orig_name, ' ', subst); + } + + char * sss_reverse_replace_space(TALLOC_CTX *mem_ctx, +@@ -81,7 +81,7 @@ char * sss_reverse_replace_space(TALLOC_CTX *mem_ctx, + return talloc_strdup(mem_ctx, orig_name); + } + +- return replace_char(mem_ctx, orig_name, subst, ' '); ++ return sss_replace_char(mem_ctx, orig_name, subst, ' '); + } + + errno_t guid_blob_to_string_buf(const uint8_t *blob, char *str_buf, +diff --git a/src/util/util.h b/src/util/util.h +index 82760940269ad8883e725e3a5cf463486c9cfd36..2170c5fb7cffda3910d2b58e33ec7abe3ec4a7d4 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -600,6 +600,11 @@ errno_t name_to_well_known_sid(const char *dom, const char *name, + const char **sid); + + /* from string_utils.c */ ++char *sss_replace_char(TALLOC_CTX *mem_ctx, ++ const char *in, ++ const char match, ++ const char sub); ++ + char * sss_replace_space(TALLOC_CTX *mem_ctx, + const char *orig_name, + const char replace_char); +-- +2.9.3 + diff --git a/SOURCES/0054-Add-domain_resolution_order-config-option.patch b/SOURCES/0054-Add-domain_resolution_order-config-option.patch new file mode 100644 index 0000000..e91960d --- /dev/null +++ b/SOURCES/0054-Add-domain_resolution_order-config-option.patch @@ -0,0 +1,205 @@ +From 26b838f2229483952aeec92a3446acef828244c4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Sun, 26 Mar 2017 03:00:14 +0200 +Subject: [PATCH 54/54] Add domain_resolution_order config option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is the local equivalent of option of ipaDomainResolutionOrder and +has precedence over the ones set on IPA side making the precedence order +to be like: Local > View > Globally. + +As done for the IPA side configurations, the domains which were not +explicitly set up will be apennded to the final of the +domain_resolution_order list in the very same order they're presented in +the "domains" option of [sssd] section in the config file. There's no +guarantee of order for the subdomains though. + +It's also important to mention that no expansion magic is performed on +our side. It means that if 'example.com' is set it does *not* stand for +all its subdomains DNS wise (like 'foo.example.com', 'bar.example.com', +etc). + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.h | 1 + + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/SSSDConfigTest.py | 7 ++++++- + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.conf | 1 + + src/man/sssd.conf.5.xml | 20 ++++++++++++++++++++ + src/responder/common/responder.h | 1 + + src/responder/common/responder_common.c | 27 +++++++++++++++++++++++++++ + 8 files changed, 58 insertions(+), 1 deletion(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index fb60675ca8beb2c2a157bf021ed9cad362742988..56a603652d6c8256735e7f8b125300ff7b254645 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -74,6 +74,7 @@ + #define CONFDB_MONITOR_CERT_VERIFICATION "certificate_verification" + #define CONFDB_MONITOR_DISABLE_NETLINK "disable_netlink" + #define CONFDB_MONITOR_ENABLE_FILES_DOM "enable_files_domain" ++#define CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER "domain_resolution_order" + + /* Both monitor and domains */ + #define CONFDB_NAME_REGEX "re_expression" +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index 03a1a43336604bb815626e64cb54052bdf87acf2..e7fb7673d393d4f12910f355d3edf33f4390c1f1 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -66,6 +66,7 @@ option_strings = { + 'override_space': _('All spaces in group or user names will be replaced with this character'), + 'disable_netlink' : _('Tune sssd to honor or ignore netlink state changes'), + 'enable_files_domain' : _('Enable or disable the implicit files domain'), ++ 'domain_resolution_order': _('A specific order of the domains to be looked up'), + + # [nss] + 'enum_cache_timeout' : _('Enumeration cache timeout length (seconds)'), +diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py +index 457a6f0a09e7139a05f29f8bef7e475fe3b58ec2..6899bf8ae04bf210546c8cbdba8235f094e23dc0 100755 +--- a/src/config/SSSDConfigTest.py ++++ b/src/config/SSSDConfigTest.py +@@ -94,6 +94,10 @@ class SSSDConfigTestValid(unittest.TestCase): + self.assertTrue('default_domain_suffix' in new_options) + self.assertEquals(new_options['default_domain_suffix'][0], str) + ++ self.assertTrue('domain_resolution_order' in new_options) ++ self.assertEquals(new_options['domain_resolution_order'][0], list) ++ self.assertEquals(new_options['domain_resolution_order'][1], str) ++ + del sssdconfig + + def testDomains(self): +@@ -314,7 +318,8 @@ class SSSDConfigTestSSSDService(unittest.TestCase): + 'certificate_verification', + 'override_space', + 'disable_netlink', +- 'enable_files_domain'] ++ 'enable_files_domain', ++ 'domain_resolution_order'] + + self.assertTrue(type(options) == dict, + "Options should be a dictionary") +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 933ebccd828189d923d2186753dfbc0b5c0814ce..41efcea552a82c5492a0d21a8d0797ee42cdc8c7 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -43,6 +43,7 @@ option = override_space + option = config_file_version + option = disable_netlink + option = enable_files_domain ++option = domain_resolution_order + + [rule/allowed_nss_options] + validator = ini_allowed_options +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index 08cecf00367aaaab3794a48bd1e728421a996e49..6965028e1ca748f8b6677d9fc1faa66d5c307a0c 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -32,6 +32,7 @@ certificate_verification = str, None, false + override_space = str, None, false + disable_netlink = bool, None, false + enable_files_domain = str, None, false ++domain_resolution_order = list, str, false + + [nss] + # Name service +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 1c27742cf0c1b6ffad23ab5b044bf4a168ed8f69..4fe13b85d511fb6a2ccc9b4de956710b05bc898c 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -542,6 +542,26 @@ + + + ++ ++ domain_resolution_order ++ ++ ++ Comma separated list of domains and subdomains ++ representing the lookup order that will be ++ followed. ++ The list doesn't have to include all possible ++ domains as the missing domains will be looked ++ up based on the order they're presented in the ++ domains configuration option. ++ The subdomains which are not listed as part of ++ lookup_order will be looked up ++ in a random order for each parent domain. ++ ++ ++ Default: Not set ++ ++ ++ + + + +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index 29e3f95caf484f43307c9c28d4abd3f50f360a95..4210307489fe25829a1674f254ecc7d185029698 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -115,6 +115,7 @@ struct resp_ctx { + int client_idle_timeout; + + struct cache_req_domain *cr_domains; ++ const char *domain_resolution_order; + + time_t last_request_time; + int idle_timeout; +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 1792a4c3771fa326c7cca31e1981dce315c03758..154d7dc7718c437d10e152fcba98161e2034fb14 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -1163,6 +1163,19 @@ int sss_process_init(TALLOC_CTX *mem_ctx, + rctx->override_space = tmp[0]; + } + ++ ret = confdb_get_string(rctx->cdb, rctx, ++ CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER, NULL, ++ &tmp); ++ if (ret == EOK) { ++ rctx->domain_resolution_order = sss_replace_char(rctx, tmp, ',', ':'); ++ } else { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot get the \"domain_resolution_order\" option.\n" ++ "The set up lookup_order won't be followed [%d]: %s.\n", ++ ret, sss_strerror(ret)); ++ } ++ + ret = sss_monitor_init(rctx, rctx->ev, monitor_intf, + svc_name, svc_version, MT_SVC_SERVICE, + rctx, &rctx->last_request_time, +@@ -1546,6 +1559,20 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + struct sss_domain_info *dom; + errno_t ret; + ++ if (rctx->domain_resolution_order != NULL) { ++ cr_domains = cache_req_domain_new_list_from_domain_resolution_order( ++ rctx, rctx->domains, rctx->domain_resolution_order); ++ ++ if (cr_domains == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Failed to use domain_resolution_order set in the config file.\n" ++ "Trying to fallback to use ipaDomainOrderResolution setup by " ++ "IPA.\n"); ++ } else { ++ goto done; ++ } ++ } ++ + for (dom = rctx->domains; dom != NULL; dom = dom->next) { + if (dom->provider != NULL && strcmp(dom->provider, "ipa") == 0) { + break; +-- +2.9.3 + diff --git a/SOURCES/0055-ssh-handle-binary-keys-correctly.patch b/SOURCES/0055-ssh-handle-binary-keys-correctly.patch new file mode 100644 index 0000000..05f87b1 --- /dev/null +++ b/SOURCES/0055-ssh-handle-binary-keys-correctly.patch @@ -0,0 +1,48 @@ +From 3ba9f82ac428f509df33e509a39eb783480f5d19 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 16 Mar 2017 12:38:08 +0100 +Subject: [PATCH 55/60] ssh: handle binary keys correctly + +Related to https://pagure.io/SSSD/sssd/issue/3332 + +Reviewed-by: Jakub Hrozek +--- + src/responder/ssh/ssh_reply.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c +index 807f4ee079128b4a3f1719de890ffac6e0d5b2e0..7093e47253b5687bab387feed5299c2d0841d43c 100644 +--- a/src/responder/ssh/ssh_reply.c ++++ b/src/responder/ssh/ssh_reply.c +@@ -32,6 +32,11 @@ + #include "responder/common/cache_req/cache_req.h" + #include "responder/ssh/ssh_private.h" + ++/* Locally used flag for libldb's ldb_message_element structure to indicate ++ * binary data. Since the related data is only used in memory it is safe. If ++ * should be used with care if libldb's I/O operations are involved. */ ++#define SSS_EL_FLAG_BIN_DATA (1<<4) ++ + static errno_t get_valid_certs_keys(TALLOC_CTX *mem_ctx, + struct ssh_ctx *ssh_ctx, + struct ldb_message_element *el_cert, +@@ -148,7 +153,7 @@ static errno_t decode_and_add_base64_data(struct sss_packet *packet, + } + + for (d = 0; d < el->num_values; d++) { +- if (skip_base64_decode) { ++ if (skip_base64_decode || (el->flags & SSS_EL_FLAG_BIN_DATA)) { + key = el->values[d].data; + key_len = el->values[d].length; + } else { +@@ -233,6 +238,7 @@ ssh_get_output_keys(TALLOC_CTX *mem_ctx, + } + + if (elements[i] != NULL) { ++ elements[i]->flags |= SSS_EL_FLAG_BIN_DATA; + num_keys += elements[i]->num_values; + i++; + } +-- +2.9.3 + diff --git a/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch b/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch new file mode 100644 index 0000000..cbeb244 --- /dev/null +++ b/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch @@ -0,0 +1,52 @@ +From 61c2661fe7445531f53ef298a98a21ae0278397c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 16 Mar 2017 13:00:48 +0100 +Subject: [PATCH 56/60] ssh: add support for certificates from non-default + views + +Reviewed-by: Jakub Hrozek +--- + src/responder/ssh/ssh_reply.c | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c +index 7093e47253b5687bab387feed5299c2d0841d43c..1bb9d331868cc18488718c24fd82f32af780b525 100644 +--- a/src/responder/ssh/ssh_reply.c ++++ b/src/responder/ssh/ssh_reply.c +@@ -204,7 +204,7 @@ ssh_get_output_keys(TALLOC_CTX *mem_ctx, + uint32_t i = 0; + errno_t ret; + +- elements = talloc_zero_array(mem_ctx, struct ldb_message_element *, 5); ++ elements = talloc_zero_array(mem_ctx, struct ldb_message_element *, 6); + if (elements == NULL) { + return ENOMEM; + } +@@ -244,6 +244,24 @@ ssh_get_output_keys(TALLOC_CTX *mem_ctx, + } + } + ++ if (DOM_HAS_VIEWS(domain)) { ++ user_cert = ldb_msg_find_element(msg, OVERRIDE_PREFIX SYSDB_USER_CERT); ++ if (user_cert != NULL) { ++ ret = get_valid_certs_keys(elements, ssh_ctx, user_cert, ++ &elements[i]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "get_valid_certs_keys failed.\n"); ++ goto done; ++ } ++ ++ if (elements[i] != NULL) { ++ elements[i]->flags |= SSS_EL_FLAG_BIN_DATA; ++ num_keys += elements[i]->num_values; ++ i++; ++ } ++ } ++ } ++ + *_elements = elements; + *_num_keys = num_keys; + +-- +2.9.3 + diff --git a/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch b/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch new file mode 100644 index 0000000..db56ae9 --- /dev/null +++ b/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch @@ -0,0 +1,60 @@ +From 01ed8c7d7fcd9090d0953f85ef0604cbcad4f48b Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 16 Mar 2017 20:43:08 +0100 +Subject: [PATCH 57/60] krb5: return to responder that pkinit is not available +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If pkinit is not available for a user but other authentication methods +are SSSD should still fall back to local certificate based +authentication if Smartcard credentials are provided. + +Resolves https://pagure.io/SSSD/sssd/issue/3343 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Lukáš Slebodník +--- + src/providers/krb5/krb5_child.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index 777a25f2a0ea434dde12d2396f6a35c2a1b86cd0..a4128dda6b0861a95dba223047d66c4158b1afb6 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -42,6 +42,10 @@ + + #define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw" + ++#define IS_SC_AUTHTOK(tok) ( \ ++ sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_PIN \ ++ || sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_KEYPAD) ++ + enum k5c_fast_opt { + K5C_FAST_NEVER, + K5C_FAST_TRY, +@@ -1529,12 +1533,17 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + * pre-auth module is missing or no Smartcard is inserted and only + * pkinit is available KRB5_PREAUTH_FAILED is returned. + * ERR_NO_AUTH_METHOD_AVAILABLE is used to indicate to the +- * frontend that local authentication might be tried. */ ++ * frontend that local authentication might be tried. ++ * Same is true if Smartcard credentials are given but only other ++ * authentication methods are available. */ + if (kr->pd->cmd == SSS_PAM_AUTHENTICATE + && kerr == KRB5_PREAUTH_FAILED +- && kr->password_prompting == false +- && kr->otp == false +- && kr->pkinit_prompting == false) { ++ && kr->pkinit_prompting == false ++ && (( kr->password_prompting == false ++ && kr->otp == false) ++ || ((kr->otp == true ++ || kr->password_prompting == true) ++ && IS_SC_AUTHTOK(kr->pd->authtok))) ) { + return ERR_NO_AUTH_METHOD_AVAILABLE; + } + return kerr; +-- +2.9.3 + diff --git a/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch b/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch new file mode 100644 index 0000000..2b6616e --- /dev/null +++ b/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch @@ -0,0 +1,153 @@ +From b8a36e1be5cdd2c61ddf8e40970270bb878d26a3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 22 Mar 2017 14:13:05 +0100 +Subject: [PATCH 58/60] IPA: add mapped attributes to user from trusted domains + +Allow the usage of the mapped attribute for the lookup of AD users on +IPA clients as already used for the normal LDAP lookup. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +--- + src/providers/ipa/ipa_s2n_exop.c | 33 ++++++++++++++++++++++++--------- + 1 file changed, 24 insertions(+), 9 deletions(-) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index c99312274073858e5e03f3e82c069dafc839eb61..05c32a24d61947e62884f460069083fb81f40fe0 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -761,6 +761,7 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom, + struct resp_attrs *simple_attrs, + const char *view_name, + struct sysdb_attrs *override_attrs, ++ struct sysdb_attrs *mapped_attrs, + bool update_initgr_timeout); + + static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, +@@ -1009,6 +1010,7 @@ struct ipa_s2n_get_list_state { + struct resp_attrs *attrs; + struct sss_domain_info *obj_domain; + struct sysdb_attrs *override_attrs; ++ struct sysdb_attrs *mapped_attrs; + }; + + static errno_t ipa_s2n_get_list_step(struct tevent_req *req); +@@ -1025,7 +1027,8 @@ static struct tevent_req *ipa_s2n_get_list_send(TALLOC_CTX *mem_ctx, + int entry_type, + enum request_types request_type, + enum req_input_type list_type, +- char **list) ++ char **list, ++ struct sysdb_attrs *mapped_attrs) + { + int ret; + struct ipa_s2n_get_list_state *state; +@@ -1057,6 +1060,7 @@ static struct tevent_req *ipa_s2n_get_list_send(TALLOC_CTX *mem_ctx, + state->request_type = request_type; + state->attrs = NULL; + state->override_attrs = NULL; ++ state->mapped_attrs = mapped_attrs; + + ret = ipa_s2n_get_list_step(req); + if (ret != EOK) { +@@ -1288,7 +1292,8 @@ static errno_t ipa_s2n_get_list_save_step(struct tevent_req *req) + + ret = ipa_s2n_save_objects(state->dom, &state->req_input, state->attrs, + NULL, state->ipa_ctx->view_name, +- state->override_attrs, false); ++ state->override_attrs, state->mapped_attrs, ++ false); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n"); + return ret; +@@ -1704,7 +1709,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq) + BE_REQ_GROUP, + REQ_FULL_WITH_MEMBERS, + REQ_INP_NAME, +- missing_list); ++ missing_list, NULL); + if (subreq == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "ipa_s2n_get_list_send failed.\n"); +@@ -1732,7 +1737,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq) + BE_REQ_USER, + REQ_FULL_WITH_MEMBERS, + REQ_INP_NAME, +- missing_list); ++ missing_list, NULL); + if (subreq == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "ipa_s2n_get_list_send failed.\n"); +@@ -1810,7 +1815,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq) + + if (ret == ENOENT || is_default_view(state->ipa_ctx->view_name)) { + ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs, +- state->simple_attrs, NULL, NULL, true); ++ state->simple_attrs, NULL, NULL, NULL, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n"); + goto done; +@@ -1978,6 +1983,7 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom, + struct resp_attrs *simple_attrs, + const char *view_name, + struct sysdb_attrs *override_attrs, ++ struct sysdb_attrs *mapped_attrs, + bool update_initgr_timeout) + { + int ret; +@@ -2305,6 +2311,15 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom, + goto done; + } + ++ if (mapped_attrs != NULL) { ++ ret = sysdb_set_user_attr(dom, name, mapped_attrs, ++ SYSDB_MOD_ADD); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n"); ++ goto done; ++ } ++ } ++ + if (gid_override_attrs != NULL) { + ret = sysdb_set_user_attr(dom, name, gid_override_attrs, + SYSDB_MOD_REP); +@@ -2487,7 +2502,7 @@ static void ipa_s2n_get_list_done(struct tevent_req *subreq) + &sid_str); + if (ret == ENOENT) { + ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs, +- state->simple_attrs, NULL, NULL, true); ++ state->simple_attrs, NULL, NULL, NULL, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n"); + goto fail; +@@ -2525,7 +2540,7 @@ static void ipa_s2n_get_list_done(struct tevent_req *subreq) + ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs, + state->simple_attrs, + state->ipa_ctx->view_name, +- state->override_attrs, true); ++ state->override_attrs, NULL, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n"); + tevent_req_error(req, ret); +@@ -2561,7 +2576,7 @@ static void ipa_s2n_get_user_get_override_done(struct tevent_req *subreq) + + ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs, + state->simple_attrs, state->ipa_ctx->view_name, +- override_attrs, true); ++ override_attrs, NULL, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n"); + tevent_req_error(req, ret); +@@ -2662,7 +2677,7 @@ struct tevent_req *ipa_get_subdom_acct_process_pac_send(TALLOC_CTX *mem_ctx, + dp_opt_get_int(ipa_ctx->sdap_id_ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), + BE_REQ_BY_SECID, REQ_FULL, REQ_INP_SECID, +- state->missing_sids); ++ state->missing_sids, NULL); + if (subreq == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_send failed.\n"); + ret = ENOMEM; +-- +2.9.3 + diff --git a/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch b/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch new file mode 100644 index 0000000..1440219 --- /dev/null +++ b/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch @@ -0,0 +1,209 @@ +From 537e057ef3bd140e418381f2ce74397ab8c34a73 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 24 Mar 2017 15:40:41 +0100 +Subject: [PATCH 59/60] IPA: lookup AD users by certificates on IPA clients + +Get a list of users mapped to a certificate back from the IPA server, +look them up and store them together with the certificate used for the +search as mapped attribute to the cache. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +--- + src/providers/ipa/ipa_s2n_exop.c | 109 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 105 insertions(+), 4 deletions(-) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 05c32a24d61947e62884f460069083fb81f40fe0..8a3391b4093f1547d84fe44a0f24b1d063d1e28c 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -52,7 +52,8 @@ enum response_types { + RESP_USER, + RESP_GROUP, + RESP_USER_GROUPLIST, +- RESP_GROUP_MEMBERS ++ RESP_GROUP_MEMBERS, ++ RESP_NAME_LIST + }; + + /* ==Sid2Name Extended Operation============================================= */ +@@ -366,8 +367,8 @@ static errno_t s2n_encode_request(TALLOC_CTX *mem_ctx, + break; + case BE_REQ_BY_CERT: + if (req_input->type == REQ_INP_CERT) { +- ret = ber_printf(ber, "{ees}", INP_CERT, request_type, +- req_input->inp.cert); ++ ret = ber_printf(ber, "{ees}", INP_CERT, request_type, ++ req_input->inp.cert); + } else { + DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n", + req_input->type); +@@ -463,6 +464,11 @@ done: + * GroupMemberList ::= SEQUENCE OF OCTET STRING + */ + ++struct name_list { ++ char *domain_name; ++ char *name; ++}; ++ + struct resp_attrs { + enum response_types response_type; + char *domain_name; +@@ -475,6 +481,7 @@ struct resp_attrs { + size_t ngroups; + char **groups; + struct sysdb_attrs *sysdb_attrs; ++ char **name_list; + }; + + static errno_t get_extra_attrs(BerElement *ber, struct resp_attrs *resp_attrs) +@@ -782,6 +789,9 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + struct resp_attrs *attrs = NULL; + char *sid_str; + bool is_v1 = false; ++ char **name_list = NULL; ++ ber_len_t ber_len; ++ char *fq_name = NULL; + + if (retoid == NULL || retdata == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Missing OID or data.\n"); +@@ -947,6 +957,53 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + goto done; + } + break; ++ case RESP_NAME_LIST: ++ tag = ber_scanf(ber, "{"); ++ if (tag == LBER_ERROR) { ++ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ while (ber_peek_tag(ber, &ber_len) == LBER_SEQUENCE) { ++ tag = ber_scanf(ber, "{aa}", &domain_name, &name); ++ if (tag == LBER_ERROR) { ++ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ fq_name = sss_create_internal_fqname(attrs, name, domain_name); ++ if (fq_name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_create_internal_fqname failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "[%s][%s][%s].\n", domain_name, name, ++ fq_name); ++ ++ ret = add_string_to_list(attrs, fq_name, &name_list); ++ ber_memfree(domain_name); ++ ber_memfree(name); ++ talloc_free(fq_name); ++ domain_name = NULL; ++ name = NULL; ++ fq_name = NULL; ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "add_to_name_list failed.\n"); ++ goto done; ++ } ++ } ++ ++ tag = ber_scanf(ber, "}}"); ++ if (tag == LBER_ERROR) { ++ DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ attrs->name_list = name_list; ++ break; + default: + DEBUG(SSSDBG_OP_FAILURE, "Unexpected response type [%d].\n", + type); +@@ -955,7 +1012,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + } + + attrs->response_type = type; +- if (type != RESP_SID) { ++ if (type != RESP_SID && type != RESP_NAME_LIST) { + attrs->domain_name = talloc_strdup(attrs, domain_name); + if (attrs->domain_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); +@@ -969,6 +1026,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + done: + ber_memfree(domain_name); + ber_memfree(name); ++ talloc_free(fq_name); + ber_free(ber, 1); + + if (ret == EOK) { +@@ -1332,6 +1390,7 @@ struct ipa_s2n_get_user_state { + struct resp_attrs *attrs; + struct resp_attrs *simple_attrs; + struct sysdb_attrs *override_attrs; ++ struct sysdb_attrs *mapped_attrs; + int exop_timeout; + }; + +@@ -1384,6 +1443,11 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx, + goto fail; + } + ++ if (entry_type == BE_REQ_BY_CERT) { ++ /* Only REQ_SIMPLE is supported for BE_REQ_BY_CERT */ ++ state->request_type = REQ_SIMPLE; ++ } ++ + ret = s2n_encode_request(state, dom->name, entry_type, state->request_type, + req_input, &bv_req); + if (ret != EOK) { +@@ -1785,6 +1849,43 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq) + goto done; + } + ++ if (state->simple_attrs->response_type == RESP_NAME_LIST ++ && state->req_input->type == REQ_INP_CERT) { ++ state->mapped_attrs = sysdb_new_attrs(state); ++ if (state->mapped_attrs == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_attrs_add_base64_blob(state->mapped_attrs, ++ SYSDB_USER_MAPPED_CERT, ++ state->req_input->inp.cert); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n"); ++ goto done; ++ } ++ ++ subreq = ipa_s2n_get_list_send(state, state->ev, ++ state->ipa_ctx, state->dom, ++ state->sh, state->exop_timeout, ++ BE_REQ_USER, ++ REQ_FULL_WITH_MEMBERS, ++ REQ_INP_NAME, ++ state->simple_attrs->name_list, ++ state->mapped_attrs); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "ipa_s2n_get_list_send failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ tevent_req_set_callback(subreq, ipa_s2n_get_list_done, ++ req); ++ ++ return; ++ } ++ + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected request type.\n"); +-- +2.9.3 + diff --git a/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch b/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch new file mode 100644 index 0000000..e6ed62e --- /dev/null +++ b/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch @@ -0,0 +1,30 @@ +From 1f29c3d5302dc4ca9f5f9c6fe64dc8de5381041f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 24 Mar 2017 15:41:37 +0100 +Subject: [PATCH 60/60] IPA: enable AD user lookup by certificate + +Without this the lookup by certificate for AD users on an IPA client +will just error out. + +Related to https://pagure.io/SSSD/sssd/issue/3050 + +Reviewed-by: Jakub Hrozek +--- + src/providers/ipa/ipa_subdomains_id.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c +index 4777d7cfd97fed39b807a659fd1f9000c7ff8625..3530af94ef59397db72465fcb0c4a03117a4d8bd 100644 +--- a/src/providers/ipa/ipa_subdomains_id.c ++++ b/src/providers/ipa/ipa_subdomains_id.c +@@ -399,6 +399,7 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx, + case BE_REQ_USER: + case BE_REQ_GROUP: + case BE_REQ_BY_SECID: ++ case BE_REQ_BY_CERT: + case BE_REQ_USER_AND_GROUP: + ret = EOK; + break; +-- +2.9.3 + diff --git a/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch b/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch new file mode 100644 index 0000000..31f541e --- /dev/null +++ b/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch @@ -0,0 +1,242 @@ +From 75a8d8e7996c35fd9bef504f2f4d3e308b7553c8 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 22 Mar 2017 12:53:17 +0100 +Subject: [PATCH 61/72] CONFDB: Introduce SSSD domain type to distinguish POSIX + and application domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +Adds a new option that allows to distinguish domains that do contain +POSIX users and groups and those that don't. The POSIX domains are the +default. The non-POSIX domains are selected by selecting an +"application" type domain. + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.c | 18 +++++++++++++++++- + src/confdb/confdb.h | 15 +++++++++++++++ + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/SSSDConfigTest.py | 2 ++ + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.conf | 1 + + src/man/sssd.conf.5.xml | 33 +++++++++++++++++++++++++++++++++ + src/util/domain_info_utils.c | 14 ++++++++++++++ + src/util/util.h | 1 + + 9 files changed, 85 insertions(+), 1 deletion(-) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index d82fd98ee02928b3c20df014528bd869ec946f92..70a1eb7b2c7e83dfa9d217a15c7d3d4c8580b891 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -1367,6 +1367,22 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + } + } + ++ domain->type = DOM_TYPE_POSIX; ++ tmp = ldb_msg_find_attr_as_string(res->msgs[0], ++ CONFDB_DOMAIN_TYPE, ++ CONFDB_DOMAIN_TYPE_POSIX); ++ if (tmp != NULL) { ++ if (strcasecmp(tmp, CONFDB_DOMAIN_TYPE_POSIX) == 0) { ++ domain->type = DOM_TYPE_POSIX; ++ } else if (strcasecmp(tmp, CONFDB_DOMAIN_TYPE_APP) == 0) { ++ domain->type = DOM_TYPE_APPLICATION; ++ } else { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Invalid value %s for [%s]\n", tmp, CONFDB_DOMAIN_TYPE); ++ goto done; ++ } ++ } ++ + ret = get_entry_as_uint32(res->msgs[0], &domain->subdomain_refresh_interval, + CONFDB_DOMAIN_SUBDOMAIN_REFRESH, 14400); + if (ret != EOK || domain->subdomain_refresh_interval == 0) { +@@ -1444,7 +1460,7 @@ int confdb_get_domains(struct confdb_ctx *cdb, + if (ret) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Error (%d [%s]) retrieving domain [%s], skipping!\n", +- ret, sss_strerror(ret), domlist[i]); ++ ret, sss_strerror(ret), domlist[i]); + continue; + } + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 56a603652d6c8256735e7f8b125300ff7b254645..a4046610f3cdbdb832de8924bf4397fb0018f2db 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -209,6 +209,9 @@ + #define CONFDB_DOMAIN_OFFLINE_TIMEOUT "offline_timeout" + #define CONFDB_DOMAIN_SUBDOMAIN_INHERIT "subdomain_inherit" + #define CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT "cached_auth_timeout" ++#define CONFDB_DOMAIN_TYPE "domain_type" ++#define CONFDB_DOMAIN_TYPE_POSIX "posix" ++#define CONFDB_DOMAIN_TYPE_APP "application" + + /* Local Provider */ + #define CONFDB_LOCAL_DEFAULT_SHELL "default_shell" +@@ -261,11 +264,23 @@ enum sss_domain_state { + DOM_INCONSISTENT, + }; + ++/** Whether the domain only supports looking up POSIX entries */ ++enum sss_domain_type { ++ /** This is the default domain type. It resolves only entries ++ * with the full POSIX set of attributes ++ */ ++ DOM_TYPE_POSIX, ++ /** In this mode, entries are typically resolved only by name */ ++ DOM_TYPE_APPLICATION, ++}; ++ + /** + * Data structure storing all of the basic features + * of a domain. + */ + struct sss_domain_info { ++ enum sss_domain_type type; ++ + char *name; + char *conn_name; + char *provider; +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index e7fb7673d393d4f12910f355d3edf33f4390c1f1..806611b6076048c08ce08c772dbd3cea5fdd656c 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -148,6 +148,7 @@ option_strings = { + 'selinux_provider' : _('SELinux provider'), + + # [domain] ++ 'domain_type' : _('Whether the domain is usable by the OS or by applications'), + 'min_id' : _('Minimum user ID'), + 'max_id' : _('Maximum user ID'), + 'enumerate' : _('Enable enumerating all users/groups'), +diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py +index 6899bf8ae04bf210546c8cbdba8235f094e23dc0..9b3175962c697e314b3d5d94c2bc5beda537b66e 100755 +--- a/src/config/SSSDConfigTest.py ++++ b/src/config/SSSDConfigTest.py +@@ -510,6 +510,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): + 'debug', + 'debug_level', + 'debug_timestamps', ++ 'domain_type', + 'min_id', + 'max_id', + 'timeout', +@@ -878,6 +879,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): + 'debug', + 'debug_level', + 'debug_timestamps', ++ 'domain_type', + 'min_id', + 'max_id', + 'timeout', +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 41efcea552a82c5492a0d21a8d0797ee42cdc8c7..3c857236eaa55b313d176bc4bb479918163b60d5 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -311,6 +311,7 @@ option = subdomains_provider + option = selinux_provider + + # Options available to all domains ++option = domain_type + option = min_id + option = max_id + option = timeout +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index 6965028e1ca748f8b6677d9fc1faa66d5c307a0c..a38b24208f89e4502e41625c540ea9958d5bbffe 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -129,6 +129,7 @@ selinux_provider = str, None, false + [domain] + # Options available to all domains + description = str, None, false ++domain_type = str, None, false + debug = int, None, false + debug_level = int, None, false + debug_timestamps = bool, None, false +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 4fe13b85d511fb6a2ccc9b4de956710b05bc898c..9abcff84a95ea1b27e36845e830cc125fdc89f90 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -1512,6 +1512,39 @@ pam_account_locked_message = Account locked, please contact help desk. + [domain/NAME] + + ++ domain_type (string) ++ ++ ++ Specifies whether the domain is meant to be used ++ by POSIX-aware clients such as the Name Service Switch ++ or by applications that do not need POSIX data to be ++ present or generated. Only objects from POSIX domains ++ are available to the operating system interfaces and ++ utilities. ++ ++ ++ Allowed values for this option are posix ++ and application. ++ ++ ++ POSIX domains are reachable by all services. Application ++ domains are only reachable from the InfoPipe responder (see ++ ++ sssd-ifp ++ 5 ++ ) and the PAM responder. ++ ++ ++ NOTE: The application domains are currently well tested with ++ id_provider=ldap only. ++ ++ ++ Default: posix ++ ++ ++ ++ ++ + min_id,max_id (integer) + + +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index a7f118842aa8ba870143b2f2b425a3e3c0ea5a78..2af7852f03f89b61f5b9fd8a244e98fb27b7e6a2 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -885,3 +885,17 @@ char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx, + subdomain->parent->name, + subdomain->name); + } ++ ++const char *sss_domain_type_str(struct sss_domain_info *dom) ++{ ++ if (dom == NULL) { ++ return "BUG: Invalid domain"; ++ } ++ switch (dom->type) { ++ case DOM_TYPE_POSIX: ++ return "POSIX"; ++ case DOM_TYPE_APPLICATION: ++ return "Application"; ++ } ++ return "Unknown"; ++} +diff --git a/src/util/util.h b/src/util/util.h +index 2170c5fb7cffda3910d2b58e33ec7abe3ec4a7d4..436550f5078cc173b8ed8cb58836d366f813146b 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -539,6 +539,7 @@ enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom); + void sss_domain_set_state(struct sss_domain_info *dom, + enum sss_domain_state state); + bool is_email_from_domain(const char *email, struct sss_domain_info *dom); ++const char *sss_domain_type_str(struct sss_domain_info *dom); + + struct sss_domain_info* + sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain, +-- +2.9.3 + diff --git a/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch b/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch new file mode 100644 index 0000000..24ecd56 --- /dev/null +++ b/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch @@ -0,0 +1,531 @@ +From 05ae58c86eae80c7e69fb809dc3cd89d0b7418f4 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 27 Mar 2017 09:48:46 +0200 +Subject: [PATCH 62/72] CONFDB: Allow configuring [application] sections as + non-POSIX domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +Allows to add a new section: + [application/$name] + +This section internally (on the confdb level) expands to: + [domain/$name] + domain_type = application + +The reasons to add this new section is two-fold. One, to make the +configuration of application domains more explicit and two, to make it +possible to share configuration between two domains, one POSIX and one +non-POSIX by application domain's inherit_from option: + [application/$name] + inherit_from = posix_domain_name + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.c | 288 ++++++++++++++++++++++++++++++++++++++++++++--- + src/confdb/confdb.h | 4 + + src/config/cfg_rules.ini | 9 +- + src/man/sssd.conf.5.xml | 77 +++++++++++++ + src/monitor/monitor.c | 8 ++ + 5 files changed, 368 insertions(+), 18 deletions(-) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index 70a1eb7b2c7e83dfa9d217a15c7d3d4c8580b891..88e114457deac3ca50c291a131122624fb6f6fe4 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -813,6 +813,50 @@ done: + return ret; + } + ++static int confdb_get_domain_section(TALLOC_CTX *mem_ctx, ++ struct confdb_ctx *cdb, ++ const char *section, ++ const char *name, ++ struct ldb_result **_res) ++{ ++ TALLOC_CTX *tmp_ctx; ++ int ret; ++ struct ldb_result *res; ++ struct ldb_dn *dn; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", name, section); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, ++ LDB_SCOPE_BASE, NULL, NULL); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ if (res->count == 0) { ++ ret = ENOENT; ++ goto done; ++ } else if (res->count > 1) { ++ ret = E2BIG; ++ goto done; ++ } ++ ++ *_res = talloc_steal(mem_ctx, res); ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ + static int confdb_get_domain_internal(struct confdb_ctx *cdb, + TALLOC_CTX *mem_ctx, + const char *name, +@@ -821,7 +865,6 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + struct sss_domain_info *domain; + struct ldb_result *res; + TALLOC_CTX *tmp_ctx; +- struct ldb_dn *dn; + const char *tmp; + int ret, val; + uint32_t entry_cache_timeout; +@@ -833,23 +876,15 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; + +- dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, +- "cn=%s,%s", name, CONFDB_DOMAIN_BASEDN); +- if (!dn) { +- ret = ENOMEM; +- goto done; +- } +- +- ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, +- LDB_SCOPE_BASE, NULL, NULL); +- if (ret != LDB_SUCCESS) { +- ret = EIO; +- goto done; +- } +- +- if (res->count != 1) { ++ ret = confdb_get_domain_section(tmp_ctx, cdb, CONFDB_DOMAIN_BASEDN, ++ name, &res); ++ if (ret == ENOENT) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unknown domain [%s]\n", name); +- ret = ENOENT; ++ goto done; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Error %d: %s while retrieving %s\n", ++ ret, sss_strerror(ret), name); + goto done; + } + +@@ -1841,3 +1876,222 @@ int confdb_ensure_files_domain(struct confdb_ctx *cdb, + return activate_files_domain(cdb, implicit_files_dom_name); + #endif /* ADD_FILES_DOMAIN */ + } ++ ++static int confdb_get_parent_domain(TALLOC_CTX *mem_ctx, ++ const char *name, ++ struct confdb_ctx *cdb, ++ struct ldb_result *app_dom, ++ struct ldb_result **_parent_dom) ++{ ++ const char *inherit_from; ++ ++ inherit_from = ldb_msg_find_attr_as_string(app_dom->msgs[0], ++ CONFDB_DOMAIN_INHERIT_FROM, NULL); ++ if (inherit_from == NULL) { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "%s does not inherit from any POSIX domain\n", name); ++ *_parent_dom = NULL; ++ return EOK; ++ } ++ ++ return confdb_get_domain_section(mem_ctx, cdb, ++ CONFDB_DOMAIN_BASEDN, inherit_from, ++ _parent_dom); ++} ++ ++static int confdb_add_app_domain(TALLOC_CTX *mem_ctx, ++ struct confdb_ctx *cdb, ++ const char *name) ++{ ++ char *cdb_path = NULL; ++ const char *val[2] = { NULL, NULL }; ++ int ret; ++ ++ cdb_path = talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL, name); ++ if (cdb_path == NULL) { ++ return ENOMEM; ++ } ++ ++ val[0] = CONFDB_DOMAIN_TYPE_APP; ++ ret = confdb_add_param(cdb, true, cdb_path, CONFDB_DOMAIN_TYPE, val); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add id_provider [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ return EOK; ++} ++ ++static int confdb_merge_parent_domain(const char *name, ++ struct confdb_ctx *cdb, ++ struct ldb_result *app_section) ++{ ++ int ret; ++ int ldb_flag; ++ struct ldb_result *parent_domain = NULL; ++ struct ldb_message *replace_msg = NULL; ++ struct ldb_message *app_msg = NULL; ++ struct ldb_dn *domain_dn; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); ++ return ENOMEM; ++ } ++ ++ domain_dn = ldb_dn_new_fmt(tmp_ctx, ++ cdb->ldb, ++ "%s=%s,%s", ++ CONFDB_DOMAIN_ATTR, ++ name, ++ CONFDB_DOMAIN_BASEDN); ++ if (domain_dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ /* Copy the parent domain parameters */ ++ ret = confdb_get_parent_domain(tmp_ctx, name, cdb, ++ app_section, &parent_domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot retrieve the parent domain [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ if (parent_domain != NULL) { ++ replace_msg = ldb_msg_copy(tmp_ctx, parent_domain->msgs[0]); ++ if (replace_msg == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ replace_msg->dn = domain_dn; ++ ++ for (unsigned i = 0; i < replace_msg->num_elements; i++) { ++ replace_msg->elements[i].flags = LDB_FLAG_MOD_ADD; ++ } ++ ++ ret = ldb_modify(cdb->ldb, replace_msg); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Inheriting options from parent domain failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ } ++ ++ /* Finally, add any app-domain specific overrides */ ++ app_msg = ldb_msg_new(tmp_ctx); ++ if (app_msg == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ app_msg->dn = domain_dn; ++ ++ for (unsigned i = 0; i < app_section->msgs[0]->num_elements; i++) { ++ struct ldb_message_element *el = NULL; ++ ++ if (replace_msg != NULL) { ++ el = ldb_msg_find_element(replace_msg, ++ app_section->msgs[0]->elements[i].name); ++ if (el == NULL) { ++ /* Adding an element */ ++ ldb_flag = LDB_FLAG_MOD_ADD; ++ } else { ++ /* Overriding an element */ ++ ldb_flag = LDB_FLAG_MOD_REPLACE; ++ } ++ } else { ++ /* If there was no domain to inherit from, just add all */ ++ ldb_flag = LDB_FLAG_MOD_ADD; ++ } ++ ++ ret = ldb_msg_add(app_msg, ++ &app_section->msgs[0]->elements[i], ++ ldb_flag); ++ if (ret != EOK) { ++ continue; ++ } ++ } ++ ++ ret = ldb_modify(cdb->ldb, app_msg); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Adding app-specific options failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Added a domain section for %s\n", name); ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++int confdb_expand_app_domains(struct confdb_ctx *cdb) ++{ ++ int ret; ++ char **domlist; ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_result *app_domain = NULL; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = confdb_get_string_as_list(cdb, tmp_ctx, ++ CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_MONITOR_ACTIVE_DOMAINS, ++ &domlist); ++ if (ret == ENOENT) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured, fatal error!\n"); ++ goto done; ++ } else if (ret != EOK ) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n"); ++ goto done; ++ } ++ ++ for (int i = 0; domlist[i]; i++) { ++ ret = confdb_get_domain_section(tmp_ctx, cdb, ++ CONFDB_APP_DOMAIN_BASEDN, domlist[i], ++ &app_domain); ++ if (ret == ENOENT) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "%s is not an app domain\n", domlist[i]); ++ continue; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Error %d: %s while retrieving %s\n", ++ ret, sss_strerror(ret), domlist[i]); ++ goto done; ++ } ++ ++ ret = confdb_add_app_domain(tmp_ctx, cdb, domlist[i]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot add the app domain section [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = confdb_merge_parent_domain(domlist[i], cdb, app_domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot add options into the app domain section [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ } ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index a4046610f3cdbdb832de8924bf4397fb0018f2db..5a8d377c312f641f544b1c7cf38826192462ea3c 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -164,6 +164,7 @@ + /* Domains */ + #define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s" + #define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config" ++#define CONFDB_APP_DOMAIN_BASEDN "cn=application,cn=config" + #define CONFDB_DOMAIN_ID_PROVIDER "id_provider" + #define CONFDB_DOMAIN_AUTH_PROVIDER "auth_provider" + #define CONFDB_DOMAIN_ACCESS_PROVIDER "access_provider" +@@ -212,6 +213,7 @@ + #define CONFDB_DOMAIN_TYPE "domain_type" + #define CONFDB_DOMAIN_TYPE_POSIX "posix" + #define CONFDB_DOMAIN_TYPE_APP "application" ++#define CONFDB_DOMAIN_INHERIT_FROM "inherit_from" + + /* Local Provider */ + #define CONFDB_LOCAL_DEFAULT_SHELL "default_shell" +@@ -398,6 +400,8 @@ int confdb_get_domains(struct confdb_ctx *cdb, + int confdb_ensure_files_domain(struct confdb_ctx *cdb, + const char *implicit_files_dom_name); + ++int confdb_expand_app_domains(struct confdb_ctx *cdb); ++ + /** + * Get a null-terminated linked-list of all domain names + * @param[in] mem_ctx The parent memory context for the value list +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 3c857236eaa55b313d176bc4bb479918163b60d5..8fd2d2c5236246394353a88c50d1510bd6233f77 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -12,6 +12,7 @@ section = secrets + section = kcm + section_re = ^secrets/users/[0-9]\+$ + section_re = ^domain/.*$ ++section_re = ^application/.*$ + + [rule/allowed_sssd_options] + validator = ini_allowed_options +@@ -286,7 +287,7 @@ option = responder_idle_timeout + + [rule/allowed_domain_options] + validator = ini_allowed_options +-section_re = ^domain/.*$ ++section_re = ^(domain|application)/.*$ + + option = debug + option = debug_level +@@ -684,3 +685,9 @@ option = ldap_user_ssh_public_key + option = ldap_user_uid_number + option = ldap_user_uuid + option = ldap_use_tokengroups ++ ++[rule/allowed_application_options] ++validator = ini_allowed_options ++section_re = ^application/.*$ ++ ++option = inherit_from +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 9abcff84a95ea1b27e36845e830cc125fdc89f90..8294793c765bfa6bf481693c7d7f206950454681 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -1539,6 +1539,10 @@ pam_account_locked_message = Account locked, please contact help desk. + id_provider=ldap only. + + ++ For an easy way to configure a non-POSIX domains, please ++ see the Application domains section. ++ ++ + Default: posix + + +@@ -2692,6 +2696,79 @@ subdomain_inherit = ldap_purge_cache_timeout + + + ++ ++ Application domains ++ ++ SSSD, with its D-Bus interface (see ++ ++ sssd-ifp ++ 5 ++ ) is appealing to applications ++ as a gateway to an LDAP directory where users and groups ++ are stored. However, contrary to the traditional SSSD ++ deployment where all users and groups either have POSIX ++ attributes or those attributes can be inferred from the ++ Windows SIDs, in many cases the users and groups in the ++ application support scenario have no POSIX attributes. ++ Instead of setting a ++ [domain/NAME] ++ section, the administrator can set up an ++ [application/NAME] ++ section that internally represents a domain with type ++ application optionally inherits settings ++ from a tradition SSSD domain. ++ ++ ++ Please note that the application domain must still be ++ explicitly enabled in the domains parameter ++ so that the lookup order between the application domain ++ and its POSIX sibling domain is set correctly. ++ ++ ++ Application domain parameters ++ ++ inherit_from (string) ++ ++ ++ The SSSD POSIX-type domain the application ++ domain inherits all settings from. The ++ application domain can moreover add its own ++ settings to the application settings that augment ++ or override the sibling ++ domain settings. ++ ++ ++ Default: Not set ++ ++ ++ ++ ++ ++ The following example illustrates the use of an application ++ domain. In this setup, the POSIX domain is connected to an LDAP ++ server and is used by the OS through the NSS responder. In addition, ++ the application domains also requests the telephoneNumber attribute, ++ stores it as the phone attribute in the cache and makes the phone ++ attribute reachable through the D-Bus interface. ++ ++ ++[sssd] ++domains = appdom, posixdom ++ ++[ifp] ++user_attributes = +phone ++ ++[domain/posixdom] ++id_provider = ldap ++ldap_uri = ldap://ldap.example.com ++ldap_search_base = dc=example,dc=com ++ ++[application/appdom] ++inherit_from = posixdom ++ldap_user_extra_attrs = phone:telephoneNumber ++ ++ ++ + + The local domain section + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index 7e7b5a07d11aecf1c0b11592213b90d385fd5076..2753b46667f7ae0b022776862c67a327d3356d6d 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -1064,6 +1064,14 @@ static int get_monitor_config(struct mt_ctx *ctx) + /* Not fatal */ + } + ++ ret = confdb_expand_app_domains(ctx->cdb); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to expand application domains\n"); ++ /* This must not be fatal so that SSSD keeps running and lets ++ * admin correct the error. ++ */ ++ } ++ + ret = confdb_get_domains(ctx->cdb, &ctx->domains); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured.\n"); +-- +2.9.3 + diff --git a/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch b/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch new file mode 100644 index 0000000..86b6c73 --- /dev/null +++ b/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch @@ -0,0 +1,976 @@ +From 5519295726bb2a0e88475e1d8deff0b8c0f65119 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 24 Mar 2017 10:39:12 +0100 +Subject: [PATCH 63/72] CACHE_REQ: Domain type selection in cache_req +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to: + https://pagure.io/SSSD/sssd/issue/3310 + +Adds a new enumeration cache_req_dom_type. It is a tri-state that +allows the caller to select which domains can be contacted - either only +POSIX, only application domains or any type. + +Not all plugins of cache_req have the new parameter added -- only those +that are usable/useful in a non-POSIX environment. For example, it makes +no sense to allow the selection for calls by ID because those are +inherently POSIX-specific. Also, services or netgroups are supported +only coming from POSIX domains. + +At the moment, the patch should not change any behaviour as all calls +default to contacting POSIX domains only. + +Reviewed-by: Pavel Březina +--- + src/responder/common/cache_req/cache_req.c | 80 ++++++++++++++++++++-- + src/responder/common/cache_req/cache_req.h | 19 +++++ + src/responder/common/cache_req/cache_req_private.h | 3 + + .../cache_req/plugins/cache_req_enum_groups.c | 4 +- + .../common/cache_req/plugins/cache_req_enum_svc.c | 3 +- + .../cache_req/plugins/cache_req_enum_users.c | 4 +- + .../cache_req/plugins/cache_req_group_by_filter.c | 5 +- + .../cache_req/plugins/cache_req_group_by_id.c | 4 +- + .../cache_req/plugins/cache_req_group_by_name.c | 5 +- + .../cache_req/plugins/cache_req_host_by_name.c | 4 +- + .../plugins/cache_req_initgroups_by_name.c | 5 +- + .../cache_req/plugins/cache_req_netgroup_by_name.c | 4 +- + .../cache_req/plugins/cache_req_object_by_id.c | 4 +- + .../cache_req/plugins/cache_req_object_by_name.c | 4 +- + .../cache_req/plugins/cache_req_object_by_sid.c | 4 +- + .../cache_req/plugins/cache_req_svc_by_name.c | 4 +- + .../cache_req/plugins/cache_req_svc_by_port.c | 4 +- + .../cache_req/plugins/cache_req_user_by_cert.c | 4 +- + .../cache_req/plugins/cache_req_user_by_filter.c | 5 +- + .../cache_req/plugins/cache_req_user_by_id.c | 4 +- + .../cache_req/plugins/cache_req_user_by_name.c | 9 ++- + src/responder/ifp/ifp_groups.c | 14 +++- + src/responder/ifp/ifp_users.c | 19 +++-- + src/responder/ifp/ifpsrv_cmd.c | 3 +- + src/responder/nss/nss_enum.c | 2 +- + src/responder/nss/nss_get_object.c | 3 +- + src/responder/pam/pamsrv_cmd.c | 5 +- + src/responder/sudo/sudosrv_get_sudorules.c | 3 +- + src/tests/cmocka/test_responder_cache_req.c | 62 ++++++++++++++--- + 29 files changed, 246 insertions(+), 47 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index 483126396f8addbad744ae03bfc739801cd0c18b..3a5fecf34427437bbf95317e05c5bd8b07b4537d 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -89,12 +89,31 @@ static errno_t cache_req_set_plugin(struct cache_req *cr, + return EOK; + } + ++static const char * ++cache_req_dom_type_as_str(struct cache_req *cr) ++{ ++ if (cr == NULL) { ++ return "BUG: Invalid cache_req pointer\n"; ++ } ++ switch (cr->req_dom_type) { ++ case CACHE_REQ_POSIX_DOM: ++ return "POSIX-only"; ++ case CACHE_REQ_APPLICATION_DOM: ++ return "Application-only"; ++ case CACHE_REQ_ANY_DOM: ++ return "Any"; ++ } ++ ++ return "Unknown"; ++} ++ + static struct cache_req * + cache_req_create(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct cache_req_data *data, + struct sss_nc_ctx *ncache, +- int midpoint) ++ int midpoint, ++ enum cache_req_dom_type req_dom_type) + { + struct cache_req *cr; + errno_t ret; +@@ -108,6 +127,7 @@ cache_req_create(TALLOC_CTX *mem_ctx, + cr->data = data; + cr->ncache = ncache; + cr->midpoint = midpoint; ++ cr->req_dom_type = req_dom_type; + cr->req_start = time(NULL); + + /* It is perfectly fine to just overflow here. */ +@@ -145,8 +165,8 @@ cache_req_set_name(struct cache_req *cr, const char *name) + } + + static bool +-cache_req_validate_domain(struct cache_req *cr, +- struct sss_domain_info *domain) ++cache_req_validate_domain_enumeration(struct cache_req *cr, ++ struct sss_domain_info *domain) + { + if (!cr->plugin->require_enumeration) { + return true; +@@ -164,6 +184,52 @@ cache_req_validate_domain(struct cache_req *cr, + return true; + } + ++static bool ++cache_req_validate_domain_type(struct cache_req *cr, ++ struct sss_domain_info *domain) ++{ ++ bool valid = false; ++ ++ switch (cr->req_dom_type) { ++ case CACHE_REQ_POSIX_DOM: ++ valid = domain->type == DOM_TYPE_POSIX ? true : false; ++ break; ++ case CACHE_REQ_APPLICATION_DOM: ++ valid = domain->type == DOM_TYPE_APPLICATION ? true : false; ++ break; ++ case CACHE_REQ_ANY_DOM: ++ valid = true; ++ break; ++ } ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Request type %s for domain %s type %s is %svalid\n", ++ cache_req_dom_type_as_str(cr), ++ domain->name, ++ sss_domain_type_str(domain), ++ valid ? "" : "not "); ++ return valid; ++} ++ ++static bool ++cache_req_validate_domain(struct cache_req *cr, ++ struct sss_domain_info *domain) ++{ ++ bool ok; ++ ++ ok = cache_req_validate_domain_enumeration(cr, domain); ++ if (ok == false) { ++ return false; ++ } ++ ++ ok = cache_req_validate_domain_type(cr, domain); ++ if (ok == false) { ++ return false; ++ } ++ ++ return true; ++} ++ + static errno_t + cache_req_is_well_known_object(TALLOC_CTX *mem_ctx, + struct cache_req *cr, +@@ -651,6 +717,7 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int midpoint, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data) + { +@@ -667,7 +734,8 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx, + } + + state->ev = ev; +- state->cr = cr = cache_req_create(state, rctx, data, ncache, midpoint); ++ state->cr = cr = cache_req_create(state, rctx, data, ++ ncache, midpoint, req_dom_type); + if (state->cr == NULL) { + ret = ENOMEM; + goto done; +@@ -952,13 +1020,15 @@ cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data) + { + struct tevent_req *req; + + req = cache_req_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ req_dom_type, domain, data); + if (req == NULL) { + talloc_zfree(data); + return NULL; +diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h +index d0e5ff43921467fc191fd5cc7d5b49cc039b7f67..c04b2fba6f0445dcfcc9cfe1b5963ac975c39118 100644 +--- a/src/responder/common/cache_req/cache_req.h ++++ b/src/responder/common/cache_req/cache_req.h +@@ -57,6 +57,18 @@ enum cache_req_type { + CACHE_REQ_SENTINEL + }; + ++/* Whether to limit the request type to a certain domain type ++ * (POSIX/non-POSIX) ++ */ ++enum cache_req_dom_type { ++ /* Only look up data in POSIX domains */ ++ CACHE_REQ_POSIX_DOM, ++ /* Only look up data in application domains */ ++ CACHE_REQ_APPLICATION_DOM, ++ /* Look up data in any domain type */ ++ CACHE_REQ_ANY_DOM ++}; ++ + /* Input data. */ + + struct cache_req_data; +@@ -172,6 +184,7 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int midpoint, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data); + +@@ -191,6 +204,7 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name); + +@@ -228,6 +242,7 @@ cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *pem_cert); + +@@ -240,6 +255,7 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name); + +@@ -264,6 +280,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name); + +@@ -274,6 +291,7 @@ struct tevent_req * + cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *filter); + +@@ -284,6 +302,7 @@ struct tevent_req * + cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *filter); + +diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h +index 2d3c1870795e4fd5667f603280edcee24f926220..851005c389f994b1bd2d04cda9b68df8b18492cc 100644 +--- a/src/responder/common/cache_req/cache_req_private.h ++++ b/src/responder/common/cache_req/cache_req_private.h +@@ -42,6 +42,8 @@ struct cache_req { + struct sss_domain_info *domain; + bool cache_first; + bool bypass_cache; ++ /* Only contact domains with this type */ ++ enum cache_req_dom_type req_dom_type; + + /* Debug information */ + uint32_t reqid; +@@ -108,6 +110,7 @@ cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + struct cache_req_data *data); + +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c +index dbb40c98339cc9295e3678e05340396aff51ac78..49ce3508e678862e4389657187b9659ce90fbd1c 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c +@@ -96,5 +96,7 @@ cache_req_enum_groups_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c +index 28dea33c601f500b9c7af0de3eb9e1c342f03522..499b994738d62707b4e86d5a8383e3e2b82e8c57 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c +@@ -97,5 +97,6 @@ cache_req_enum_svc_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c +index 3b1a85841e3ed853cd329dfa9d762cb7a05cbd43..b635354be6e9d2e2e2af1a6f867ac68e6cf7f085 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c +@@ -96,5 +96,7 @@ cache_req_enum_users_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c +index 6ce6ae0d63967ac50b813a47ac938251619948da..4377a476c36e5e03c8533bc62335b84fa1cee3ff 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c +@@ -140,6 +140,7 @@ struct tevent_req * + cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *filter) + { +@@ -151,5 +152,7 @@ cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL, +- 0, domain, data); ++ 0, ++ req_dom_type, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c +index e98f76f8cd20742b81ae247df61db159d2584a17..ad5b7d890a42f29b586ab8e0943fef3dfab1162d 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c +@@ -166,5 +166,7 @@ cache_req_group_by_id_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c +index af6f23ccfd68f952027462ba3e74ed7219d04651..de1e8f9442273acf386a2278b06f28ee63a7e3c6 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c +@@ -205,6 +205,7 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name) + { +@@ -216,5 +217,7 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ req_dom_type, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c +index 77b46831fec3abc4126ef9d9be67221469801094..1171cd63fac5cc1d36b31bf8a069f059705cae90 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c +@@ -117,5 +117,7 @@ cache_req_host_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c +index 307b65a24282838b99c472b50a71f06865aed3f0..f100aefe5c92279cde7e3209c7f48f5e2b35f135 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c +@@ -220,6 +220,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name) + { +@@ -231,5 +232,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ req_dom_type, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +index e49d6d84a41ce8dabf18c87373826f8e7b684bda..ab3e553d3ecb8ae09094dcfc938ed0ac01925327 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +@@ -150,5 +150,7 @@ cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c +index 046e313c83d1d4c75237b047be779201b8a5d3c0..9557bd15270b2eb1a0671f9ef91033efac29c3ac 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c +@@ -134,5 +134,7 @@ cache_req_object_by_id_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c +index 74d2b3dea287e890b38e4d5bb176ad2dc6337b7e..e236d1fa4aadcd87b192d34ebaf5f9ad8908b6c2 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c +@@ -228,5 +228,7 @@ cache_req_object_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c +index ab577663111cfd424e7f46308b2621af7f1ca264..dfec79da07d669165205a767cab22c2254686134 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c +@@ -143,5 +143,7 @@ cache_req_object_by_sid_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c +index ef13f097a8ae78ec9db5b7f6e14924b511578b34..b2bfb26ffed1a60ed8389fa89b0e728c8c6cf76c 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c +@@ -175,5 +175,7 @@ cache_req_svc_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c +index afa2eeeda12794de26e798aee4b88900bc87ed93..0e48437f4b64d26112be88af1eebc20f012b70fd 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c ++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c +@@ -149,5 +149,7 @@ cache_req_svc_by_port_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c +index f237c8d0fe3faf5aea553480f3f92eb279209a20..286a34db276e0098060982c572e2a68ceceebf60 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c +@@ -105,6 +105,7 @@ cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *pem_cert) + { +@@ -117,5 +118,6 @@ cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx, + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, + cache_refresh_percent, +- domain, data); ++ req_dom_type, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c +index eb71b42dad3a805298df0c8425409d571befb31b..c476814373cd784bf8dbbea1da7b010afe5bb4e4 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c +@@ -140,6 +140,7 @@ struct tevent_req * + cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *filter) + { +@@ -151,5 +152,7 @@ cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL, +- 0, domain, data); ++ 0, ++ req_dom_type, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c +index fa783714b7c67ca029d18a223b64a3a69e3e6929..9ba73292e5dc518e86c6e00e7e493d6871f28e70 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c +@@ -166,5 +166,7 @@ cache_req_user_by_id_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c +index 0670febdce2d51e0373045570dd07f56255db7bc..15da7d0d20b1ac97511a226daecc8ef7e7d2e7e4 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c +@@ -210,6 +210,7 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_nc_ctx *ncache, + int cache_refresh_percent, ++ enum cache_req_dom_type req_dom_type, + const char *domain, + const char *name) + { +@@ -221,7 +222,9 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ req_dom_type, domain, ++ data); + } + + struct tevent_req * +@@ -243,5 +246,7 @@ cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx, + } + + return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, +- cache_refresh_percent, domain, data); ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, domain, ++ data); + } +diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c +index 94d1e84cc9de75727d3c47c0a5a24790d21ce132..99908e96bd971bce4b4e9064a77d8413f837d743 100644 +--- a/src/responder/ifp/ifp_groups.c ++++ b/src/responder/ifp/ifp_groups.c +@@ -118,7 +118,9 @@ int ifp_groups_find_by_name(struct sbus_request *sbus_req, + } + + req = cache_req_group_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, +- ctx->rctx->ncache, 0, NULL, name); ++ ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, NULL, ++ name); + if (req == NULL) { + return ENOMEM; + } +@@ -271,6 +273,7 @@ static int ifp_groups_list_by_name_step(struct ifp_list_ctx *list_ctx) + req = cache_req_group_by_filter_send(list_ctx, + list_ctx->ctx->rctx->ev, + list_ctx->ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -355,7 +358,8 @@ int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req, + } + + req = cache_req_group_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx, +- domain, filter); ++ CACHE_REQ_POSIX_DOM, ++ domain, filter); + if (req == NULL) { + return ENOMEM; + } +@@ -522,7 +526,10 @@ static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx, + } + + subreq = cache_req_group_by_name_send(state, ev, ctx->rctx, +- ctx->rctx->ncache, 0, domain->name, name); ++ ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, ++ domain->name, ++ name); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; +@@ -601,6 +608,7 @@ errno_t resolv_ghosts_step(struct tevent_req *req) + + subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx, + state->ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, + state->domain->name, + state->ghosts[state->index]); + if (subreq == NULL) { +diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c +index cc78300f31e863fcb5366e18a14abc597c6f7ddb..436bb268fa9c78d72fb744e0d338aa561a7d8764 100644 +--- a/src/responder/ifp/ifp_users.c ++++ b/src/responder/ifp/ifp_users.c +@@ -99,7 +99,9 @@ int ifp_users_find_by_name(struct sbus_request *sbus_req, + } + + req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, +- ctx->rctx->ncache, 0, NULL, name); ++ ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, ++ NULL, name); + if (req == NULL) { + return ENOMEM; + } +@@ -253,7 +255,9 @@ int ifp_users_find_by_cert(struct sbus_request *sbus_req, void *data, + } + + req = cache_req_user_by_cert_send(sbus_req, ctx->rctx->ev, ctx->rctx, +- ctx->rctx->ncache, 0, NULL, derb64); ++ ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, NULL, ++ derb64); + if (req == NULL) { + return ENOMEM; + } +@@ -367,6 +371,7 @@ static int ifp_users_list_by_cert_step(struct ifp_list_ctx *list_ctx) + list_ctx->ctx->rctx, + list_ctx->ctx->rctx->ncache, + 0, ++ CACHE_REQ_POSIX_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -532,7 +537,9 @@ int ifp_users_find_by_name_and_cert(struct sbus_request *sbus_req, void *data, + + if (name_and_cert_ctx->name != NULL) { + req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, +- ctx->rctx->ncache, 0, NULL, ++ ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, ++ NULL, + name_and_cert_ctx->name); + if (req == NULL) { + return ENOMEM; +@@ -614,6 +621,7 @@ static int ifp_users_find_by_name_and_cert_step( + list_ctx->ctx->rctx, + list_ctx->ctx->rctx->ncache, + 0, ++ CACHE_REQ_POSIX_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -774,6 +782,7 @@ static int ifp_users_list_by_name_step(struct ifp_list_ctx *list_ctx) + req = cache_req_user_by_filter_send(list_ctx, + list_ctx->ctx->rctx->ev, + list_ctx->ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -858,6 +867,7 @@ int ifp_users_list_by_domain_and_name(struct sbus_request *sbus_req, + } + + req = cache_req_user_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + domain, filter); + if (req == NULL) { + return ENOMEM; +@@ -1102,7 +1112,8 @@ int ifp_users_user_update_groups_list(struct sbus_request *sbus_req, + } + + req = cache_req_initgr_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, +- ctx->rctx->ncache, 0, domain->name, ++ ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, domain->name, + username); + if (req == NULL) { + return ENOMEM; +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index 07edcddffa1091f8bbcf79a25962aadc791bb890..118b5083b14bf5692c6fdd7ba90668fe514aa89d 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -509,7 +509,8 @@ ifp_user_get_attr_lookup(struct tevent_req *subreq) + } + + subreq = cache_req_send(state, state->rctx->ev, state->rctx, +- state->ncache, 0, state->domname, data); ++ state->ncache, 0, CACHE_REQ_POSIX_DOM, ++ state->domname, data); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; +diff --git a/src/responder/nss/nss_enum.c b/src/responder/nss/nss_enum.c +index b1cce2cde43ece735285c132d0127c142ee83cb0..aa7d8428f37e943a6b5904495c40ad4b8011b767 100644 +--- a/src/responder/nss/nss_enum.c ++++ b/src/responder/nss/nss_enum.c +@@ -93,7 +93,7 @@ nss_setent_internal_send(TALLOC_CTX *mem_ctx, + /* Create new object. */ + state->enum_ctx->is_ready = false; + subreq = cache_req_send(req, ev, cli_ctx->rctx, cli_ctx->rctx->ncache, +- 0, NULL, data); ++ 0, CACHE_REQ_POSIX_DOM, NULL, data); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n"); + ret = ENOMEM; +diff --git a/src/responder/nss/nss_get_object.c b/src/responder/nss/nss_get_object.c +index f83dd393c0e333d8914802e005672a15a51d5939..9058793ea2d72b57003a7219414af6a0f0c5b89e 100644 +--- a/src/responder/nss/nss_get_object.c ++++ b/src/responder/nss/nss_get_object.c +@@ -190,7 +190,8 @@ nss_get_object_send(TALLOC_CTX *mem_ctx, + } + + subreq = cache_req_send(req, ev, cli_ctx->rctx, cli_ctx->rctx->ncache, +- state->nss_ctx->cache_refresh_percent, NULL, data); ++ state->nss_ctx->cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, NULL, data); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n"); + ret = ENOMEM; +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index ba2563c11885ff39681c2ef432acaedf26702b64..fa6d2cc10fe1404196f9d9221a469d7a9a768211 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1315,7 +1315,9 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) + + + req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx, +- pctx->rctx->ncache, 0, NULL, cert); ++ pctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, NULL, ++ cert); + if (req == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n"); + ret = ENOMEM; +@@ -1507,6 +1509,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + preq->cctx->rctx, + preq->cctx->rctx->ncache, + 0, ++ CACHE_REQ_POSIX_DOM, + preq->pd->domain, + data); + if (!dpreq) { +diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c +index 52dfd5c709e48f0d4610a4b384962fbc2869312b..cfdbfc9c9c66d96f774822d6a4d4aaaf1327abe3 100644 +--- a/src/responder/sudo/sudosrv_get_sudorules.c ++++ b/src/responder/sudo/sudosrv_get_sudorules.c +@@ -644,7 +644,8 @@ struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx, + DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username); + + subreq = cache_req_initgr_by_name_send(state, ev, sudo_ctx->rctx, +- sudo_ctx->rctx->ncache, 0, NULL, ++ sudo_ctx->rctx->ncache, 0, ++ CACHE_REQ_POSIX_DOM, NULL, + username); + if (subreq == NULL) { + ret = ENOMEM; +diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c +index 5f1e5350eaf1d85de376c94731f0bd678f884a1d..80086232fd437876c2b190fb972c2ee3194d9efd 100644 +--- a/src/tests/cmocka/test_responder_cache_req.c ++++ b/src/tests/cmocka/test_responder_cache_req.c +@@ -84,6 +84,28 @@ struct test_group { + talloc_free(req_mem_ctx); \ + } while (0) + ++#define run_cache_req_domtype(ctx, send_fn, done_fn, dom, crp, domtype, lookup, expret) do { \ ++ TALLOC_CTX *req_mem_ctx; \ ++ struct tevent_req *req; \ ++ errno_t ret; \ ++ \ ++ req_mem_ctx = talloc_new(global_talloc_context); \ ++ check_leaks_push(req_mem_ctx); \ ++ \ ++ req = send_fn(req_mem_ctx, ctx->tctx->ev, ctx->rctx, \ ++ ctx->ncache, crp, \ ++ domtype, \ ++ (dom == NULL ? NULL : dom->name), lookup); \ ++ assert_non_null(req); \ ++ tevent_req_set_callback(req, done_fn, ctx); \ ++ \ ++ ret = test_ev_loop(ctx->tctx); \ ++ assert_int_equal(ret, expret); \ ++ assert_true(check_leaks_pop(req_mem_ctx)); \ ++ \ ++ talloc_free(req_mem_ctx); \ ++} while (0) ++ + struct cache_req_test_ctx { + struct sss_test_ctx *tctx; + struct resp_ctx *rctx; +@@ -211,9 +233,11 @@ static void run_user_by_name(struct cache_req_test_ctx *test_ctx, + int cache_refresh_percent, + errno_t exp_ret) + { +- run_cache_req(test_ctx, cache_req_user_by_name_send, +- cache_req_user_by_name_test_done, domain, +- cache_refresh_percent, users[0].short_name, exp_ret); ++ run_cache_req_domtype(test_ctx, cache_req_user_by_name_send, ++ cache_req_user_by_name_test_done, domain, ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, ++ users[0].short_name, exp_ret); + } + + static void run_user_by_upn(struct cache_req_test_ctx *test_ctx, +@@ -221,9 +245,11 @@ static void run_user_by_upn(struct cache_req_test_ctx *test_ctx, + int cache_refresh_percent, + errno_t exp_ret) + { +- run_cache_req(test_ctx, cache_req_user_by_name_send, +- cache_req_user_by_name_test_done, domain, +- cache_refresh_percent, users[0].upn, exp_ret); ++ run_cache_req_domtype(test_ctx, cache_req_user_by_name_send, ++ cache_req_user_by_name_test_done, domain, ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, ++ users[0].upn, exp_ret); + } + + static void run_user_by_id(struct cache_req_test_ctx *test_ctx, +@@ -318,9 +344,11 @@ static void run_group_by_name(struct cache_req_test_ctx *test_ctx, + int cache_refresh_percent, + errno_t exp_ret) + { +- run_cache_req(test_ctx, cache_req_group_by_name_send, +- cache_req_group_by_name_test_done, domain, +- cache_refresh_percent, groups[0].short_name, exp_ret); ++ run_cache_req_domtype(test_ctx, cache_req_group_by_name_send, ++ cache_req_group_by_name_test_done, domain, ++ cache_refresh_percent, ++ CACHE_REQ_POSIX_DOM, ++ groups[0].short_name, exp_ret); + } + + static void run_group_by_id(struct cache_req_test_ctx *test_ctx, +@@ -605,7 +633,9 @@ void test_user_by_name_multiple_domains_parse(void **state) + check_leaks_push(req_mem_ctx); + + req = cache_req_user_by_name_send(req_mem_ctx, test_ctx->tctx->ev, +- test_ctx->rctx, test_ctx->ncache, 0, ++ test_ctx->rctx, test_ctx->ncache, ++ CACHE_REQ_POSIX_DOM, ++ 0, + NULL, input_fqn); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_name_test_done, test_ctx); +@@ -1119,7 +1149,8 @@ void test_group_by_name_multiple_domains_parse(void **state) + + req = cache_req_group_by_name_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, test_ctx->ncache, 0, +- NULL, input_fqn); ++ CACHE_REQ_POSIX_DOM, NULL, ++ input_fqn); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_name_test_done, test_ctx); + +@@ -1421,6 +1452,7 @@ void test_user_by_recent_filter_valid(void **state) + /* User TEST_USER is created with a DP callback. */ + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); +@@ -1463,6 +1495,7 @@ void test_users_by_recent_filter_valid(void **state) + /* User TEST_USER1 and TEST_USER2 are created with a DP callback. */ + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); +@@ -1524,6 +1557,7 @@ void test_users_by_filter_filter_old(void **state) + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); +@@ -1559,6 +1593,7 @@ void test_users_by_filter_notfound(void **state) + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + "nosuchuser*"); + assert_non_null(req); +@@ -1592,6 +1627,7 @@ static void test_users_by_filter_multiple_domains_notfound(void **state) + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + domain->name, + "nosuchuser*"); + assert_non_null(req); +@@ -1636,6 +1672,7 @@ void test_group_by_recent_filter_valid(void **state) + /* Group TEST_GROUP is created with a DP callback. */ + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); +@@ -1680,6 +1717,7 @@ void test_groups_by_recent_filter_valid(void **state) + /* Group TEST_GROUP1 and TEST_GROUP2 are created with a DP callback. */ + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); +@@ -1738,6 +1776,7 @@ void test_groups_by_filter_notfound(void **state) + + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + "nosuchgroup*"); + assert_non_null(req); +@@ -1770,6 +1809,7 @@ void test_groups_by_filter_multiple_domains_notfound(void **state) + + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, ++ CACHE_REQ_POSIX_DOM, + domain->name, + "nosuchgroup*"); + assert_non_null(req); +-- +2.9.3 + diff --git a/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch b/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch new file mode 100644 index 0000000..c956b23 --- /dev/null +++ b/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch @@ -0,0 +1,705 @@ +From bab9c21c9ec7ad39555db52511f0f2e425decd94 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 24 Mar 2017 12:44:09 +0100 +Subject: [PATCH 64/72] IFP: Search both POSIX and non-POSIX domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +Changes the behaviour of the InfoPipe responder so that both application +and POSIX domains are searched. In general, the IFP responder uses the +CACHE_REQ_ANY_DOM lookup type because we can't presume the intention of +the caller. Therefore, deployments that combine both POSIX and non-POSIX +domains must use fully qualified names or select the right domain order +manually. + +There is one change between the POSIX and non-POSIX users or groups - +the object path. For the POSIX users, the object path includes the UID +or GID. Because we don't have that for the non-POSIX objects, the object +name is used in the path instead. + +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/responder/ifp/ifp_groups.c | 135 ++++++++++++++++++++++------------- + src/responder/ifp/ifp_users.c | 158 ++++++++++++++++++++++++++--------------- + src/responder/ifp/ifpsrv_cmd.c | 6 +- + 3 files changed, 194 insertions(+), 105 deletions(-) + +diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c +index 99908e96bd971bce4b4e9064a77d8413f837d743..c568c62009cd4b777919dea048fd381a91bd3460 100644 +--- a/src/responder/ifp/ifp_groups.c ++++ b/src/responder/ifp/ifp_groups.c +@@ -35,25 +35,33 @@ char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_message *msg) + { +- const char *gid; ++ const char *key = NULL; + +- gid = ldb_msg_find_attr_as_string(msg, SYSDB_GIDNUM, NULL); ++ switch (domain->type) { ++ case DOM_TYPE_APPLICATION: ++ key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); ++ break; ++ case DOM_TYPE_POSIX: ++ key = ldb_msg_find_attr_as_string(msg, SYSDB_GIDNUM, NULL); ++ break; ++ } + +- if (gid == NULL) { ++ ++ if (key == NULL) { + return NULL; + } + +- return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, gid); ++ return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, key); + } + +-static errno_t ifp_groups_decompose_path(struct sss_domain_info *domains, ++static errno_t ifp_groups_decompose_path(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, + const char *path, + struct sss_domain_info **_domain, +- gid_t *_gid) ++ char **_key) + { + char **parts = NULL; + struct sss_domain_info *domain; +- gid_t gid; + errno_t ret; + + ret = sbus_opath_decompose_exact(NULL, path, IFP_PATH_GROUPS, 2, &parts); +@@ -67,14 +75,8 @@ static errno_t ifp_groups_decompose_path(struct sss_domain_info *domains, + goto done; + } + +- gid = strtouint32(parts[1], NULL, 10); +- ret = errno; +- if (ret != EOK) { +- goto done; +- } +- + *_domain = domain; +- *_gid = gid; ++ *_key = talloc_steal(mem_ctx, parts[1]); + + done: + talloc_free(parts); +@@ -119,7 +121,7 @@ int ifp_groups_find_by_name(struct sbus_request *sbus_req, + + req = cache_req_group_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, NULL, ++ CACHE_REQ_ANY_DOM, NULL, + name); + if (req == NULL) { + return ENOMEM; +@@ -273,7 +275,7 @@ static int ifp_groups_list_by_name_step(struct ifp_list_ctx *list_ctx) + req = cache_req_group_by_filter_send(list_ctx, + list_ctx->ctx->rctx->ev, + list_ctx->ctx->rctx, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -358,7 +360,7 @@ int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req, + } + + req = cache_req_group_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + domain, filter); + if (req == NULL) { + return ENOMEM; +@@ -412,16 +414,65 @@ done: + } + + static errno_t ++ifp_groups_get_from_cache(struct sbus_request *sbus_req, ++ struct sss_domain_info *domain, ++ const char *key, ++ struct ldb_message **_group) ++{ ++ struct ldb_result *group_res; ++ errno_t ret; ++ gid_t gid; ++ ++ switch (domain->type) { ++ case DOM_TYPE_POSIX: ++ gid = strtouint32(key, NULL, 10); ++ ret = errno; ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID value\n"); ++ return ret; ++ } ++ ++ ret = sysdb_getgrgid_with_views(sbus_req, domain, gid, &group_res); ++ if (ret == EOK && group_res->count == 0) { ++ *_group = NULL; ++ return ENOENT; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n", ++ gid, domain->name, ret, sss_strerror(ret)); ++ return ret; ++ } ++ break; ++ case DOM_TYPE_APPLICATION: ++ ret = sysdb_getgrnam_with_views(sbus_req, domain, key, &group_res); ++ if (ret == EOK && group_res->count == 0) { ++ *_group = NULL; ++ return ENOENT; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %s@%s [%d]: %s\n", ++ key, domain->name, ret, sss_strerror(ret)); ++ return ret; ++ } ++ break; ++ } ++ ++ if (group_res->count > 1) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "More groups matched by the single key\n"); ++ return EIO; ++ } ++ ++ *_group = group_res->msgs[0]; ++ return EOK; ++} ++ ++static errno_t + ifp_groups_group_get(struct sbus_request *sbus_req, + void *data, +- gid_t *_gid, + struct sss_domain_info **_domain, + struct ldb_message **_group) + { + struct ifp_ctx *ctx; + struct sss_domain_info *domain; +- struct ldb_result *res; +- uid_t gid; ++ char *key; + errno_t ret; + + ctx = talloc_get_type(data, struct ifp_ctx); +@@ -430,8 +481,9 @@ ifp_groups_group_get(struct sbus_request *sbus_req, + return ERR_INTERNAL; + } + +- ret = ifp_groups_decompose_path(ctx->rctx->domains, sbus_req->path, +- &domain, &gid); ++ ret = ifp_groups_decompose_path(sbus_req, ++ ctx->rctx->domains, sbus_req->path, ++ &domain, &key); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path" + "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret)); +@@ -439,28 +491,15 @@ ifp_groups_group_get(struct sbus_request *sbus_req, + } + + if (_group != NULL) { +- ret = sysdb_getgrgid_with_views(sbus_req, domain, gid, &res); +- if (ret == EOK && res->count == 0) { +- *_group = NULL; +- ret = ENOENT; +- } +- +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n", +- gid, domain->name, ret, sss_strerror(ret)); +- } else { +- *_group = res->msgs[0]; +- } ++ ret = ifp_groups_get_from_cache(sbus_req, domain, key, _group); + } + + if (ret == EOK || ret == ENOENT) { +- if (_gid != NULL) { +- *_gid = gid; +- } +- + if (_domain != NULL) { + *_domain = domain; + } ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve group from cache\n"); + } + + return ret; +@@ -513,7 +552,7 @@ static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx, + state->ctx = ctx; + state->data = data; + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &group); + if (ret != EOK) { + goto immediately; + } +@@ -527,7 +566,7 @@ static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx, + + subreq = cache_req_group_by_name_send(state, ev, ctx->rctx, + ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + domain->name, + name); + if (subreq == NULL) { +@@ -561,7 +600,7 @@ static void resolv_ghosts_group_done(struct tevent_req *subreq) + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct resolv_ghosts_state); + +- ret = ifp_groups_group_get(state->sbus_req, state->data, NULL, ++ ret = ifp_groups_group_get(state->sbus_req, state->data, + &state->domain, &group); + if (ret != EOK) { + goto done; +@@ -608,7 +647,7 @@ errno_t resolv_ghosts_step(struct tevent_req *req) + + subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx, + state->ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + state->domain->name, + state->ghosts[state->index]); + if (subreq == NULL) { +@@ -719,7 +758,7 @@ void ifp_groups_group_get_name(struct sbus_request *sbus_req, + return; + } + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &msg); + if (ret != EOK) { + *_out = NULL; + return; +@@ -744,7 +783,7 @@ void ifp_groups_group_get_gid_number(struct sbus_request *sbus_req, + struct sss_domain_info *domain; + errno_t ret; + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &msg); + if (ret != EOK) { + *_out = 0; + return; +@@ -763,7 +802,7 @@ void ifp_groups_group_get_unique_id(struct sbus_request *sbus_req, + struct sss_domain_info *domain; + errno_t ret; + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &msg); + if (ret != EOK) { + *_out = 0; + return; +@@ -803,7 +842,7 @@ ifp_groups_group_get_members(TALLOC_CTX *mem_ctx, + return ENOMEM; + } + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &group); + if (ret != EOK) { + goto done; + } +@@ -954,7 +993,7 @@ int ifp_cache_object_store_group(struct sbus_request *sbus_req, + struct ldb_message *group; + errno_t ret; + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &group); + if (ret != EOK) { + error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch " + "group [%d]: %s\n", ret, sss_strerror(ret)); +@@ -973,7 +1012,7 @@ int ifp_cache_object_remove_group(struct sbus_request *sbus_req, + struct ldb_message *group; + errno_t ret; + +- ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group); ++ ret = ifp_groups_group_get(sbus_req, data, &domain, &group); + if (ret != EOK) { + error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch " + "group [%d]: %s\n", ret, sss_strerror(ret)); +diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c +index 436bb268fa9c78d72fb744e0d338aa561a7d8764..ce9557f94351b730ee46f3cbce31613cb5901942 100644 +--- a/src/responder/ifp/ifp_users.c ++++ b/src/responder/ifp/ifp_users.c +@@ -37,25 +37,33 @@ char * ifp_users_build_path_from_msg(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_message *msg) + { +- const char *uid; ++ const char *key = NULL; + +- uid = ldb_msg_find_attr_as_string(msg, SYSDB_UIDNUM, NULL); ++ switch (domain->type) { ++ case DOM_TYPE_APPLICATION: ++ key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); ++ break; ++ case DOM_TYPE_POSIX: ++ key = ldb_msg_find_attr_as_string(msg, SYSDB_UIDNUM, NULL); ++ break; ++ } + +- if (uid == NULL) { ++ ++ if (key == NULL) { + return NULL; + } + +- return sbus_opath_compose(mem_ctx, IFP_PATH_USERS, domain->name, uid); ++ return sbus_opath_compose(mem_ctx, IFP_PATH_USERS, domain->name, key); + } + +-static errno_t ifp_users_decompose_path(struct sss_domain_info *domains, ++static errno_t ifp_users_decompose_path(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domains, + const char *path, + struct sss_domain_info **_domain, +- uid_t *_uid) ++ char **_key) + { + char **parts = NULL; + struct sss_domain_info *domain; +- uid_t uid; + errno_t ret; + + ret = sbus_opath_decompose_exact(NULL, path, IFP_PATH_USERS, 2, &parts); +@@ -69,14 +77,8 @@ static errno_t ifp_users_decompose_path(struct sss_domain_info *domains, + goto done; + } + +- uid = strtouint32(parts[1], NULL, 10); +- ret = errno; +- if (ret != EOK) { +- goto done; +- } +- + *_domain = domain; +- *_uid = uid; ++ *_key = talloc_steal(mem_ctx, parts[1]); + + done: + talloc_free(parts); +@@ -100,7 +102,7 @@ int ifp_users_find_by_name(struct sbus_request *sbus_req, + + req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + NULL, name); + if (req == NULL) { + return ENOMEM; +@@ -256,7 +258,7 @@ int ifp_users_find_by_cert(struct sbus_request *sbus_req, void *data, + + req = cache_req_user_by_cert_send(sbus_req, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, NULL, ++ CACHE_REQ_ANY_DOM, NULL, + derb64); + if (req == NULL) { + return ENOMEM; +@@ -371,7 +373,7 @@ static int ifp_users_list_by_cert_step(struct ifp_list_ctx *list_ctx) + list_ctx->ctx->rctx, + list_ctx->ctx->rctx->ncache, + 0, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -538,7 +540,7 @@ int ifp_users_find_by_name_and_cert(struct sbus_request *sbus_req, void *data, + if (name_and_cert_ctx->name != NULL) { + req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + NULL, + name_and_cert_ctx->name); + if (req == NULL) { +@@ -621,7 +623,7 @@ static int ifp_users_find_by_name_and_cert_step( + list_ctx->ctx->rctx, + list_ctx->ctx->rctx->ncache, + 0, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -782,7 +784,7 @@ static int ifp_users_list_by_name_step(struct ifp_list_ctx *list_ctx) + req = cache_req_user_by_filter_send(list_ctx, + list_ctx->ctx->rctx->ev, + list_ctx->ctx->rctx, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + list_ctx->dom->name, + list_ctx->filter); + if (req == NULL) { +@@ -867,7 +869,7 @@ int ifp_users_list_by_domain_and_name(struct sbus_request *sbus_req, + } + + req = cache_req_user_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx, +- CACHE_REQ_POSIX_DOM, ++ CACHE_REQ_ANY_DOM, + domain, filter); + if (req == NULL) { + return ENOMEM; +@@ -930,19 +932,69 @@ done: + } + + static errno_t ++ifp_users_get_from_cache(struct sbus_request *sbus_req, ++ struct sss_domain_info *domain, ++ const char *key, ++ struct ldb_message **_user) ++{ ++ struct ldb_result *user_res; ++ errno_t ret; ++ uid_t uid; ++ ++ switch (domain->type) { ++ case DOM_TYPE_POSIX: ++ uid = strtouint32(key, NULL, 10); ++ ret = errno; ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID value\n"); ++ return ret; ++ } ++ ++ ret = sysdb_getpwuid_with_views(sbus_req, domain, uid, &user_res); ++ if (ret == EOK && user_res->count == 0) { ++ *_user = NULL; ++ return ENOENT; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %u@%s [%d]: %s\n", ++ uid, domain->name, ret, sss_strerror(ret)); ++ return ret; ++ } ++ break; ++ case DOM_TYPE_APPLICATION: ++ ret = sysdb_getpwnam_with_views(sbus_req, domain, key, &user_res); ++ if (ret == EOK && user_res->count == 0) { ++ *_user = NULL; ++ return ENOENT; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %s@%s [%d]: %s\n", ++ key, domain->name, ret, sss_strerror(ret)); ++ return ret; ++ } ++ break; ++ } ++ ++ if (user_res->count > 1) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "More users matched by the single key\n"); ++ return EIO; ++ } ++ ++ *_user = user_res->msgs[0]; ++ return EOK; ++} ++ ++static errno_t + ifp_users_user_get(struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, +- uid_t *_uid, + struct sss_domain_info **_domain, + struct ldb_message **_user) + { + struct sss_domain_info *domain; +- struct ldb_result *res; +- uid_t uid; ++ char *key; + errno_t ret; + +- ret = ifp_users_decompose_path(ifp_ctx->rctx->domains, sbus_req->path, +- &domain, &uid); ++ ret = ifp_users_decompose_path(sbus_req, ++ ifp_ctx->rctx->domains, sbus_req->path, ++ &domain, &key); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path" + "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret)); +@@ -950,28 +1002,15 @@ ifp_users_user_get(struct sbus_request *sbus_req, + } + + if (_user != NULL) { +- ret = sysdb_getpwuid_with_views(sbus_req, domain, uid, &res); +- if (ret == EOK && res->count == 0) { +- *_user = NULL; +- ret = ENOENT; +- } +- +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %u@%s [%d]: %s\n", +- uid, domain->name, ret, sss_strerror(ret)); +- } else { +- *_user = res->msgs[0]; +- } ++ ret = ifp_users_get_from_cache(sbus_req, domain, key, _user); + } + + if (ret == EOK || ret == ENOENT) { +- if (_uid != NULL) { +- *_uid = uid; +- } +- + if (_domain != NULL) { + *_domain = domain; + } ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve user from cache\n"); + } + + return ret; +@@ -1000,7 +1039,7 @@ static void ifp_users_get_as_string(struct sbus_request *sbus_req, + return; + } + +- ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &msg); ++ ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &msg); + if (ret != EOK) { + return; + } +@@ -1034,7 +1073,7 @@ static void ifp_users_get_name(struct sbus_request *sbus_req, + return; + } + +- ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &msg); ++ ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &msg); + if (ret != EOK) { + return; + } +@@ -1072,7 +1111,7 @@ static void ifp_users_get_as_uint32(struct sbus_request *sbus_req, + return; + } + +- ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &msg); ++ ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &msg); + if (ret != EOK) { + return; + } +@@ -1100,7 +1139,7 @@ int ifp_users_user_update_groups_list(struct sbus_request *sbus_req, + return ERR_INTERNAL; + } + +- ret = ifp_users_user_get(sbus_req, data, NULL, &domain, &user); ++ ret = ifp_users_user_get(sbus_req, data, &domain, &user); + if (ret != EOK) { + return ret; + } +@@ -1113,7 +1152,7 @@ int ifp_users_user_update_groups_list(struct sbus_request *sbus_req, + + req = cache_req_initgr_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, domain->name, ++ CACHE_REQ_ANY_DOM, domain->name, + username); + if (req == NULL) { + return ENOMEM; +@@ -1235,7 +1274,7 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req, + return; + } + +- ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &user); ++ ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &user); + if (ret != EOK) { + return; + } +@@ -1268,7 +1307,7 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req, + for (i = 0; i < res->count; i++) { + gid = sss_view_ldb_msg_find_attr_as_uint64(domain, res->msgs[i], + SYSDB_GIDNUM, 0); +- if (gid == 0) { ++ if (gid == 0 && domain->type == DOM_TYPE_POSIX) { + continue; + } + +@@ -1293,11 +1332,12 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req, + { + struct ifp_ctx *ifp_ctx; + struct sss_domain_info *domain; ++ struct ldb_message *base_user; ++ const char *name; + struct ldb_message **user; + struct ldb_message_element *el; + struct ldb_dn *basedn; + size_t count; +- uid_t uid; + const char *filter; + const char **extra; + hash_table_t *table; +@@ -1322,7 +1362,7 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req, + return; + } + +- ret = ifp_users_user_get(sbus_req, data, &uid, &domain, NULL); ++ ret = ifp_users_user_get(sbus_req, data, &domain, &base_user); + if (ret != EOK) { + return; + } +@@ -1333,9 +1373,15 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req, + return; + } + +- filter = talloc_asprintf(sbus_req, "(&(%s=%s)(%s=%u))", ++ name = ldb_msg_find_attr_as_string(base_user, SYSDB_NAME, NULL); ++ if (name == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name\n"); ++ return; ++ } ++ ++ filter = talloc_asprintf(sbus_req, "(&(%s=%s)(%s=%s))", + SYSDB_OBJECTCLASS, SYSDB_USER_CLASS, +- SYSDB_UIDNUM, uid); ++ SYSDB_NAME, name); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); + return; +@@ -1351,7 +1397,7 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req, + } + + if (count == 0) { +- DEBUG(SSSDBG_TRACE_FUNC, "User %u not found!\n", uid); ++ DEBUG(SSSDBG_TRACE_FUNC, "User %s not found!\n", name); + return; + } else if (count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one entry found!\n"); +@@ -1421,7 +1467,7 @@ int ifp_cache_object_store_user(struct sbus_request *sbus_req, + struct ldb_message *user; + errno_t ret; + +- ret = ifp_users_user_get(sbus_req, data, NULL, &domain, &user); ++ ret = ifp_users_user_get(sbus_req, data, &domain, &user); + if (ret != EOK) { + error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch " + "user [%d]: %s\n", ret, sss_strerror(ret)); +@@ -1440,7 +1486,7 @@ int ifp_cache_object_remove_user(struct sbus_request *sbus_req, + struct ldb_message *user; + errno_t ret; + +- ret = ifp_users_user_get(sbus_req, data, NULL, &domain, &user); ++ ret = ifp_users_user_get(sbus_req, data, &domain, &user); + if (ret != EOK) { + error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch " + "user [%d]: %s\n", ret, sss_strerror(ret)); +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index 118b5083b14bf5692c6fdd7ba90668fe514aa89d..d10f35e41dbb1623a0b9de37a4c43363cbefc1a3 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -508,8 +508,12 @@ ifp_user_get_attr_lookup(struct tevent_req *subreq) + return; + } + ++ /* IFP serves both POSIX and application domains. Requests that need ++ * to differentiate between the two must be qualified ++ */ + subreq = cache_req_send(state, state->rctx->ev, state->rctx, +- state->ncache, 0, CACHE_REQ_POSIX_DOM, ++ state->ncache, 0, ++ CACHE_REQ_ANY_DOM, + state->domname, data); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); +-- +2.9.3 + diff --git a/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch b/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch new file mode 100644 index 0000000..f062ffa --- /dev/null +++ b/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch @@ -0,0 +1,57 @@ +From c9268488cd24fe8e13580d6c4ea2fa237faededa Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 28 Mar 2017 14:07:29 +0200 +Subject: [PATCH 65/72] IFP: ListByName: Don't crash when no results are found +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If no results were found using the List command, the results variable +was undefined which resulted in a crash. + +Instead, only copy the results of the cache_req lookup returns EOK and +we can presume that the results are valid. + +Reviewed-by: Pavel Březina +Reviewed-by: Sumit Bose +--- + src/responder/ifp/ifp_users.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c +index ce9557f94351b730ee46f3cbce31613cb5901942..188194f2ab356d0e67b0f26b003f3a9ce48e6acd 100644 +--- a/src/responder/ifp/ifp_users.c ++++ b/src/responder/ifp/ifp_users.c +@@ -801,7 +801,7 @@ static void ifp_users_list_by_name_done(struct tevent_req *req) + DBusError *error; + struct ifp_list_ctx *list_ctx; + struct sbus_request *sbus_req; +- struct cache_req_result *result; ++ struct cache_req_result *result = NULL; + errno_t ret; + + list_ctx = tevent_req_callback_data(req, struct ifp_list_ctx); +@@ -816,12 +816,14 @@ static void ifp_users_list_by_name_done(struct tevent_req *req) + return; + } + +- ret = ifp_users_list_copy(list_ctx, result->ldb_result); +- if (ret != EOK) { +- error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL, +- "Failed to copy domain result"); +- sbus_request_fail_and_finish(sbus_req, error); +- return; ++ if (ret == EOK) { ++ ret = ifp_users_list_copy(list_ctx, result->ldb_result); ++ if (ret != EOK) { ++ error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL, ++ "Failed to copy domain result"); ++ sbus_request_fail_and_finish(sbus_req, error); ++ return; ++ } + } + + list_ctx->dom = get_next_domain(list_ctx->dom, SSS_GND_DESCEND); +-- +2.9.3 + diff --git a/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch b/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch new file mode 100644 index 0000000..64ba6af --- /dev/null +++ b/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch @@ -0,0 +1,58 @@ +From d11e7faa2a3464ed921ccf88a02e0a48871484b4 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 24 Mar 2017 20:36:06 +0100 +Subject: [PATCH 66/72] PAM: Remove unneeded memory context + +Since we only store data into pam_ctx in get_public_domains(), it +doesn't make sense to allow passing a separate memory context. It is +always going to be pam_ctx, otherwise the memory hierarchy will cause +issues anyway. + +Reviewed-by: Sumit Bose +--- + src/responder/pam/pamsrv.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c +index 816f2293130ff8761ca94b4a42ca93063c11ea35..ab3f4545520f3fcb2492a6089a039c46f0fb847f 100644 +--- a/src/responder/pam/pamsrv.c ++++ b/src/responder/pam/pamsrv.c +@@ -122,7 +122,7 @@ done: + return ret; + } + +-static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx) ++static errno_t get_public_domains(struct pam_ctx *pctx) + { + char *domains_str = NULL; + errno_t ret; +@@ -137,7 +137,7 @@ static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx) + + if (strcmp(domains_str, ALL_DOMAIMS_ARE_PUBLIC) == 0) { /* all */ + /* copy all domains */ +- ret = get_dom_names(mem_ctx, ++ ret = get_dom_names(pctx, + pctx->rctx->domains, + &pctx->public_domains, + &pctx->public_domains_count); +@@ -149,7 +149,7 @@ static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx) + pctx->public_domains = NULL; + pctx->public_domains_count = 0; + } else { +- ret = split_on_separator(mem_ctx, domains_str, ',', true, false, ++ ret = split_on_separator(pctx, domains_str, ',', true, false, + &pctx->public_domains, + &pctx->public_domains_count); + if (ret != EOK) { +@@ -212,7 +212,7 @@ static int pam_process_init(TALLOC_CTX *mem_ctx, + goto done; + } + +- ret = get_public_domains(pctx, pctx); ++ ret = get_public_domains(pctx); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "get_public_domains failed: %d:[%s].\n", + ret, sss_strerror(ret)); +-- +2.9.3 + diff --git a/SOURCES/0067-PAM-Add-application-services.patch b/SOURCES/0067-PAM-Add-application-services.patch new file mode 100644 index 0000000..2c8fddd --- /dev/null +++ b/SOURCES/0067-PAM-Add-application-services.patch @@ -0,0 +1,452 @@ +From 855201c70f69f2b1dbcb3faef780fbdb84354f18 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Sun, 26 Mar 2017 18:28:41 +0200 +Subject: [PATCH 67/72] PAM: Add application services + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +Adds a new PAM responder option 'pam_app_services'. This option can hold +a list of PAM services that are allowed to contact the application +non-POSIX domains. These services are NOT allowed to contact any of the +POSIX domains. + +Reviewed-by: Sumit Bose +--- + src/confdb/confdb.h | 1 + + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.conf | 1 + + src/man/sssd.conf.5.xml | 12 +++ + src/responder/pam/pamsrv.c | 33 +++++++ + src/responder/pam/pamsrv.h | 5 ++ + src/responder/pam/pamsrv_cmd.c | 26 +++++- + src/tests/cmocka/test_pam_srv.c | 167 ++++++++++++++++++++++++++++++++++- + 9 files changed, 241 insertions(+), 6 deletions(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 5a8d377c312f641f544b1c7cf38826192462ea3c..8719c239362b371fcdb1b78956bcddde871f141b 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -129,6 +129,7 @@ + #define CONFDB_PAM_CERT_AUTH "pam_cert_auth" + #define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path" + #define CONFDB_PAM_P11_CHILD_TIMEOUT "p11_child_timeout" ++#define CONFDB_PAM_APP_SERVICES "pam_app_services" + + /* SUDO */ + #define CONFDB_SUDO_CONF_ENTRY "config/sudo" +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index 806611b6076048c08ce08c772dbd3cea5fdd656c..211338778e81c1c60ffb3cdbc67c9619343d7798 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -102,6 +102,7 @@ option_strings = { + 'pam_cert_auth' : _('Allow certificate based/Smartcard authentication.'), + 'pam_cert_db_path' : _('Path to certificate databse with PKCS#11 modules.'), + 'p11_child_timeout' : _('How many seconds will pam_sss wait for p11_child to finish'), ++ 'pam_app_services' : _('Which PAM services are permitted to contact application domains'), + + # [sudo] + 'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'), +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 8fd2d2c5236246394353a88c50d1510bd6233f77..1a749db754cedd87f263f7ae596d6f8238bb4357 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -119,6 +119,7 @@ option = pam_account_locked_message + option = pam_cert_auth + option = pam_cert_db_path + option = p11_child_timeout ++option = pam_app_services + + [rule/allowed_sudo_options] + validator = ini_allowed_options +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index a38b24208f89e4502e41625c540ea9958d5bbffe..a1a0c2992925a4c7df86832117eec2a0cf7894c9 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -73,6 +73,7 @@ pam_account_locked_message = str, None, false + pam_cert_auth = bool, None, false + pam_cert_db_path = str, None, false + p11_child_timeout = int, None, false ++pam_app_services = str, None, false + + [sudo] + # sudo service +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 8294793c765bfa6bf481693c7d7f206950454681..c4e30396f16c40db37af2f56ac218b6e37201ef7 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -1325,6 +1325,18 @@ pam_account_locked_message = Account locked, please contact help desk. + + + ++ ++ pam_app_services (string) ++ ++ ++ Which PAM services are permitted to contact ++ domains of type application ++ ++ ++ Default: Not set ++ ++ ++ + + + +diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c +index ab3f4545520f3fcb2492a6089a039c46f0fb847f..79470823d18138da6ef9235e6336a3220ead1797 100644 +--- a/src/responder/pam/pamsrv.c ++++ b/src/responder/pam/pamsrv.c +@@ -166,6 +166,32 @@ done: + return ret; + } + ++static errno_t get_app_services(struct pam_ctx *pctx) ++{ ++ errno_t ret; ++ ++ ret = confdb_get_string_as_list(pctx->rctx->cdb, pctx, ++ CONFDB_PAM_CONF_ENTRY, ++ CONFDB_PAM_APP_SERVICES, ++ &pctx->app_services); ++ if (ret == ENOENT) { ++ pctx->app_services = talloc_zero_array(pctx, char *, 1); ++ if (pctx->app_services == NULL) { ++ return ENOMEM; ++ } ++ /* Allocating an empty array makes it easier for the consumer ++ * to iterate over it ++ */ ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot read "CONFDB_PAM_APP_SERVICES" [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ return EOK; ++} ++ + static int pam_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb, +@@ -219,6 +245,13 @@ static int pam_process_init(TALLOC_CTX *mem_ctx, + goto done; + } + ++ ret = get_app_services(pctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "get_app_services failed: %d:[%s].\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ + /* Enable automatic reconnection to the Data Provider */ + + /* FIXME: "retries" is too generic, either get it from a global config +diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h +index b3eb56441048ecdba82866a95f1d6d6d5e786c60..b569748fe2a2005cee5df34bef55e803175492a9 100644 +--- a/src/responder/pam/pamsrv.h ++++ b/src/responder/pam/pamsrv.h +@@ -26,6 +26,7 @@ + #include "util/util.h" + #include "sbus/sssd_dbus.h" + #include "responder/common/responder.h" ++#include "responder/common/cache_req/cache_req.h" + + struct pam_auth_req; + +@@ -42,6 +43,9 @@ struct pam_ctx { + char **public_domains; + int public_domains_count; + ++ /* What services are permitted to access application domains */ ++ char **app_services; ++ + bool cert_auth; + int p11_child_debug_fd; + char *nss_db; +@@ -54,6 +58,7 @@ struct pam_auth_dp_req { + struct pam_auth_req { + struct cli_ctx *cctx; + struct sss_domain_info *domain; ++ enum cache_req_dom_type req_dom_type; + + struct pam_data *pd; + +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index fa6d2cc10fe1404196f9d9221a469d7a9a768211..f2b3c74b483e527932dda42279d14a9ac184b475 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1161,6 +1161,25 @@ static bool is_domain_public(char *name, + return false; + } + ++static enum cache_req_dom_type ++get_domain_request_type(struct pam_auth_req *preq, ++ struct pam_ctx *pctx) ++{ ++ enum cache_req_dom_type req_dom_type; ++ ++ /* By default, only POSIX domains are to be contacted */ ++ req_dom_type = CACHE_REQ_POSIX_DOM; ++ ++ for (int i = 0; pctx->app_services[i]; i++) { ++ if (strcmp(pctx->app_services[i], preq->pd->service) == 0) { ++ req_dom_type = CACHE_REQ_APPLICATION_DOM; ++ break; ++ } ++ } ++ ++ return req_dom_type; ++} ++ + static errno_t check_cert(TALLOC_CTX *mctx, + struct tevent_context *ev, + struct pam_ctx *pctx, +@@ -1257,6 +1276,9 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) + goto done; + } + ++ /* Determine what domain type to contact */ ++ preq->req_dom_type = get_domain_request_type(preq, pctx); ++ + /* try backend first for authentication before doing local Smartcard + * authentication */ + if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) { +@@ -1316,7 +1338,7 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) + + req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx, + pctx->rctx->ncache, 0, +- CACHE_REQ_POSIX_DOM, NULL, ++ preq->req_dom_type, NULL, + cert); + if (req == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n"); +@@ -1509,7 +1531,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + preq->cctx->rctx, + preq->cctx->rctx->ncache, + 0, +- CACHE_REQ_POSIX_DOM, ++ preq->req_dom_type, + preq->pd->domain, + data); + if (!dpreq) { +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index 847419658bb983e6548722d6fa6fb22c63ee86b8..d249b8f1ea48f1c17b461c3add9e8c63774e5f88 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -186,6 +186,15 @@ struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx) + ret = sss_hash_create(pctx, 10, &pctx->id_table); + assert_int_equal(ret, EOK); + ++ /* Two NULLs so that tests can just assign a const to the first slot ++ * should they need it. The code iterates until first NULL anyway ++ */ ++ pctx->app_services = talloc_zero_array(pctx, char *, 2); ++ if (pctx->app_services == NULL) { ++ talloc_free(pctx); ++ return NULL; ++ } ++ + return pctx; + } + +@@ -495,8 +504,12 @@ int __wrap_pam_dp_send_req(struct pam_auth_req *preq, int timeout) + return EOK; + } + +-static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name, +- const char *pwd, const char *fa2) ++static void mock_input_pam_ex(TALLOC_CTX *mem_ctx, ++ const char *name, ++ const char *pwd, ++ const char *fa2, ++ const char *svc, ++ bool contact_dp) + { + size_t buf_size; + uint8_t *m_buf; +@@ -536,7 +549,10 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name, + } + } + +- pi.pam_service = "pam_test_service"; ++ if (svc == NULL) { ++ svc = "pam_test_service"; ++ } ++ pi.pam_service = svc; + pi.pam_service_size = strlen(pi.pam_service) + 1; + pi.pam_tty = "/dev/tty"; + pi.pam_tty_size = strlen(pi.pam_tty) + 1; +@@ -559,7 +575,17 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name, + will_return(__wrap_sss_packet_get_body, buf_size); + + mock_parse_inp(name, NULL, EOK); +- mock_account_recv_simple(); ++ if (contact_dp) { ++ mock_account_recv_simple(); ++ } ++} ++ ++static void mock_input_pam(TALLOC_CTX *mem_ctx, ++ const char *name, ++ const char *pwd, ++ const char *fa2) ++{ ++ return mock_input_pam_ex(mem_ctx, name, pwd, fa2, NULL, true); + } + + static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name, +@@ -2097,6 +2123,127 @@ void test_filter_response(void **state) + talloc_free(pd); + } + ++static int pam_test_setup_appsvc_posix_dom(void **state) ++{ ++ int ret; ++ ++ ret = pam_test_setup(state); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ /* This config option is only read on startup, which is not executed ++ * in test, so we can't just pass in a param ++ */ ++ pam_test_ctx->pctx->app_services[0] = discard_const("app_svc"); ++ return 0; ++} ++ ++void test_appsvc_posix_dom(void **state) ++{ ++ int ret; ++ ++ /* The domain is POSIX, the request will skip over it */ ++ mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", false); ++ pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN; ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_user_unknown_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_not_appsvc_posix_dom(void **state) ++{ ++ int ret; ++ ++ /* A different service than the app one can authenticate against a POSIX domain */ ++ mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", true); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++static int pam_test_setup_appsvc_app_dom(void **state) ++{ ++ struct sss_test_conf_param dom_params[] = { ++ { "domain_type", "application" }, ++ { NULL, NULL }, /* Sentinel */ ++ }; ++ struct sss_test_conf_param pam_params[] = { ++ { NULL, NULL }, /* Sentinel */ ++ }; ++ struct sss_test_conf_param monitor_params[] = { ++ { NULL, NULL }, /* Sentinel */ ++ }; ++ ++ ++ test_pam_setup(dom_params, pam_params, monitor_params, state); ++ pam_test_setup_common(); ++ ++ /* This config option is only read on startup, which is not executed ++ * in test, so we can't just pass in a param ++ */ ++ pam_test_ctx->pctx->app_services[0] = discard_const("app_svc"); ++ return 0; ++} ++ ++void test_appsvc_app_dom(void **state) ++{ ++ int ret; ++ ++ /* The domain is POSIX, the request will skip over it */ ++ mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", true); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_not_appsvc_app_dom(void **state) ++{ ++ int ret; ++ ++ /* A different service than the app one can authenticate against a POSIX domain */ ++ mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", false); ++ ++ pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN; ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_user_unknown_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ + int main(int argc, const char *argv[]) + { + int rv; +@@ -2216,6 +2363,18 @@ int main(int argc, const char *argv[]) + + cmocka_unit_test_setup_teardown(test_filter_response, + pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_appsvc_posix_dom, ++ pam_test_setup_appsvc_posix_dom, ++ pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_not_appsvc_posix_dom, ++ pam_test_setup_appsvc_posix_dom, ++ pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_appsvc_app_dom, ++ pam_test_setup_appsvc_app_dom, ++ pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_not_appsvc_app_dom, ++ pam_test_setup_appsvc_posix_dom, ++ pam_test_teardown), + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ +-- +2.9.3 + diff --git a/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch b/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch new file mode 100644 index 0000000..8ed534b --- /dev/null +++ b/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch @@ -0,0 +1,152 @@ +From ea8a4436b66877bbae1a73d11917ecdb3bf72718 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 22 Mar 2017 13:00:31 +0100 +Subject: [PATCH 68/72] SYSDB: Allow storing non-POSIX users + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +We already do the same for groups. If the user does not have UID number +set but does have the POSIX: false attribute set, then we save the user +with zero UID and the non-POSIX flag. + +Reviewed-by: Sumit Bose +--- + src/db/sysdb_ops.c | 32 ++++++++++++++++++++-------- + src/tests/sysdb-tests.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 79 insertions(+), 9 deletions(-) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 919f22370ff87eff2bf0bb569ca90f1ee699a61e..3cf9d903f25b9ccd506d7957c94040bdc7d658a3 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -1855,6 +1855,7 @@ int sysdb_add_user(struct sss_domain_info *domain, + struct sysdb_attrs *id_attrs; + uint32_t id; + int ret; ++ bool posix; + + if (domain->mpg) { + if (gid != 0) { +@@ -1926,7 +1927,28 @@ int sysdb_add_user(struct sss_domain_info *domain, + /* Not fatal */ + } + +- if (uid == 0) { ++ if (!attrs) { ++ attrs = sysdb_new_attrs(tmp_ctx); ++ if (!attrs) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix); ++ if (ret == ENOENT) { ++ posix = true; ++ ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, true); ++ if (ret) { ++ DEBUG(SSSDBG_TRACE_LIBS, "Failed to add posix attribute.\n"); ++ goto done; ++ } ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_TRACE_LIBS, "Failed to get posix attribute.\n"); ++ goto done; ++ } ++ ++ if (uid == 0 && posix == true) { + ret = sysdb_get_new_id(domain, &id); + if (ret) goto done; + +@@ -1948,14 +1970,6 @@ int sysdb_add_user(struct sss_domain_info *domain, + if (ret) goto done; + } + +- if (!attrs) { +- attrs = sysdb_new_attrs(tmp_ctx); +- if (!attrs) { +- ret = ENOMEM; +- goto done; +- } +- } +- + if (!now) { + now = time(NULL); + } +diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c +index 1767dc3c734c6b2e5f74564debd603e2442f491b..6ec82ce4ca5c4f918bc9f3144c21f33b270ea47e 100644 +--- a/src/tests/sysdb-tests.c ++++ b/src/tests/sysdb-tests.c +@@ -1428,6 +1428,59 @@ START_TEST (test_sysdb_get_user_attr_subdomain) + } + END_TEST + ++START_TEST (test_sysdb_add_nonposix_user) ++{ ++ struct sysdb_test_ctx *test_ctx; ++ const char *get_attrs[] = { SYSDB_GIDNUM, ++ SYSDB_UIDNUM, ++ SYSDB_POSIX, ++ NULL }; ++ struct ldb_result *res; ++ const char *attrval; ++ const char *username = "test_sysdb_add_nonposix_user"; ++ const char *fq_name; ++ struct sysdb_attrs *user_attrs; ++ int ret; ++ uint64_t id; ++ ++ /* Setup */ ++ ret = setup_sysdb_tests(&test_ctx); ++ fail_if(ret != EOK, "Could not set up the test"); ++ ++ /* Create user */ ++ fq_name = sss_create_internal_fqname(test_ctx, username, test_ctx->domain->name); ++ fail_if(fq_name == NULL, "Failed to create fq name."); ++ ++ user_attrs = sysdb_new_attrs(test_ctx); ++ fail_if(user_attrs == NULL); ++ ++ ret = sysdb_attrs_add_bool(user_attrs, SYSDB_POSIX, false); ++ fail_if(ret != EOK, "Could not add attribute"); ++ ++ ret = sysdb_add_user(test_ctx->domain, fq_name, 0, 0, "Gecos", ++ "/home/userhome", "/bin/bash", NULL, user_attrs, 0, 0); ++ fail_if(ret != EOK, "sysdb_add_user failed."); ++ ++ /* Test */ ++ ret = sysdb_get_user_attr(test_ctx, test_ctx->domain, fq_name, ++ get_attrs, &res); ++ fail_if(ret != EOK, "Could not get user attributes."); ++ fail_if(res->count != 1, "Invalid number of entries, expected 1, got %d", ++ res->count); ++ ++ attrval = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_POSIX, NULL); ++ fail_if(strcasecmp(attrval, "false") != 0, "Got bad attribute value."); ++ ++ id = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 123); ++ fail_unless(id == 0, "Wrong UID value"); ++ ++ id = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 123); ++ fail_unless(id == 0, "Wrong GID value"); ++ ++ talloc_free(test_ctx); ++} ++END_TEST ++ + START_TEST (test_sysdb_add_group_member) + { + struct sysdb_test_ctx *test_ctx; +@@ -7044,6 +7097,9 @@ Suite *create_sysdb_suite(void) + /* Test GetUserAttr with subdomain user */ + tcase_add_test(tc_sysdb, test_sysdb_get_user_attr_subdomain); + ++ /* Test adding a non-POSIX user */ ++ tcase_add_test(tc_sysdb, test_sysdb_add_nonposix_user); ++ + /* ===== NETGROUP TESTS ===== */ + + /* Create a new netgroup */ +-- +2.9.3 + diff --git a/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch b/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch new file mode 100644 index 0000000..c6a76f3 --- /dev/null +++ b/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch @@ -0,0 +1,36 @@ +From ee344275c041f68e943360c975e3356ba251cef8 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 28 Mar 2017 14:49:31 +0200 +Subject: [PATCH 69/72] SYSDB: Only generate new UID in local domain + +To avoid issues where a user with no UID but without the posix=false +flag was passed to sysdb, we only allow generating the new ID in the +local domain. This might prevent bugs where non-POSIX users would get a +UID created by sysdb which might allow accessing resources owned by that +UID. + +Reviewed-by: Sumit Bose +--- + src/db/sysdb_ops.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 3cf9d903f25b9ccd506d7957c94040bdc7d658a3..4d7b2abd8026c90aaf4e7be687102e459cf3690e 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -1422,6 +1422,12 @@ int sysdb_get_new_id(struct sss_domain_info *domain, + return ENOMEM; + } + ++ if (strcasecmp(domain->provider, "local") != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Generating new ID is only supported in the local domain!\n"); ++ return ENOTSUP; ++ } ++ + base_dn = sysdb_domain_dn(tmp_ctx, domain); + if (!base_dn) { + talloc_zfree(tmp_ctx); +-- +2.9.3 + diff --git a/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch b/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch new file mode 100644 index 0000000..d70a026 --- /dev/null +++ b/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch @@ -0,0 +1,141 @@ +From 3abbd7569f96a980676e0323d95301c50acdf062 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 22 Mar 2017 13:06:08 +0100 +Subject: [PATCH 70/72] LDAP: save non-POSIX users in application domains + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +If a user being saved by the LDAP provider does not have a UID or GID +and the domain type is application, we save the user entry as non-POSIX. + +Reviewed-by: Sumit Bose +--- + src/providers/ldap/sdap_async_users.c | 72 +++++++++++++++++++++++++++-------- + 1 file changed, 57 insertions(+), 15 deletions(-) + +diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c +index 3d957ab584865f74499bc732395388a78965fe5f..265cd7e4f7929c295d5bdcfbd781221b74601f13 100644 +--- a/src/providers/ldap/sdap_async_users.c ++++ b/src/providers/ldap/sdap_async_users.c +@@ -112,6 +112,28 @@ done: + return ret; + } + ++static errno_t sdap_set_non_posix_flag(struct sysdb_attrs *attrs, ++ const char *pkey) ++{ ++ errno_t ret; ++ ++ ret = sysdb_attrs_add_uint32(attrs, pkey, 0); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to add a zero ID to a non-posix object!\n"); ++ return ret; ++ } ++ ++ ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, false); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Error: Failed to mark objects as non-posix!\n"); ++ return ret; ++ } ++ ++ return EOK; ++} ++ + /* FIXME: support storing additional attributes */ + int sdap_save_user(TALLOC_CTX *memctx, + struct sdap_options *opts, +@@ -130,8 +152,8 @@ int sdap_save_user(TALLOC_CTX *memctx, + const char *homedir; + const char *shell; + const char *orig_dn = NULL; +- uid_t uid; +- gid_t gid; ++ uid_t uid = 0; ++ gid_t gid = 0; + struct sysdb_attrs *user_attrs; + char *upn = NULL; + size_t i; +@@ -146,6 +168,7 @@ int sdap_save_user(TALLOC_CTX *memctx, + size_t c; + char *p1; + char *p2; ++ bool is_posix = true; + + DEBUG(SSSDBG_TRACE_FUNC, "Save user\n"); + +@@ -295,19 +318,29 @@ int sdap_save_user(TALLOC_CTX *memctx, + ret = sysdb_attrs_get_uint32_t(attrs, + opts->user_map[SDAP_AT_USER_UID].sys_name, + &uid); +- if (ret != EOK) { ++ if (ret == ENOENT && dom->type == DOM_TYPE_APPLICATION) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Marking object as non-posix and setting ID=0!\n"); ++ ret = sdap_set_non_posix_flag(user_attrs, ++ opts->user_map[SDAP_AT_USER_UID].sys_name); ++ if (ret != EOK) { ++ goto done; ++ } ++ is_posix = false; ++ } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "no uid provided for [%s] in domain [%s].\n", ++ "Cannot retrieve UID for [%s] in domain [%s].\n", + user_name, dom->name); +- ret = EINVAL; ++ ret = ERR_NO_POSIX; + goto done; + } + } +- /* check that the uid is valid for this domain */ +- if (OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) { +- DEBUG(SSSDBG_OP_FAILURE, +- "User [%s] filtered out! (uid out of range)\n", +- user_name); ++ ++ /* check that the uid is valid for this domain if the user is a POSIX one */ ++ if (is_posix == true && OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "User [%s] filtered out! (uid out of range)\n", ++ user_name); + ret = EINVAL; + goto done; + } +@@ -349,17 +382,26 @@ int sdap_save_user(TALLOC_CTX *memctx, + ret = sysdb_attrs_get_uint32_t(attrs, + opts->user_map[SDAP_AT_USER_GID].sys_name, + &gid); +- if (ret != EOK) { ++ if (ret == ENOENT && dom->type == DOM_TYPE_APPLICATION) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Marking object as non-posix and setting ID=0!\n"); ++ ret = sdap_set_non_posix_flag(attrs, ++ opts->user_map[SDAP_AT_USER_GID].sys_name); ++ if (ret != EOK) { ++ goto done; ++ } ++ is_posix = false; ++ } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "no gid provided for [%s] in domain [%s].\n", +- user_name, dom->name); +- ret = EINVAL; ++ "Cannot retrieve GID for [%s] in domain [%s].\n", ++ user_name, dom->name); ++ ret = ERR_NO_POSIX; + goto done; + } + } + + /* check that the gid is valid for this domain */ +- if (IS_SUBDOMAIN(dom) == false && ++ if (is_posix == true && IS_SUBDOMAIN(dom) == false && + OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) { + DEBUG(SSSDBG_CRIT_FAILURE, + "User [%s] filtered out! (primary gid out of range)\n", +-- +2.9.3 + diff --git a/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch b/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch new file mode 100644 index 0000000..73f2862 --- /dev/null +++ b/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch @@ -0,0 +1,254 @@ +From b2a823cf415a12416dca9ff019666906d61cfc2f Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 22 Mar 2017 13:06:14 +0100 +Subject: [PATCH 71/72] LDAP: Relax search filters in application domains + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +If a request comes towards an application domain, we can drop the part +of the filter that asserts that the object has a valid UID/GID. Instead, +we just search by name. + +Reviewed-by: Sumit Bose +--- + src/providers/ldap/ldap_id.c | 35 ++++++++++++++++++++++++---- + src/providers/ldap/sdap_async_enum.c | 7 +++++- + src/providers/ldap/sdap_async_initgroups.c | 37 ++++++++++++++++++++++++------ + 3 files changed, 66 insertions(+), 13 deletions(-) + +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index 0bee0ca8d71abece6749fdb8393b9ceacb64417d..7400dc1f57e30cc6ae5f939ffa628a1e9dd47e06 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -56,6 +56,7 @@ struct users_get_state { + char *filter; + const char **attrs; + bool use_id_mapping; ++ bool non_posix; + + int dp_error; + int sdap_ret; +@@ -114,6 +115,10 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + state->filter_value = filter_value; + state->filter_type = filter_type; + ++ if (state->domain->type == DOM_TYPE_APPLICATION) { ++ state->non_posix = true; ++ } ++ + state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping( + ctx->opts->idmap_ctx, + sdom->dom->name, +@@ -292,7 +297,13 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + } + } + +- if (state->use_id_mapping || filter_type == BE_FILTER_SECID) { ++ if (state->non_posix) { ++ state->filter = talloc_asprintf(state, ++ "(&%s(objectclass=%s)(%s=*))", ++ user_filter, ++ ctx->opts->user_map[SDAP_OC_USER].name, ++ ctx->opts->user_map[SDAP_AT_USER_NAME].name); ++ } else if (state->use_id_mapping || filter_type == BE_FILTER_SECID) { + /* When mapping IDs or looking for SIDs, we don't want to limit + * ourselves to users with a UID value. But there must be a SID to map + * from. +@@ -304,7 +315,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + ctx->opts->user_map[SDAP_AT_USER_NAME].name, + ctx->opts->user_map[SDAP_AT_USER_OBJECTSID].name); + } else { +- /* When not ID-mapping, make sure there is a non-NULL UID */ ++ /* When not ID-mapping or looking up POSIX users, ++ * make sure there is a non-NULL UID */ + state->filter = talloc_asprintf(state, + "(&%s(objectclass=%s)(%s=*)(&(%s=*)(!(%s=0))))", + user_filter, +@@ -380,6 +392,7 @@ static void users_get_connect_done(struct tevent_req *subreq) + * have no idea about POSIX attributes support, run a one-time check + */ + if (state->use_id_mapping == false && ++ state->non_posix == false && + state->ctx->opts->schema_type == SDAP_SCHEMA_AD && + state->ctx->srv_opts && + state->ctx->srv_opts->posix_checked == false) { +@@ -650,6 +663,7 @@ struct groups_get_state { + char *filter; + const char **attrs; + bool use_id_mapping; ++ bool non_posix; + + int dp_error; + int sdap_ret; +@@ -709,6 +723,10 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, + state->filter_value = filter_value; + state->filter_type = filter_type; + ++ if (state->domain->type == DOM_TYPE_APPLICATION) { ++ state->non_posix = true; ++ } ++ + state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping( + ctx->opts->idmap_ctx, + sdom->dom->name, +@@ -827,9 +845,11 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, + goto done; + } + +- if (state->use_id_mapping || filter_type == BE_FILTER_SECID) { +- /* When mapping IDs or looking for SIDs, we don't want to limit +- * ourselves to groups with a GID value ++ if (state->non_posix ++ || state->use_id_mapping ++ || filter_type == BE_FILTER_SECID) { ++ /* When mapping IDs or looking for SIDs, or when in a non-POSIX domain, ++ * we don't want to limit ourselves to groups with a GID value + */ + + state->filter = talloc_asprintf(state, +@@ -1123,6 +1143,7 @@ struct groups_by_user_state { + int filter_type; + const char *extra_value; + const char **attrs; ++ bool non_posix; + + int dp_error; + int sdap_ret; +@@ -1204,6 +1225,10 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, + state->domain = sdom->dom; + state->sysdb = sdom->dom->sysdb; + ++ if (state->domain->type == DOM_TYPE_APPLICATION) { ++ state->non_posix = true; ++ } ++ + ret = build_attrs_from_map(state, ctx->opts->group_map, SDAP_OPTS_GROUP, + NULL, &state->attrs, NULL); + if (ret != EOK) goto fail; +diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c +index 3f65059e18d5c8b548da0babec867d27c3a64198..91e481c4e694126900c729e86d187fba355de0b8 100644 +--- a/src/providers/ldap/sdap_async_enum.c ++++ b/src/providers/ldap/sdap_async_enum.c +@@ -717,6 +717,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, + struct enum_groups_state *state; + int ret; + bool use_mapping; ++ bool non_posix = false; + char *oc_list; + + req = tevent_req_create(memctx, &state, struct enum_groups_state); +@@ -727,6 +728,10 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, + state->ctx = ctx; + state->op = op; + ++ if (sdom->dom->type == DOM_TYPE_APPLICATION) { ++ non_posix = true; ++ } ++ + use_mapping = sdap_idmap_domain_has_algorithmic_mapping( + ctx->opts->idmap_ctx, + sdom->dom->name, +@@ -749,7 +754,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, + goto fail; + } + +- if (use_mapping) { ++ if (!non_posix && use_mapping) { + /* If we're ID-mapping, check for the objectSID as well */ + state->filter = talloc_asprintf_append_buffer( + state->filter, "(%s=*)", +diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c +index 79af7a3eda3fe8533933535c98c2b4b4698dfda2..c926ddcbefe471daa80505e139c3f19efa33b9ba 100644 +--- a/src/providers/ldap/sdap_async_initgroups.c ++++ b/src/providers/ldap/sdap_async_initgroups.c +@@ -376,7 +376,7 @@ struct sdap_initgr_rfc2307_state { + struct sdap_handle *sh; + const char **attrs; + const char *name; +- const char *base_filter; ++ char *base_filter; + const char *orig_dn; + char *filter; + int timeout; +@@ -473,18 +473,32 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx, + } + + state->base_filter = talloc_asprintf(state, +- "(&(%s=%s)(%s)(%s=*)(&(%s=*)(!(%s=0))))", ++ "(&(%s=%s)(%s)(%s=*)", + opts->group_map[SDAP_AT_GROUP_MEMBER].name, + clean_name, oc_list, +- opts->group_map[SDAP_AT_GROUP_NAME].name, +- opts->group_map[SDAP_AT_GROUP_GID].name, +- opts->group_map[SDAP_AT_GROUP_GID].name); ++ opts->group_map[SDAP_AT_GROUP_NAME].name); + if (!state->base_filter) { + talloc_zfree(req); + return NULL; + } + talloc_zfree(clean_name); + ++ switch (domain->type) { ++ case DOM_TYPE_APPLICATION: ++ state->base_filter = talloc_asprintf_append(state->base_filter, ")"); ++ break; ++ case DOM_TYPE_POSIX: ++ state->base_filter = talloc_asprintf_append(state->base_filter, ++ "(&(%s=*)(!(%s=0))))", ++ opts->group_map[SDAP_AT_GROUP_GID].name, ++ opts->group_map[SDAP_AT_GROUP_GID].name); ++ break; ++ } ++ if (!state->base_filter) { ++ ret = ENOMEM; ++ goto done; ++ } ++ + ret = sdap_initgr_rfc2307_next_base(req); + + done: +@@ -2666,6 +2680,7 @@ struct sdap_get_initgr_state { + char *shortname; + char *filter; + int timeout; ++ bool non_posix; + + struct sysdb_attrs *orig_user; + +@@ -2724,6 +2739,10 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, + goto done; + } + ++ if (state->dom->type == DOM_TYPE_APPLICATION) { ++ state->non_posix = true; ++ } ++ + use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping( + id_ctx->opts->idmap_ctx, + sdom->dom->name, +@@ -2813,7 +2832,10 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, + } + } + +- if (use_id_mapping) { ++ if (state->non_posix) { ++ state->user_base_filter = talloc_asprintf_append(state->user_base_filter, ++ ")"); ++ } else if (use_id_mapping) { + /* When mapping IDs or looking for SIDs, we don't want to limit + * ourselves to users with a UID value. But there must be a SID to map + * from. +@@ -2822,7 +2844,8 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, + "(%s=*))", + id_ctx->opts->user_map[SDAP_AT_USER_OBJECTSID].name); + } else { +- /* When not ID-mapping, make sure there is a non-NULL UID */ ++ /* When not ID-mapping or looking up app users, make sure there ++ * is a non-NULL UID */ + state->user_base_filter = talloc_asprintf_append(state->user_base_filter, + "(&(%s=*)(!(%s=0))))", + id_ctx->opts->user_map[SDAP_AT_USER_UID].name, +-- +2.9.3 + diff --git a/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch b/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch new file mode 100644 index 0000000..05693f1 --- /dev/null +++ b/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch @@ -0,0 +1,353 @@ +From 3f32e79858f268ce6501de44e5158e8c12f688dd Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 22 Mar 2017 13:01:18 +0100 +Subject: [PATCH 72/72] KRB5: Authenticate users in a non-POSIX domain using a + MEMORY ccache + +Related to: +https://pagure.io/SSSD/sssd/issue/3310 + +The following changes were done to the Kerberos authentication code +in order to support authentication in a non-POSIX environment: + - delayed authentication is disabled in non-POSIX domains + - when a user logs in in a non-POSIX domain, SSSD uses a + MEMORY:$username ccache and destroys is then krb5_child finishes + so that just the numeric result is used + - krb5_child doesn't drop privileges in this configuration because + there is nothing to drop privileges to + +Reviewed-by: Sumit Bose +--- + src/providers/krb5/krb5_auth.c | 62 ++++++++++++++++------ + src/providers/krb5/krb5_auth.h | 2 + + src/providers/krb5/krb5_child.c | 32 +++++++++-- + src/providers/krb5/krb5_child_handler.c | 15 +++++- + .../krb5/krb5_delayed_online_authentication.c | 7 +++ + src/providers/krb5/krb5_init.c | 3 ++ + 6 files changed, 99 insertions(+), 22 deletions(-) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index c2d6d7eeacc1f766024c4d629f25fd0f0be24e5e..2faf18d17a735476c20f9cc27b15be4a39cadc5c 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -42,6 +42,8 @@ + #include "providers/krb5/krb5_utils.h" + #include "providers/krb5/krb5_ccache.h" + ++#define NON_POSIX_CCNAME_FMT "MEMORY:sssd_nonposix_dummy_%u" ++ + static int krb5_mod_ccname(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, +@@ -200,6 +202,7 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, + talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup); + + kr->pd = pd; ++ kr->dom = dom; + kr->krb5_ctx = krb5_ctx; + + ret = get_krb_primary(krb5_ctx->name_to_primary, +@@ -275,8 +278,11 @@ static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx, + return; + } + +- ret = add_user_to_delayed_online_authentication(krb5_ctx, pd, uid); +- if (ret != EOK) { ++ ret = add_user_to_delayed_online_authentication(krb5_ctx, domain, pd, uid); ++ if (ret == ENOTSUP) { ++ /* This error is not fatal */ ++ DEBUG(SSSDBG_MINOR_FAILURE, "Delayed authentication not supported\n"); ++ } else if (ret != EOK) { + /* This error is not fatal */ + DEBUG(SSSDBG_CRIT_FAILURE, + "add_user_to_delayed_online_authentication failed.\n"); +@@ -291,21 +297,43 @@ static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr, + { + const char *ccname_template; + +- ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL); ++ switch (kr->dom->type) { ++ case DOM_TYPE_POSIX: ++ 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; +- } ++ 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; ++ } + +- 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); ++ 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); ++ } ++ break; ++ case DOM_TYPE_APPLICATION: ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Domain type application, will use in-memory ccache\n"); ++ /* We don't care about using cryptographic randomness, just ++ * a non-predictable ccname, so using rand() here is fine ++ */ ++ kr->ccname = talloc_asprintf(kr, ++ NON_POSIX_CCNAME_FMT, ++ rand() % UINT_MAX); ++ if (kr->ccname == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); ++ return ENOMEM; ++ } ++ ++ break; ++ default: ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unsupported domain type\n"); ++ return EINVAL; + } + + return EOK; +@@ -617,7 +645,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, + kr->uid = sss_view_ldb_msg_find_attr_as_uint64(state->domain, + res->msgs[0], + SYSDB_UIDNUM, 0); +- if (kr->uid == 0) { ++ if (kr->uid == 0 && state->domain->type == DOM_TYPE_POSIX) { + DEBUG(SSSDBG_CONF_SETTINGS, + "UID for user [%s] not known.\n", pd->user); + ret = ENOENT; +@@ -627,7 +655,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, + kr->gid = sss_view_ldb_msg_find_attr_as_uint64(state->domain, + res->msgs[0], + SYSDB_GIDNUM, 0); +- if (kr->gid == 0) { ++ if (kr->gid == 0 && state->domain->type == DOM_TYPE_POSIX) { + DEBUG(SSSDBG_CONF_SETTINGS, + "GID for user [%s] not known.\n", pd->user); + ret = ENOENT; +diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h +index 75ad916e79b29043120543ab3c4c1bd27e09d913..8ad3aeff21e58f9055ae144eaa450992c6391ba6 100644 +--- a/src/providers/krb5/krb5_auth.h ++++ b/src/providers/krb5/krb5_auth.h +@@ -50,6 +50,7 @@ + struct krb5child_req { + struct pam_data *pd; + struct krb5_ctx *krb5_ctx; ++ struct sss_domain_info *dom; + + const char *ccname; + const char *old_ccname; +@@ -118,6 +119,7 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len, + struct krb5_child_response **_res); + + errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx, ++ struct sss_domain_info *domain, + struct pam_data *pd, + uid_t uid); + errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx, +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index a4128dda6b0861a95dba223047d66c4158b1afb6..cbbc892bee0365892ac66d3654c974d325166b60 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -80,6 +80,7 @@ struct krb5_req { + char *ccname; + char *keytab; + bool validate; ++ bool posix_domain; + bool send_pac; + bool use_enterprise_princ; + char *fast_ccname; +@@ -102,6 +103,16 @@ struct krb5_req { + static krb5_context krb5_error_ctx; + #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error) + ++static errno_t k5c_become_user(uid_t uid, gid_t gid, bool is_posix) ++{ ++ if (is_posix == false) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Will not drop privileges for a non-POSIX user\n"); ++ return EOK; ++ } ++ return become_user(uid, gid); ++} ++ + static krb5_error_code set_lifetime_options(struct cli_opts *cli_opts, + krb5_get_init_creds_opt *options) + { +@@ -1561,6 +1572,15 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + DEBUG(SSSDBG_CONF_SETTINGS, "TGT validation is disabled.\n"); + } + ++ /* In a non-POSIX environment, we only care about the return code from ++ * krb5_child, so let's not even attempt to create the ccache ++ */ ++ if (kr->posix_domain == false) { ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Finished authentication in a non-POSIX domain\n"); ++ goto done; ++ } ++ + /* If kr->ccname is cache collection (DIR:/...), we want to work + * directly with file ccache (DIR::/...), but cache collection + * should be returned back to back end. +@@ -2146,6 +2166,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, + size_t p = 0; + uint32_t len; + uint32_t validate; ++ uint32_t posix_domain; + uint32_t send_pac; + uint32_t use_enterprise_princ; + struct pam_data *pd; +@@ -2167,6 +2188,8 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, + SAFEALIGN_COPY_UINT32_CHECK(&kr->gid, buf + p, size, &p); + SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p); + kr->validate = (validate == 0) ? false : true; ++ SAFEALIGN_COPY_UINT32_CHECK(&posix_domain, buf + p, size, &p); ++ kr->posix_domain = (posix_domain == 0) ? false : true; + SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p); + SAFEALIGN_COPY_UINT32_CHECK(&send_pac, buf + p, size, &p); + kr->send_pac = (send_pac == 0) ? false : true; +@@ -2331,6 +2354,7 @@ static krb5_error_code check_fast_ccache(TALLOC_CTX *mem_ctx, + krb5_context ctx, + uid_t fast_uid, + gid_t fast_gid, ++ bool posix_domain, + struct cli_opts *cli_opts, + const char *primary, + const char *realm, +@@ -2420,7 +2444,7 @@ static krb5_error_code check_fast_ccache(TALLOC_CTX *mem_ctx, + /* Try to carry on */ + } + +- kerr = become_user(fast_uid, fast_gid); ++ kerr = k5c_become_user(fast_uid, fast_gid, posix_domain); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed: %d\n", kerr); + exit(1); +@@ -2572,7 +2596,7 @@ static int k5c_setup_fast(struct krb5_req *kr, bool demand) + } + + kerr = check_fast_ccache(kr, kr->ctx, kr->fast_uid, kr->fast_gid, +- kr->cli_opts, ++ kr->posix_domain, kr->cli_opts, + fast_principal, fast_principal_realm, + kr->keytab, &kr->fast_ccname); + if (kerr != 0) { +@@ -2773,7 +2797,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline) + * 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); ++ kerr = k5c_become_user(kr->uid, kr->gid, kr->posix_domain); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); + return kerr; +@@ -3075,7 +3099,7 @@ int main(int argc, const char *argv[]) + if ((sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN + && sss_authtok_get_type(kr->pd->authtok) + != SSS_AUTHTOK_TYPE_SC_KEYPAD)) { +- kerr = become_user(kr->uid, kr->gid); ++ kerr = k5c_become_user(kr->uid, kr->gid, kr->posix_domain); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); + ret = EFAULT; +diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c +index 680e67b089fcb32280352af24aae35af133a52f3..87e79a06e917aadb622455bccfc2e9c6769f70c2 100644 +--- a/src/providers/krb5/krb5_child_handler.c ++++ b/src/providers/krb5/krb5_child_handler.c +@@ -107,6 +107,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + uint32_t validate; + uint32_t send_pac; + uint32_t use_enterprise_principal; ++ uint32_t posix_domain; + size_t username_len = 0; + errno_t ret; + +@@ -131,6 +132,17 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + break; + } + ++ switch (kr->dom->type) { ++ case DOM_TYPE_POSIX: ++ posix_domain = 1; ++ break; ++ case DOM_TYPE_APPLICATION: ++ posix_domain = 0; ++ break; ++ default: ++ return EINVAL; ++ } ++ + if (kr->pd->cmd == SSS_CMD_RENEW || kr->is_offline) { + use_enterprise_principal = false; + } else { +@@ -151,7 +163,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + kr->pd->cmd == SSS_CMD_RENEW || + kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || + kr->pd->cmd == SSS_PAM_CHAUTHTOK) { +- buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) + ++ buf->size += 5*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) + + sss_authtok_get_size(kr->pd->authtok); + + buf->size += sizeof(uint32_t); +@@ -182,6 +194,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->uid, &rp); + SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->gid, &rp); + SAFEALIGN_COPY_UINT32(&buf->data[rp], &validate, &rp); ++ SAFEALIGN_COPY_UINT32(&buf->data[rp], &posix_domain, &rp); + SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->is_offline, &rp); + SAFEALIGN_COPY_UINT32(&buf->data[rp], &send_pac, &rp); + SAFEALIGN_COPY_UINT32(&buf->data[rp], &use_enterprise_principal, &rp); +diff --git a/src/providers/krb5/krb5_delayed_online_authentication.c b/src/providers/krb5/krb5_delayed_online_authentication.c +index bf2ef775573ba6bad79a99ad43b5d9748516e794..1cb7eade0e4cb9afbc4d031a07b3519ba08456d6 100644 +--- a/src/providers/krb5/krb5_delayed_online_authentication.c ++++ b/src/providers/krb5/krb5_delayed_online_authentication.c +@@ -234,6 +234,7 @@ static void delayed_online_authentication_callback(void *private_data) + } + + errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx, ++ struct sss_domain_info *domain, + struct pam_data *pd, + uid_t uid) + { +@@ -242,6 +243,12 @@ errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx, + hash_value_t value; + struct pam_data *new_pd; + ++ if (domain->type != DOM_TYPE_POSIX) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Domain type does not support delayed authentication\n"); ++ return ENOTSUP; ++ } ++ + if (krb5_ctx->deferred_auth_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Missing context for delayed online authentication.\n"); +diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c +index 12c8dfcc49af75de619ec0858aaff81504698273..66ae68fb4773af3987f2062246bc6493107c74d5 100644 +--- a/src/providers/krb5/krb5_init.c ++++ b/src/providers/krb5/krb5_init.c +@@ -136,6 +136,9 @@ errno_t sssm_krb5_init(TALLOC_CTX *mem_ctx, + return ENOMEM; + } + ++ /* Only needed to generate random ccache names for non-POSIX domains */ ++ srand(time(NULL) * getpid()); ++ + ret = sss_krb5_get_options(ctx, be_ctx->cdb, be_ctx->conf_path, &ctx->opts); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get krb5 options [%d]: %s\n", +-- +2.9.3 + diff --git a/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch b/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch new file mode 100644 index 0000000..0530278 --- /dev/null +++ b/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch @@ -0,0 +1,211 @@ +From 088be07a9e5aae54379a7f98e9e4615cd4451501 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 29 Mar 2017 22:49:09 +0200 +Subject: [PATCH 73/90] KCM: Fix off-by-one error in secrets key parsing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When parsing the secrets key, the code tried to protect against malformed keys +or keys that are too short, but it did an error - the UUID stringified +form is 36 bytes long, so the UUID_STR_SIZE is 37 because UUID_STR_SIZE +accounts for the null terminator. + +But the code, that was trying to assert that there are two characters after +the UUID string (separator and at least a single character for the name) +didn't take the NULL terminator (which strlen() doesn't return) into +account and ended up rejecting all ccaches whose name is only a single +character. + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 7d73049884e3a96ca3b00b5bd4104f4edd6287ab) +--- + src/responder/kcm/kcmsrv_ccache_json.c | 43 +++++++++------- + src/tests/cmocka/test_kcm_json_marshalling.c | 75 ++++++++++++++++++++++++++++ + 2 files changed, 101 insertions(+), 17 deletions(-) + +diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c +index 40b64861c209206d6f60ccd0843857edee24a844..8199bc613e4204859438e1cd820f3f4b2123dd7e 100644 +--- a/src/responder/kcm/kcmsrv_ccache_json.c ++++ b/src/responder/kcm/kcmsrv_ccache_json.c +@@ -109,6 +109,28 @@ static const char *sec_key_create(TALLOC_CTX *mem_ctx, + "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name); + } + ++static bool sec_key_valid(const char *sec_key) ++{ ++ if (sec_key == NULL) { ++ return false; ++ } ++ ++ if (strlen(sec_key) < UUID_STR_SIZE + 1) { ++ /* One char for separator (at UUID_STR_SIZE, because strlen doesn't ++ * include the '\0', but UUID_STR_SIZE does) and at least one for ++ * the name */ ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); ++ return false; ++ } ++ ++ if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n"); ++ return false; ++ } ++ ++ return true; ++} ++ + static errno_t sec_key_parse(TALLOC_CTX *mem_ctx, + const char *sec_key, + const char **_name, +@@ -116,9 +138,7 @@ static errno_t sec_key_parse(TALLOC_CTX *mem_ctx, + { + char uuid_str[UUID_STR_SIZE]; + +- if (strlen(sec_key) < UUID_STR_SIZE + 2) { +- /* One char for separator and at least one for the name */ +- DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); ++ if (!sec_key_valid(sec_key)) { + return EINVAL; + } + +@@ -143,14 +163,7 @@ errno_t sec_key_get_uuid(const char *sec_key, + { + char uuid_str[UUID_STR_SIZE]; + +- if (strlen(sec_key) < UUID_STR_SIZE + 2) { +- /* One char for separator and at least one for the name */ +- DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); +- return EINVAL; +- } +- +- if (sec_key[UUID_STR_SIZE-1] != SEC_KEY_SEPARATOR) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n"); ++ if (!sec_key_valid(sec_key)) { + return EINVAL; + } + +@@ -162,9 +175,7 @@ errno_t sec_key_get_uuid(const char *sec_key, + + const char *sec_key_get_name(const char *sec_key) + { +- if (strlen(sec_key) < UUID_STR_SIZE + 2) { +- /* One char for separator and at least one for the name */ +- DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key); ++ if (!sec_key_valid(sec_key)) { + return NULL; + } + +@@ -174,9 +185,7 @@ const char *sec_key_get_name(const char *sec_key) + bool sec_key_match_name(const char *sec_key, + const char *name) + { +- if (strlen(sec_key) < UUID_STR_SIZE + 2) { +- /* One char for separator and at least one for the name */ +- DEBUG(SSSDBG_MINOR_FAILURE, "Key %s is too short\n", sec_key); ++ if (!sec_key_valid(sec_key) || name == NULL) { + return false; + } + +diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c +index 8eff2f501066c70a8730cd3d4dc41b92d7a03e4c..108eaf55628029a6de8c23cd6486bdccc42c0364 100644 +--- a/src/tests/cmocka/test_kcm_json_marshalling.c ++++ b/src/tests/cmocka/test_kcm_json_marshalling.c +@@ -32,6 +32,12 @@ + + #define TEST_CREDS "TESTCREDS" + ++#define TEST_UUID_STR "5f8f296b-02be-4e86-9235-500e82354186" ++#define TEST_SEC_KEY_ONEDIGIT TEST_UUID_STR"-0" ++#define TEST_SEC_KEY_MULTIDIGITS TEST_UUID_STR"-123456" ++ ++#define TEST_SEC_KEY_NOSEP TEST_UUID_STR"+0" ++ + const struct kcm_ccdb_ops ccdb_mem_ops; + const struct kcm_ccdb_ops ccdb_sec_ops; + +@@ -188,6 +194,72 @@ static void test_kcm_ccache_marshall_unmarshall(void **state) + assert_int_equal(ret, EOK); + + assert_cc_equal(cc, cc2); ++ ++ /* This key is exactly one byte shorter than it should be */ ++ ret = sec_kv_to_ccache(test_ctx, ++ TEST_UUID_STR"-", ++ (const char *) data, ++ &owner, ++ &cc2); ++ assert_int_equal(ret, EINVAL); ++} ++ ++void test_sec_key_get_uuid(void **state) ++{ ++ errno_t ret; ++ uuid_t uuid; ++ char str_uuid[UUID_STR_SIZE]; ++ ++ uuid_clear(uuid); ++ ret = sec_key_get_uuid(TEST_SEC_KEY_ONEDIGIT, uuid); ++ assert_int_equal(ret, EOK); ++ uuid_unparse(uuid, str_uuid); ++ assert_string_equal(TEST_UUID_STR, str_uuid); ++ ++ ret = sec_key_get_uuid(TEST_SEC_KEY_NOSEP, uuid); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sec_key_get_uuid(TEST_UUID_STR, uuid); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sec_key_get_uuid(NULL, uuid); ++ assert_int_equal(ret, EINVAL); ++} ++ ++void test_sec_key_get_name(void **state) ++{ ++ const char *name; ++ ++ name = sec_key_get_name(TEST_SEC_KEY_ONEDIGIT); ++ assert_non_null(name); ++ assert_string_equal(name, "0"); ++ ++ name = sec_key_get_name(TEST_SEC_KEY_MULTIDIGITS); ++ assert_non_null(name); ++ assert_string_equal(name, "123456"); ++ ++ name = sec_key_get_name(TEST_UUID_STR); ++ assert_null(name); ++ ++ name = sec_key_get_name(TEST_SEC_KEY_NOSEP); ++ assert_null(name); ++ ++ name = sec_key_get_name(NULL); ++ assert_null(name); ++} ++ ++void test_sec_key_match_name(void **state) ++{ ++ assert_true(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "0")); ++ assert_true(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "123456")); ++ ++ assert_false(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "0")); ++ assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "123456")); ++ ++ assert_false(sec_key_match_name(TEST_UUID_STR, "0")); ++ assert_false(sec_key_match_name(TEST_SEC_KEY_NOSEP, "0")); ++ assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, NULL)); ++ assert_false(sec_key_match_name(NULL, "0")); + } + + int main(int argc, const char *argv[]) +@@ -205,6 +277,9 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall, + setup_kcm_marshalling, + teardown_kcm_marshalling), ++ cmocka_unit_test(test_sec_key_get_uuid), ++ cmocka_unit_test(test_sec_key_get_name), ++ cmocka_unit_test(test_sec_key_match_name), + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ +-- +2.9.3 + diff --git a/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch b/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch new file mode 100644 index 0000000..6351c10 --- /dev/null +++ b/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch @@ -0,0 +1,1478 @@ +From 2ca9a394063baac075e05b14fcc6c027027ab8f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 24 Feb 2017 10:40:43 +0100 +Subject: [PATCH 74/90] tcurl: add support for ssl and raw output + +At first, this patch separates curl_easy handle from the multi-handle +processing and makes it encapsulated in custom tcurl_request structure. +This allows us to separate protocol initialization from its asynchonous +logic which gives us the ability to set different options for each +request without over-extending the parameter list. + +In this patch we implement options for peer verification for TLS-enabled +protocols and to return response with body and headers together. + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 300b9e9217ee1ed8d845ed2370c5ccf5c87afb36) +--- + src/tests/tcurl_test_tool.c | 41 +- + src/util/tev_curl.c | 992 +++++++++++++++++++++++++------------------- + src/util/tev_curl.h | 172 +++++++- + src/util/util_errors.c | 4 + + src/util/util_errors.h | 4 + + 5 files changed, 755 insertions(+), 458 deletions(-) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 2af950ebb76a22bdf4a6dfd58442b10486e64293..9a6266f89131ffd3a561e857af85df9854c44949 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -42,9 +42,7 @@ static void request_done(struct tevent_req *req) + struct tool_ctx *tool_ctx = tevent_req_callback_data(req, + struct tool_ctx); + +- tool_ctx->error = tcurl_http_recv(tool_ctx, req, +- &http_code, +- &outbuf); ++ tool_ctx->error = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code); + talloc_zfree(req); + + if (tool_ctx->error != EOK) { +@@ -87,16 +85,17 @@ int main(int argc, const char *argv[]) + "The path to the HTTP server socket", NULL }, + { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL }, + { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL }, +- { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, + { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL }, ++ { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, + { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL }, + POPT_TABLEEND + }; + + struct tevent_req *req; + struct tevent_context *ev; +- enum tcurl_http_request req_type = TCURL_HTTP_GET; ++ enum tcurl_http_method method = TCURL_HTTP_GET; + struct tcurl_ctx *ctx; ++ struct tcurl_request *tcurl_req; + struct tool_ctx *tool_ctx; + + const char *urls[MAXREQ] = { 0 }; +@@ -111,16 +110,16 @@ int main(int argc, const char *argv[]) + while ((opt = poptGetNextOpt(pc)) > 0) { + switch (opt) { + case 'g': +- req_type = TCURL_HTTP_GET; ++ method = TCURL_HTTP_GET; + break; + case 'p': +- req_type = TCURL_HTTP_PUT; +- break; +- case 'd': +- req_type = TCURL_HTTP_DELETE; ++ method = TCURL_HTTP_PUT; + break; + case 'o': +- req_type = TCURL_HTTP_POST; ++ method = TCURL_HTTP_POST; ++ break; ++ case 'd': ++ method = TCURL_HTTP_DELETE; + break; + case 'v': + pc_verbose = 1; +@@ -146,7 +145,7 @@ int main(int argc, const char *argv[]) + } + + while ((extra_arg_ptr = poptGetArg(pc)) != NULL) { +- switch (req_type) { ++ switch(method) { + case TCURL_HTTP_GET: + case TCURL_HTTP_DELETE: + case TCURL_HTTP_POST: +@@ -203,14 +202,16 @@ int main(int argc, const char *argv[]) + } + + for (size_t i = 0; i < n_reqs; i++) { +- req = tcurl_http_send(tool_ctx, ev, ctx, +- req_type, +- socket_path, +- urls[i], +- headers, +- inbufs[i], +- 5); +- if (req == NULL) { ++ tcurl_req = tcurl_http(tool_ctx, method, socket_path, ++ urls[i], headers, inbufs[i]); ++ if (tcurl_req == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create TCURL request\n"); ++ talloc_zfree(tool_ctx); ++ return 1; ++ } ++ ++ req = tcurl_request_send(tool_ctx, ev, ctx, tcurl_req, 10); ++ if (ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n"); + talloc_zfree(tool_ctx); + return 1; +diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c +index 645d1182d10f825f209f48e0ba7e6804dde1971c..c155f4c038d4215933ee30d41c694ad4a14ae132 100644 +--- a/src/util/tev_curl.c ++++ b/src/util/tev_curl.c +@@ -34,8 +34,8 @@ + #include "util/util.h" + #include "util/tev_curl.h" + +-#define IOBUF_CHUNK 1024 +-#define IOBUF_MAX 4096 ++#define TCURL_IOBUF_CHUNK 1024 ++#define TCURL_IOBUF_MAX 4096 + + static bool global_is_curl_initialized; + +@@ -71,39 +71,12 @@ struct tcurl_sock { + struct tevent_fd *fde; /* tevent tracker of the fd events */ + }; + +-/** +- * @brief A state of one curl transfer +- * +- * Intentionally breaking the tevent coding style here and making the struct available +- * in the whole module so that the structure is available to curl callbacks that +- * need to access the state of the transfer. +- * +- * @see handle_curlmsg_done() +- */ +-struct tcurl_http_state { +- /* Input parameters */ +- struct tcurl_ctx *tctx; +- const char *socket_path; +- const char *url; +- int timeout; +- struct sss_iobuf *inbuf; +- +- /* Internal state */ +- CURL *http_handle; +- struct curl_slist *curl_headers; +- +- /* Output data */ +- struct sss_iobuf *outbuf; +- long http_code; +-}; ++static void tcurl_request_done(struct tevent_req *req, ++ errno_t process_error, ++ int response_code); + + static errno_t curl_code2errno(CURLcode crv) + { +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "curl error %d: %s\n", crv, curl_easy_strerror(crv)); +- } +- + switch (crv) { + /* HTTP error does not fail the whole request, just returns the error + * separately +@@ -121,6 +94,47 @@ static errno_t curl_code2errno(CURLcode crv) + return ENOMEM; + case CURLE_OPERATION_TIMEDOUT: + return ETIMEDOUT; ++ case CURLE_SSL_ISSUER_ERROR: ++ case CURLE_SSL_CACERT_BADFILE: ++ case CURLE_SSL_CACERT: ++ case CURLE_SSL_CERTPROBLEM: ++ return ERR_INVALID_CERT; ++ ++ case CURLE_SSL_CRL_BADFILE: ++ case CURLE_SSL_SHUTDOWN_FAILED: ++ case CURLE_SSL_ENGINE_INITFAILED: ++ case CURLE_USE_SSL_FAILED: ++ case CURLE_SSL_CIPHER: ++ case CURLE_SSL_ENGINE_SETFAILED: ++ case CURLE_SSL_ENGINE_NOTFOUND: ++ case CURLE_SSL_CONNECT_ERROR: ++ return ERR_SSL_FAILURE; ++ case CURLE_PEER_FAILED_VERIFICATION: ++ return ERR_UNABLE_TO_VERIFY_PEER; ++ case CURLE_COULDNT_RESOLVE_HOST: ++ return ERR_UNABLE_TO_RESOLVE_HOST; ++ default: ++ break; ++ } ++ ++ return EIO; ++} ++ ++static errno_t curlm_code2errno(CURLcode crv) ++{ ++ switch (crv) { ++ case CURLM_OK: ++ return EOK; ++ case CURLM_BAD_SOCKET: ++ return EPIPE; ++ case CURLM_OUT_OF_MEMORY: ++ return ENOMEM; ++ case CURLM_BAD_HANDLE: ++ case CURLM_BAD_EASY_HANDLE: ++ case CURLM_UNKNOWN_OPTION: ++ return EINVAL; ++ case CURLM_INTERNAL_ERROR: ++ return ERR_INTERNAL; + default: + break; + } +@@ -145,22 +159,6 @@ static errno_t tcurl_global_init(void) + return EOK; + } + +-static const char *http_req2str(enum tcurl_http_request req) +-{ +- switch (req) { +- case TCURL_HTTP_GET: +- return "GET"; +- case TCURL_HTTP_PUT: +- return "PUT"; +- case TCURL_HTTP_DELETE: +- return "DELETE"; +- case TCURL_HTTP_POST: +- return "POST"; +- } +- +- return "Uknown request type"; +-} +- + static int curl2tev_flags(int curlflags) + { + int flags = 0; +@@ -185,9 +183,9 @@ static void handle_curlmsg_done(CURLMsg *message) + CURL *easy_handle; + CURLcode crv; + struct tevent_req *req; ++ long response_code = 0; + char *done_url; + errno_t ret; +- struct tcurl_http_state *state; + + easy_handle = message->easy_handle; + if (easy_handle == NULL) { +@@ -198,9 +196,8 @@ static void handle_curlmsg_done(CURLMsg *message) + if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) { + crv = curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url); + if (crv != CURLE_OK) { +- DEBUG(SSSDBG_MINOR_FAILURE, +- "Cannot get CURLINFO_EFFECTIVE_URL [%d]: %s\n", +- crv, curl_easy_strerror(crv)); ++ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get CURLINFO_EFFECTIVE_URL " ++ "[%d]: %s\n", crv, curl_easy_strerror(crv)); + /* not fatal since we need this only for debugging */ + } else { + DEBUG(SSSDBG_TRACE_FUNC, "Handled %s\n", done_url); +@@ -209,38 +206,32 @@ static void handle_curlmsg_done(CURLMsg *message) + + crv = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (void *) &req); + if (crv != CURLE_OK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Cannot get CURLINFO_PRIVATE [%d]: %s\n", ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get CURLINFO_PRIVATE [%d]: %s\n", + crv, curl_easy_strerror(crv)); +- return; +- } +- +- state = tevent_req_data(req, struct tcurl_http_state); +- if (state == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "BUG: request has no state\n"); +- tevent_req_error(req, EFAULT); +- return; ++ ret = curl_code2errno(crv); ++ goto done; + } + + ret = curl_code2errno(message->data.result); + if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "curl operation failed [%d]: %s\n", ret, sss_strerror(ret)); +- tevent_req_error(req, ret); +- return; ++ DEBUG(SSSDBG_OP_FAILURE, "CURL operation failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; + } + +- /* If there was no fatal error, let's read the HTTP error code and mark +- * the request as done +- */ +- crv = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &state->http_code); ++ /* If there was no fatal error, let's read the response code ++ * and mark the request as done */ ++ crv = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &response_code); + if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, "Cannot get HTTP status code\n"); +- tevent_req_error(req, EFAULT); +- return; ++ DEBUG(SSSDBG_OP_FAILURE, "Cannot get response code\n"); ++ ret = curl_code2errno(crv); ++ goto done; + } + +- tevent_req_done(req); ++ ret = EOK; ++ ++done: ++ tcurl_request_done(req, ret, response_code); + } + + static void process_curl_activity(struct tcurl_ctx *tctx) +@@ -551,346 +542,42 @@ fail: + return NULL; + } + +-static errno_t tcurl_add_headers(struct tcurl_http_state *state, +- const char *headers[]); +- +-static errno_t tcurl_set_options(struct tcurl_http_state *state, +- struct tevent_req *req, +- enum tcurl_http_request req_type); +- +-static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr); +- +-static size_t tcurl_http_write_data(char *ptr, +- size_t size, +- size_t nmemb, +- void *userdata); +- +-static size_t tcurl_http_read_data(void *ptr, +- size_t size, +- size_t nmemb, +- void *userdata); +- +-struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx, +- struct tevent_context *ev, +- struct tcurl_ctx *tctx, +- enum tcurl_http_request req_type, +- const char *socket_path, +- const char *url, +- const char *headers[], +- struct sss_iobuf *req_data, +- int timeout) +-{ +- errno_t ret; +- struct tevent_req *req; +- struct tcurl_http_state *state; +- +- req = tevent_req_create(mem_ctx, &state, struct tcurl_http_state); +- if (req == NULL) { +- return NULL; +- } +- +- state->tctx = tctx; +- state->socket_path = socket_path; +- state->url = url; +- state->inbuf = req_data; +- state->timeout = timeout; +- +- state->outbuf = sss_iobuf_init_empty(state, IOBUF_CHUNK, IOBUF_MAX); +- if (state->outbuf == NULL) { +- ret = ENOMEM; +- goto fail; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, +- "HTTP request %s for URL %s\n", http_req2str(req_type), url); +- talloc_set_destructor((TALLOC_CTX *) state, tcurl_http_cleanup_handle); +- +- /* All transfer share the same multi handle, but each trasfer has its own +- * easy handle we can use to set per-transfer options +- */ +- state->http_handle = curl_easy_init(); +- if (state->http_handle == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "curl_easy_init failed\n"); +- ret = EIO; +- goto fail; +- } +- +- ret = tcurl_add_headers(state, headers); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to set CURL headers [%d]: %s\n", ret, sss_strerror(ret)); +- goto fail; +- } +- +- ret = tcurl_set_options(state, req, req_type); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to set CURL options [%d]: %s\n", ret, sss_strerror(ret)); +- goto fail; +- } +- +- /* Pass control to the curl handling which will mark the request as +- * done +- */ +- curl_multi_add_handle(tctx->multi_handle, state->http_handle); +- +- return req; +- +-fail: +- tevent_req_error(req, ret); +- tevent_req_post(req, ev); +- return req; +-} +- +-static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr) +-{ +- struct tcurl_http_state *state = talloc_get_type(ptr, struct tcurl_http_state); +- +- if (state == NULL) { +- return 0; +- } +- +- /* it is safe to pass NULL here */ +- curl_multi_remove_handle(state->tctx->multi_handle, state->http_handle); +- curl_slist_free_all(state->curl_headers); +- curl_easy_cleanup(state->http_handle); +- return 0; +-} +- +-static errno_t tcurl_add_headers(struct tcurl_http_state *state, +- const char *headers[]) +-{ +- if (headers == NULL) { +- return EOK; +- } +- +- /* The headers will be freed later in tcurl_http_cleanup_handle */ +- for (int i = 0; headers[i] != NULL; i++) { +- state->curl_headers = curl_slist_append(state->curl_headers, headers[i]); +- if (state->curl_headers == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add header %s\n", headers[i]); +- return ENOMEM; +- } +- } +- +- /* Add a dummy header to suppress libcurl adding Expect 100-continue which +- * was causing libcurl to always wait for the internal timeout when sending +- * a PUT/PATCH request +- */ +- state->curl_headers = curl_slist_append(state->curl_headers, "Expect:"); +- if (state->curl_headers == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add the dummy expect header\n"); +- return ENOMEM; +- } +- +- return EOK; +-} +- +-static errno_t tcurl_set_common_options(struct tcurl_http_state *state, +- struct tevent_req *req) +-{ +- CURLcode crv; +- +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_HTTPHEADER, +- state->curl_headers); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set HTTP headers [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_UNIX_SOCKET_PATH, +- state->socket_path); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set UNIX socket path %s [%d]: %s\n", +- state->socket_path, crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- crv = curl_easy_setopt(state->http_handle, CURLOPT_URL, state->url); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set URL %s [%d]: %s\n", +- state->url, crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- crv = curl_easy_setopt(state->http_handle, CURLOPT_PRIVATE, req); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set private data [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- if (state->timeout > 0) { +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_TIMEOUT, +- state->timeout); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set timeout [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- } +- +- return EOK; +-} +- +-static errno_t tcurl_set_write_options(struct tcurl_http_state *state) +-{ +- CURLcode crv; +- +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_WRITEFUNCTION, +- tcurl_http_write_data); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set write function [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_WRITEDATA, +- state->outbuf); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set write data [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- return EOK; +-} +- +-static errno_t tcurl_set_read_options(struct tcurl_http_state *state) +-{ +- CURLcode crv; +- +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_READFUNCTION, +- tcurl_http_read_data); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set read function [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_READDATA, +- state->inbuf); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set read data [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- return EOK; +-} +- +-static errno_t tcurl_set_options(struct tcurl_http_state *state, +- struct tevent_req *req, +- enum tcurl_http_request req_type) +-{ +- CURLcode crv; +- errno_t ret; +- +- ret = tcurl_set_common_options(state, req); +- if (ret != EOK) { +- return ret; +- } +- +- ret = tcurl_set_write_options(state); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to set write callbacks [%d]: %s\n", +- ret, sss_strerror(ret)); +- return ret; +- } +- +- switch (req_type) { +- case TCURL_HTTP_POST: +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_CUSTOMREQUEST, +- "POST"); +- break; +- case TCURL_HTTP_PUT: +- /* CURLOPT_UPLOAD enables HTTP_PUT */ +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_UPLOAD, +- 1L); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set the uplodad option [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- /* Causes libcurl to add a sane Content-Length header */ +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_INFILESIZE_LARGE, +- (curl_off_t) sss_iobuf_get_size(state->inbuf)); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set CURLOPT_INFILESIZE_LARGE [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- +- ret = tcurl_set_read_options(state); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to set write callbacks [%d]: %s\n", +- ret, sss_strerror(ret)); +- return ret; +- } +- break; +- case TCURL_HTTP_GET: +- /* GET just needs the write callbacks, nothing to do here.. */ +- break; +- case TCURL_HTTP_DELETE: +- crv = curl_easy_setopt(state->http_handle, +- CURLOPT_CUSTOMREQUEST, +- "DELETE"); +- if (crv != CURLE_OK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to set the uplodad option [%d]: %s\n", +- crv, curl_easy_strerror(crv)); +- return EIO; +- } +- break; +- default: +- return EFAULT; +- } +- +- return EOK; +-} +- +-static size_t tcurl_http_write_data(char *ptr, +- size_t size, +- size_t nmemb, +- void *userdata) ++#define tcurl_set_option(tcurl_req, option, value) \ ++({ \ ++ CURLcode __curl_code; \ ++ errno_t __ret; \ ++ \ ++ __curl_code = curl_easy_setopt((tcurl_req)->curl_easy_handle, \ ++ (option), (value)); \ ++ if (__curl_code == CURLE_OK) { \ ++ __ret = EOK; \ ++ } else { \ ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to set CURL option %s [%d]: %s\n", \ ++ #option, __curl_code, curl_easy_strerror(__curl_code)); \ ++ __ret = curl_code2errno(__curl_code); \ ++ } \ ++ __ret; \ ++}) ++ ++static size_t tcurl_write_data(char *ptr, ++ size_t size, ++ size_t nmemb, ++ void *userdata) + { + errno_t ret; + size_t realsize = size * nmemb; +- struct sss_iobuf *outbuf = talloc_get_type(userdata, struct sss_iobuf); ++ struct sss_iobuf *outbuf; ++ ++ outbuf = talloc_get_type(userdata, struct sss_iobuf); + + DEBUG(SSSDBG_TRACE_INTERNAL, "---> begin libcurl data\n"); + DEBUG(SSSDBG_TRACE_INTERNAL, "%s\n", ptr); + DEBUG(SSSDBG_TRACE_INTERNAL, "<--- end libcurl data\n"); + +- ret = sss_iobuf_write_len(outbuf, (uint8_t *) ptr, realsize); ++ ret = sss_iobuf_write_len(outbuf, (uint8_t *)ptr, realsize); + if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to write data to buffer [%d]: %s\n", ret, sss_strerror(ret)); ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to write data to buffer [%d]: %s\n", ++ ret, sss_strerror(ret)); + /* zero signifies an EOF */ + return 0; + } +@@ -898,14 +585,16 @@ static size_t tcurl_http_write_data(char *ptr, + return realsize; + } + +-static size_t tcurl_http_read_data(void *ptr, +- size_t size, +- size_t nmemb, +- void *userdata) ++static size_t tcurl_read_data(void *ptr, ++ size_t size, ++ size_t nmemb, ++ void *userdata) + { + errno_t ret; + size_t readbytes; +- struct sss_iobuf *inbuf = (struct sss_iobuf *) userdata; ++ struct sss_iobuf *inbuf; ++ ++ inbuf = talloc_get_type(userdata, struct sss_iobuf); + + if (inbuf == NULL) { + return CURL_READFUNC_ABORT; +@@ -919,22 +608,487 @@ static size_t tcurl_http_read_data(void *ptr, + return readbytes; + } + +-int tcurl_http_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- int *_http_code, +- struct sss_iobuf **_outbuf) ++ ++struct tcurl_request { ++ CURL *curl_easy_handle; ++ ++ struct sss_iobuf *body; ++ struct curl_slist *headers; ++ ++ const char *url; ++ const char *socket; ++ ++ /* Associated tcurl context if this request is in progress. */ ++ struct tcurl_ctx *tcurl_ctx; ++}; ++ ++struct tcurl_request_state { ++ struct tcurl_request *tcurl_req; ++ struct sss_iobuf *response; ++ int response_code; ++}; ++ ++struct tevent_req * ++tcurl_request_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct tcurl_ctx *tcurl_ctx, ++ struct tcurl_request *tcurl_req, ++ long int timeout) + { +- struct tcurl_http_state *state = tevent_req_data(req, struct tcurl_http_state); ++ struct tcurl_request_state *state; ++ struct tevent_req *req; ++ CURLMcode curl_code; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct tcurl_request_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); ++ return NULL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Sending TCURL request for %s, at socket %s\n", ++ tcurl_req->url == NULL ? "" : tcurl_req->url, ++ tcurl_req->socket == NULL ? "" : tcurl_req->socket); ++ ++ state->tcurl_req = talloc_steal(state, tcurl_req); ++ ++ state->response = sss_iobuf_init_empty(state, TCURL_IOBUF_CHUNK, TCURL_IOBUF_MAX); ++ if (state->response == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_PRIVATE, req); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_TIMEOUT, timeout); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEFUNCTION, tcurl_write_data); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEDATA, state->response); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ if (tcurl_req->body != NULL) { ++ ret = tcurl_set_option(tcurl_req, CURLOPT_READFUNCTION, tcurl_read_data); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_READDATA, tcurl_req->body); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ ++ curl_code = curl_multi_add_handle(tcurl_ctx->multi_handle, ++ tcurl_req->curl_easy_handle); ++ if (curl_code != CURLM_OK) { ++ ret = curlm_code2errno(curl_code); ++ goto done; ++ } ++ ++ tcurl_req->tcurl_ctx = tcurl_ctx; ++ ++ ret = EAGAIN; ++ ++done: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ tevent_req_post(req, ev); ++ } else if (ret != EAGAIN) { ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ } ++ ++ return req; ++} ++ ++static void tcurl_request_done(struct tevent_req *req, ++ errno_t process_error, ++ int response_code) ++{ ++ struct tcurl_request_state *state; ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "TCURL request finished [%d]: %s\n", ++ process_error, sss_strerror(process_error)); ++ ++ if (req == NULL) { ++ /* To handle case where we fail to obtain request from private data. */ ++ DEBUG(SSSDBG_MINOR_FAILURE, "No tevent request provided!\n"); ++ return; ++ } ++ ++ state = tevent_req_data(req, struct tcurl_request_state); ++ ++ curl_multi_remove_handle(state->tcurl_req->tcurl_ctx->multi_handle, ++ state->tcurl_req->curl_easy_handle); ++ ++ /* This request is no longer associated with tcurl context. */ ++ state->tcurl_req->tcurl_ctx = NULL; ++ ++ if (process_error != EOK) { ++ tevent_req_error(req, process_error); ++ return; ++ } ++ ++ state->response_code = response_code; ++ ++ tevent_req_done(req); ++ return; ++} ++ ++errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct sss_iobuf **_response, ++ int *_response_code) ++{ ++ struct tcurl_request_state *state; ++ state = tevent_req_data(req, struct tcurl_request_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + +- if (_http_code != NULL) { +- *_http_code = state->http_code; ++ if (_response != NULL) { ++ *_response = talloc_steal(mem_ctx, state->response); + } + +- if (_outbuf != NULL) { +- *_outbuf = talloc_steal(mem_ctx, state->outbuf); ++ if (_response_code != NULL) { ++ *_response_code = state->response_code; ++ } ++ ++ return EOK; ++} ++ ++static struct curl_slist * ++tcurl_add_header(struct curl_slist *slist, const char *header) ++{ ++ struct curl_slist *new; ++ ++ new = curl_slist_append(slist, header); ++ if (new == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add header %s\n", header); ++ if (slist != NULL) { ++ curl_slist_free_all(slist); ++ } ++ ++ return NULL; ++ } ++ ++ return new; ++} ++ ++static errno_t ++tcurl_construct_headers(const char **headers, ++ struct curl_slist **_slist) ++{ ++ struct curl_slist *slist = NULL; ++ int i; ++ ++ if (headers == NULL || headers[0] == NULL) { ++ *_slist = NULL; ++ return EOK; ++ } ++ ++ for (i = 0; headers[i] != NULL; i++) { ++ slist = tcurl_add_header(slist, headers[i]); ++ if (slist == NULL) { ++ return ENOMEM; ++ } ++ } ++ ++ /* Add a dummy header to suppress libcurl adding Expect 100-continue which ++ * was causing libcurl to always wait for the internal timeout when sending ++ * a PUT/POST request because secrets responder does not implement this. ++ */ ++ slist = tcurl_add_header(slist, "Expect: "); ++ if (slist == NULL) { ++ return ENOMEM; ++ } ++ ++ *_slist = slist; ++ ++ return EOK; ++} ++ ++static int ++tcurl_request_destructor(struct tcurl_request *tcurl_req) ++{ ++ if (tcurl_req->tcurl_ctx != NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Terminating TCURL request...\n"); ++ curl_multi_remove_handle(tcurl_req->tcurl_ctx->multi_handle, ++ tcurl_req->curl_easy_handle); ++ } ++ ++ if (tcurl_req->headers != NULL) { ++ curl_slist_free_all(tcurl_req->headers); ++ } ++ ++ if (tcurl_req->curl_easy_handle != NULL) { ++ curl_easy_cleanup(tcurl_req->curl_easy_handle); + } + + return 0; + } ++ ++static struct tcurl_request * ++tcurl_request_create(TALLOC_CTX *mem_ctx, ++ const char *socket_path, ++ const char *url, ++ const char **headers, ++ struct sss_iobuf *body) ++{ ++ struct tcurl_request *tcurl_req; ++ errno_t ret; ++ ++ tcurl_req = talloc_zero(mem_ctx, struct tcurl_request); ++ if (tcurl_req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); ++ return NULL; ++ } ++ ++ if (url == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "URL cannot be NULL!\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ /* Setup a curl easy handle. This handle contains state for the request ++ * and is later associated with curl multi handle which performs ++ * asynchronous processing. */ ++ tcurl_req->curl_easy_handle = curl_easy_init(); ++ if (tcurl_req->curl_easy_handle == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize curl easy handle!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tcurl_req->url = talloc_strdup(tcurl_req, url); ++ if (tcurl_req->url == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ if (socket_path != NULL) { ++ tcurl_req->socket = talloc_strdup(tcurl_req, socket_path); ++ if (tcurl_req->socket == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ ret = tcurl_construct_headers(headers, &tcurl_req->headers); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct headers [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tcurl_req->body = body; ++ ++ talloc_set_destructor(tcurl_req, tcurl_request_destructor); ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_URL, url); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ if (socket_path != NULL) { ++ ret = tcurl_set_option(tcurl_req, CURLOPT_UNIX_SOCKET_PATH, socket_path); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ ++ if (body != NULL) { ++ /* Curl will tell the underlying protocol about incoming data length. ++ * In case of HTTP it will add a sane Content-Length header. */ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_INFILESIZE_LARGE, ++ (curl_off_t)sss_iobuf_get_size(body)); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(tcurl_req); ++ return NULL; ++ } ++ ++ return tcurl_req; ++} ++ ++struct tcurl_request *tcurl_http(TALLOC_CTX *mem_ctx, ++ enum tcurl_http_method method, ++ const char *socket_path, ++ const char *url, ++ const char **headers, ++ struct sss_iobuf *body) ++{ ++ struct tcurl_request *tcurl_req; ++ errno_t ret; ++ ++ tcurl_req = tcurl_request_create(mem_ctx, socket_path, url, headers, body); ++ if (tcurl_req == NULL) { ++ return NULL; ++ } ++ ++ /* Set HTTP specific options. */ ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_HTTPHEADER, tcurl_req->headers); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ switch (method) { ++ case TCURL_HTTP_GET: ++ /* Nothing to do here. GET is default. */ ++ break; ++ case TCURL_HTTP_PUT: ++ ret = tcurl_set_option(tcurl_req, CURLOPT_UPLOAD, 1L); ++ if (ret != EOK) { ++ goto done; ++ } ++ break; ++ case TCURL_HTTP_POST: ++ ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "POST"); ++ if (ret != EOK) { ++ goto done; ++ } ++ break; ++ case TCURL_HTTP_DELETE: ++ ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "DELETE"); ++ if (ret != EOK) { ++ goto done; ++ } ++ break; ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(tcurl_req); ++ return NULL; ++ } ++ ++ return tcurl_req; ++} ++ ++struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct tcurl_ctx *tcurl_ctx, ++ enum tcurl_http_method method, ++ const char *socket_path, ++ const char *url, ++ const char **headers, ++ struct sss_iobuf *body, ++ int timeout) ++{ ++ struct tcurl_request *tcurl_req; ++ struct tevent_req *req; ++ ++ tcurl_req = tcurl_http(mem_ctx, method, socket_path, url, headers, body); ++ if (tcurl_req == NULL) { ++ return NULL; ++ } ++ ++ req = tcurl_request_send(mem_ctx, ev, tcurl_ctx, tcurl_req, timeout); ++ if (req == NULL) { ++ talloc_free(tcurl_req); ++ } ++ ++ return req; ++} ++ ++errno_t tcurl_http_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ int *_http_code, ++ struct sss_iobuf **_response) ++{ ++ return tcurl_request_recv(mem_ctx, req, _response, _http_code); ++} ++ ++errno_t tcurl_req_enable_rawoutput(struct tcurl_request *tcurl_req) ++{ ++ return tcurl_set_option(tcurl_req, CURLOPT_HEADER, 1L); ++} ++ ++errno_t tcurl_req_verify_peer(struct tcurl_request *tcurl_req, ++ const char *capath, ++ const char *cacert, ++ bool verify_peer, ++ bool verify_host) ++{ ++ errno_t ret; ++ ++ long peer = verify_peer ? 1L : 0L; ++ long host = verify_host ? 2L : 0L; ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYPEER, peer); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYHOST, host); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ if (capath != NULL) { ++ ret = tcurl_set_option(tcurl_req, CURLOPT_CAPATH, capath); ++ if (ret != EOK) { ++ return ret; ++ } ++ } ++ ++ if (cacert != NULL) { ++ ret = tcurl_set_option(tcurl_req, CURLOPT_CAINFO, cacert); ++ if (ret != EOK) { ++ return ret; ++ } ++ } ++ ++ return EOK; ++} ++ ++errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req, ++ const char *cert, ++ const char *key) ++{ ++ errno_t ret; ++ ++ if (cert == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "You must specify client certificate!\n"); ++ return EINVAL; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_SSLCERT, cert); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ if (key != NULL) { ++ /* If client's private key is in separate file. */ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_SSLKEY, key); ++ if (ret != EOK) { ++ return ret; ++ } ++ } ++ ++ return EOK; ++} +diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h +index 444eb286e09d189b4588e2b2152b5202df3914d8..933abcb9b531412737e8fcf391644d828b125cf8 100644 +--- a/src/util/tev_curl.h ++++ b/src/util/tev_curl.h +@@ -27,14 +27,16 @@ + + #include "util/sss_iobuf.h" + ++struct tcurl_request; ++ + /** +- * @brief Supported HTTP requests ++ * @brief Supported HTTP methods + */ +-enum tcurl_http_request { ++enum tcurl_http_method { + TCURL_HTTP_GET, + TCURL_HTTP_PUT, +- TCURL_HTTP_DELETE, + TCURL_HTTP_POST, ++ TCURL_HTTP_DELETE, + }; + + /** +@@ -46,16 +48,95 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev); + + /** ++ * @brief Run a single asynchronous TCURL request. ++ * ++ * If the libcurl processing succeeds but we obtain a protocol error we still ++ * mark the tevent request as successful. The protocol error is return from ++ * @tcurl_request_recv as an output parameter. ++ * ++ * @param[in] mem_ctx The talloc context that owns the request ++ * @param[in] ev Event loop context ++ * @param[in] tctx Use tcurl_init to get this context ++ * @param[in] tcurl_req TCURL request ++ * @param[in] timeout The request timeout in seconds. Use 0 if you want ++ * to use the default libcurl timeout. ++ * ++ * @returns A tevent request or NULL on allocation error. On other errors, we ++ * try to set the errno as event error code and run it to completion so that ++ * the programmer can use tcurl_request_recv to read the error code. ++ * ++ * @see tcurl_init ++ * @see tcurl_http ++ * @see tcurl_request_recv ++ */ ++struct tevent_req * ++tcurl_request_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct tcurl_ctx *tcurl_ctx, ++ struct tcurl_request *tcurl_req, ++ long int timeout); ++ ++/** ++ * @brief Receive a result of a single asynchronous TCURL request. ++ * ++ * @param[in] mem_ctx The talloc context that owns the response ++ * @param[in] req The request previously obtained with tcurl_request_send ++ * @param[out] _response Response to the request ++ * @param[out] _response_code Protocol response code (may indicate a protocl error) ++ * ++ * @returns The error code of the curl request (not the HTTP code!) ++ */ ++errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct sss_iobuf **_response, ++ int *_response_code); ++ ++/** ++ * @brief Create a HTTP request. ++ * ++ * Use this if you need better control over the request options. ++ * ++ * Headers are a NULL-terminated array of strings such as: ++ * static const char *headers[] = { ++ * "Content-type: application/octet-stream", ++ * NULL, ++ * }; ++ * ++ * @param[in] mem_ctx The talloc context that owns the tcurl_request ++ * @param[in] method TCURL HTTP method ++ * @param[in] socket_path The path to the UNIX socket to forward the ++ * request to, may be NULL. ++ * @param[in] url The request URL, cannot be NULL. ++ * @param[in] headers A NULL-terminated array of strings to use ++ * as additional HTTP headers. Pass NULL if you ++ * don't need any additional headers. ++ * @param[in] body The HTTP request input data. For some request ++ * types like DELETE, this is OK to leave as NULL. ++ * ++ * @returns A tcurl_request that can be later started with tcurl_request_send ++ * or NULL on error. ++ * ++ * @see tcurl_init ++ * @see tcurl_request_send ++ * @see tcurl_request_recv ++ */ ++struct tcurl_request *tcurl_http(TALLOC_CTX *mem_ctx, ++ enum tcurl_http_method method, ++ const char *socket_path, ++ const char *url, ++ const char **headers, ++ struct sss_iobuf *body); ++ ++/** + * @brief Run a single asynchronous HTTP request. + * +- * Currently only UNIX sockets at socket_path are supported. ++ * Use this if you do not need control over additional request options. + * + * If the request runs into completion, but reports a failure with HTTP return + * code, the request will be marked as done. Only if the request cannot run at + * all (if e.g. the socket is unreachable), the request will fail completely. + * +- * Headers are a NULL-terminated +- * array of strings such as: ++ * Headers are a NULL-terminated array of strings such as: + * static const char *headers[] = { + * "Content-type: application/octet-stream", + * NULL, +@@ -63,15 +144,15 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx, + * + * @param[in] mem_ctx The talloc context that owns the iobuf + * @param[in] ev Event loop context +- * @param[in] tctx Use tcurl_init to get this context +- * @param[in] req_type The request type ++ * @param[in] tcurl_ctx Use tcurl_init to get this context ++ * @param[in] method HTTP method + * @param[in] socket_path The path to the UNIX socket to forward the +- * request to +- * @param[in] url The request URL ++ * request to, may be NULL. ++ * @param[in] url The request URL, cannot be NULL. + * @param[in] headers A NULL-terminated array of strings to use + * as additional HTTP headers. Pass NULL if you + * don't need any additional headers. +- * @param[in] req_data The HTTP request input data. For some request ++ * @param[in] body The HTTP request input data. For some request + * types like DELETE, this is OK to leave as NULL. + * @param[in] timeout The request timeout in seconds. Use 0 if you want + * to use the default libcurl timeout. +@@ -85,12 +166,12 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx, + */ + struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, +- struct tcurl_ctx *tctx, +- enum tcurl_http_request req_type, ++ struct tcurl_ctx *tcurl_ctx, ++ enum tcurl_http_method method, + const char *socket_path, + const char *url, +- const char *headers[], +- struct sss_iobuf *req_data, ++ const char **headers, ++ struct sss_iobuf *body, + int timeout); + + /** +@@ -104,9 +185,62 @@ struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx, + * + * @returns The error code of the curl request (not the HTTP code!) + */ +-int tcurl_http_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- int *_http_code, +- struct sss_iobuf **_outbuf); ++errno_t tcurl_http_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ int *_http_code, ++ struct sss_iobuf **_response); ++ ++/** ++ * @brief We are usually interested only in the reply body without protocol ++ * headers. Call this function on tcurl_request, if you want to include ++ * complete protocol response in the output buffer. ++ * ++ * @param[in] tcurl_request ++ * ++ * @returns errno code ++ * ++ * @see tcurl_http ++ */ ++errno_t tcurl_req_enable_rawoutput(struct tcurl_request *tcurl_req); ++ ++/** ++ * @brief TLS is enabled automatically by providing an URL that points to ++ * TLS-enabled protocol such as https. If you want to provide different ++ * path to CA directory or disable peer/hostname check explicitly, use ++ * this function on tcurl_request. ++ * ++ * @param[in] tcurl_request ++ * @param[in] capath Path to directory containing installed CA certificates. ++ * If not set, libcurl default is used. ++ * @param[ing cacert CA certificate. If NULL it is found in @capath. ++ * @param[in] verify_peer If false, the peer certificate is not verified. ++ * @param[in] verify_host If false, the host name provided in remote ++ * certificate may differ from the actual host name. ++ * ++ * @returns errno code ++ * ++ * @see tcurl_http ++ */ ++errno_t tcurl_req_verify_peer(struct tcurl_request *tcurl_req, ++ const char *capath, ++ const char *cacert, ++ bool verify_peer, ++ bool verify_host); ++/** ++ * @brief Some server require client verification during TLS setup. You can ++ * provide path to client's certificate file. If this file does not contain ++ * private key, you can specify a different file the holds the private key. ++ * ++ * @param[in] tcurl_request ++ * @param[in] cert Path to client's certificate. ++ * @param[in] key Path to client's private key. ++ * ++ * @returns errno code ++ * ++ * @see tcurl_http ++ */ ++errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req, ++ const char *cert, ++ const char *key); + + #endif /* __TEV_CURL_H */ +diff --git a/src/util/util_errors.c b/src/util/util_errors.c +index 60c2f439b3e39b1dbff353e429114cb5a3070052..466a3b4062f39b29d831a5d8a62dc8d576eb2e97 100644 +--- a/src/util/util_errors.c ++++ b/src/util/util_errors.c +@@ -111,6 +111,10 @@ struct err_string error_to_str[] = { + { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */ + { "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */ + { "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */ ++ { "Invalid certificate provided" }, /* ERR_INVALID_CERT */ ++ { "Unable to initialize SSL" }, /* ERR_SSL_FAILURE */ ++ { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */ ++ { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */ + { "ERR_LAST" } /* ERR_LAST */ + }; + +diff --git a/src/util/util_errors.h b/src/util/util_errors.h +index 4e9da814702e2cd46edc52fd5c2ae5f640602609..2f90c0a5d65325a431a8e4d9a480170808c9198e 100644 +--- a/src/util/util_errors.h ++++ b/src/util/util_errors.h +@@ -133,6 +133,10 @@ enum sssd_errors { + ERR_KCM_WRONG_CCNAME_FORMAT, + ERR_JSON_ENCODING, + ERR_JSON_DECODING, ++ ERR_INVALID_CERT, ++ ERR_SSL_FAILURE, ++ ERR_UNABLE_TO_VERIFY_PEER, ++ ERR_UNABLE_TO_RESOLVE_HOST, + ERR_LAST /* ALWAYS LAST */ + }; + +-- +2.9.3 + diff --git a/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch b/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch new file mode 100644 index 0000000..bcea2c6 --- /dev/null +++ b/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch @@ -0,0 +1,438 @@ +From a886247bcdb1c551486c34a8d4eccd046a11382f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 24 Feb 2017 12:23:01 +0100 +Subject: [PATCH 75/90] tcurl test: refactor so new options can be added more + easily + +Just to make the tool a little bit nicer and more flexible. + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit b800a6d09244359959404aca81c6796a58cafbcb) +--- + src/tests/tcurl_test_tool.c | 334 +++++++++++++++++++++++++++----------------- + 1 file changed, 209 insertions(+), 125 deletions(-) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 9a6266f89131ffd3a561e857af85df9854c44949..e5fc9705db415650d849b89c3d18e41574b7e28b 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -28,26 +28,39 @@ + + struct tool_ctx { + bool verbose; +- +- errno_t error; + bool done; + + size_t nreqs; + }; + ++struct tool_options { ++ int debug; ++ int verbose; ++ ++ enum tcurl_http_method method; ++ const char *socket_path; ++}; ++ + static void request_done(struct tevent_req *req) + { +- int http_code; ++ struct tool_ctx *tool_ctx; + struct sss_iobuf *outbuf; +- struct tool_ctx *tool_ctx = tevent_req_callback_data(req, +- struct tool_ctx); ++ int http_code; ++ errno_t ret; + +- tool_ctx->error = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code); ++ tool_ctx = tevent_req_callback_data(req, struct tool_ctx); ++ ++ ret = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code); + talloc_zfree(req); + +- if (tool_ctx->error != EOK) { +- DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed: %d\n", tool_ctx->error); ++ tool_ctx->nreqs--; ++ if (tool_ctx->nreqs == 0) { + tool_ctx->done = true; ++ } ++ ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed [%d]: %s\n", ++ ret, sss_strerror(ret)); + return; + } else if (tool_ctx->verbose) { + printf("Request HTTP code: %d\n", http_code); +@@ -55,167 +68,171 @@ static void request_done(struct tevent_req *req) + (const char *) sss_iobuf_get_data(outbuf)); + talloc_zfree(outbuf); + } +- +- tool_ctx->nreqs--; +- if (tool_ctx->nreqs == 0) { +- tool_ctx->done = true; +- } + } + +-int main(int argc, const char *argv[]) ++static errno_t ++parse_options(poptContext pc, struct tool_options *opts) + { + int opt; +- poptContext pc; +- +- int pc_debug = 0; +- int pc_verbose = 0; +- const char *socket_path = NULL; +- const char *extra_arg_ptr; +- +- static const char *headers[] = { +- "Content-type: application/octet-stream", +- NULL, +- }; +- +- struct poptOption long_options[] = { +- POPT_AUTOHELP +- { "debug", '\0', POPT_ARG_INT, &pc_debug, 0, +- "The debug level to run with", NULL }, +- { "socket-path", 's', POPT_ARG_STRING, &socket_path, 0, +- "The path to the HTTP server socket", NULL }, +- { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL }, +- { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL }, +- { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL }, +- { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, +- { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL }, +- POPT_TABLEEND +- }; +- +- struct tevent_req *req; +- struct tevent_context *ev; +- enum tcurl_http_method method = TCURL_HTTP_GET; +- struct tcurl_ctx *ctx; +- struct tcurl_request *tcurl_req; +- struct tool_ctx *tool_ctx; +- +- const char *urls[MAXREQ] = { 0 }; +- struct sss_iobuf **inbufs; +- +- size_t n_reqs = 0; +- +- debug_prg_name = argv[0]; +- pc = poptGetContext(NULL, argc, argv, long_options, 0); +- poptSetOtherOptionHelp(pc, "HTTPDATA"); + + while ((opt = poptGetNextOpt(pc)) > 0) { + switch (opt) { + case 'g': +- method = TCURL_HTTP_GET; ++ opts->method = TCURL_HTTP_GET; + break; + case 'p': +- method = TCURL_HTTP_PUT; ++ opts->method = TCURL_HTTP_PUT; + break; + case 'o': +- method = TCURL_HTTP_POST; ++ opts->method = TCURL_HTTP_POST; + break; + case 'd': +- method = TCURL_HTTP_DELETE; +- break; +- case 'v': +- pc_verbose = 1; ++ opts->method = TCURL_HTTP_DELETE; + break; + default: + DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected option\n"); +- return 1; ++ return EINVAL; + } + } + +- DEBUG_CLI_INIT(pc_debug); +- +- tool_ctx = talloc_zero(NULL, struct tool_ctx); +- if (tool_ctx == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n"); +- return 1; ++ if (opt != -1) { ++ poptPrintUsage(pc, stderr, 0); ++ fprintf(stderr, "%s", poptStrerror(opt)); ++ return EINVAL; + } + +- inbufs = talloc_zero_array(tool_ctx, struct sss_iobuf *, MAXREQ); +- if (inbufs == NULL) { +- talloc_zfree(tool_ctx); +- return 1; ++ return EOK; ++} ++ ++static errno_t ++prepare_requests(TALLOC_CTX *mem_ctx, ++ poptContext pc, ++ struct tool_options *opts, ++ struct tcurl_request ***_requests, ++ size_t *_num_requests) ++{ ++ struct tcurl_request **requests; ++ const char *arg; ++ const char *url; ++ struct sss_iobuf *body; ++ errno_t ret; ++ size_t i; ++ ++ static const char *headers[] = { ++ "Content-type: application/octet-stream", ++ NULL, ++ }; ++ ++ requests = talloc_zero_array(mem_ctx, struct tcurl_request *, MAXREQ + 1); ++ if (requests == NULL) { ++ return ENOMEM; + } + +- while ((extra_arg_ptr = poptGetArg(pc)) != NULL) { +- switch(method) { ++ i = 0; ++ while ((arg = poptGetArg(pc)) != NULL) { ++ if (i >= MAXREQ) { ++ fprintf(stderr, _("Too many requests!\n")); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ switch (opts->method) { + case TCURL_HTTP_GET: + case TCURL_HTTP_DELETE: +- case TCURL_HTTP_POST: +- urls[n_reqs++] = extra_arg_ptr; ++ url = arg; ++ body = NULL; + break; + case TCURL_HTTP_PUT: +- if (urls[n_reqs] == NULL) { +- urls[n_reqs] = extra_arg_ptr; +- } else { +- inbufs[n_reqs] = sss_iobuf_init_readonly( +- inbufs, +- (uint8_t *) discard_const(extra_arg_ptr), +- strlen(extra_arg_ptr)); +- if (inbufs[n_reqs] == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Could not init input buffer\n"); +- talloc_zfree(tool_ctx); +- return 1; +- } +- n_reqs++; ++ case TCURL_HTTP_POST: ++ url = arg; ++ ++ arg = poptGetArg(pc); ++ if (arg == NULL) { ++ body = NULL; ++ break; ++ } ++ ++ body = sss_iobuf_init_readonly(requests, ++ discard_const_p(uint8_t, arg), ++ strlen(arg)); ++ if (body == NULL) { ++ ret = ENOMEM; ++ goto done; + } + break; ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid method!\n"); ++ ret = EINVAL; ++ goto done; + } ++ ++ requests[i] = tcurl_http(requests, opts->method, opts->socket_path, ++ url, headers, body); ++ if (requests[i] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ i++; + } + +- if (opt != -1) { +- poptPrintUsage(pc, stderr, 0); +- fprintf(stderr, "%s", poptStrerror(opt)); +- talloc_zfree(tool_ctx); +- return 1; ++ *_requests = requests; ++ *_num_requests = i; ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(requests); + } + +- if (!socket_path) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Please specify the socket path\n"); +- poptPrintUsage(pc, stderr, 0); +- talloc_zfree(tool_ctx); +- return 1; ++ return ret; ++} ++ ++static errno_t ++run_requests(struct tool_ctx *tool_ctx, ++ struct tcurl_request **requests) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct tcurl_ctx *tcurl_ctx; ++ struct tevent_context *ev; ++ struct tevent_req *req; ++ errno_t ret; ++ int i; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; + } + +- tool_ctx->nreqs = n_reqs; +- tool_ctx->verbose = !!pc_verbose; ++ if (requests == NULL || requests[0] == NULL) { ++ ret = EOK; ++ goto done; ++ } + +- ev = tevent_context_init(tool_ctx); ++ ev = tevent_context_init(tmp_ctx); + if (ev == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tevent context\n"); +- talloc_zfree(tool_ctx); +- return 1; ++ ret = ENOMEM; ++ goto done; + } + +- ctx = tcurl_init(tool_ctx, ev); +- if (ctx == NULL) { ++ tcurl_ctx = tcurl_init(tmp_ctx, ev); ++ if (tcurl_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Could not init tcurl context\n"); +- talloc_zfree(tool_ctx); +- return 1; ++ ret = ENOMEM; ++ goto done; + } + +- for (size_t i = 0; i < n_reqs; i++) { +- tcurl_req = tcurl_http(tool_ctx, method, socket_path, +- urls[i], headers, inbufs[i]); +- if (tcurl_req == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create TCURL request\n"); +- talloc_zfree(tool_ctx); +- return 1; ++ for (i = 0; requests[i] != NULL; i++) { ++ req = tcurl_request_send(tmp_ctx, ev, tcurl_ctx, requests[i], 5); ++ if (req == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Could not create tevent request\n"); ++ ret = ENOMEM; ++ goto done; + } + +- req = tcurl_request_send(tool_ctx, ev, ctx, tcurl_req, 10); +- if (ctx == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n"); +- talloc_zfree(tool_ctx); +- return 1; +- } + tevent_req_set_callback(req, request_done, tool_ctx); + } + +@@ -226,11 +243,78 @@ int main(int argc, const char *argv[]) + if (tool_ctx->nreqs > 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "The tool finished with some pending requests, fail!\n"); +- talloc_zfree(tool_ctx); +- return 1; ++ ret = EEXIST; ++ goto done; + } + ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ struct tool_options opts = { 0 }; ++ struct tool_ctx *tool_ctx; ++ struct tcurl_request **requests; ++ poptContext pc; ++ errno_t ret; ++ ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ { "debug", '\0', POPT_ARG_INT, &opts.debug, 0, "The debug level to run with", NULL }, ++ { "socket-path", 's', POPT_ARG_STRING, &opts.socket_path, 0, "The path to the HTTP server socket", NULL }, ++ { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL }, ++ { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL }, ++ { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL }, ++ { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, ++ { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL }, ++ POPT_TABLEEND ++ }; ++ ++ pc = poptGetContext(NULL, argc, argv, long_options, 0); ++ poptSetOtherOptionHelp(pc, "[URL HTTPDATA]*"); ++ ++ tool_ctx = talloc_zero(NULL, struct tool_ctx); ++ if (tool_ctx == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = parse_options(pc, &opts); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to parse options [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ DEBUG_CLI_INIT(opts.debug); ++ tool_ctx->verbose = opts.verbose; ++ ++ ret = prepare_requests(tool_ctx, pc, &opts, &requests, &tool_ctx->nreqs); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to prepare requests [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = run_requests(tool_ctx, requests); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to issue requests [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++done: + talloc_free(tool_ctx); + poptFreeContext(pc); +- return 0; ++ ++ if (ret != EOK) { ++ return EXIT_FAILURE; ++ } ++ ++ return EXIT_SUCCESS; + } +-- +2.9.3 + diff --git a/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch b/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch new file mode 100644 index 0000000..3004adc --- /dev/null +++ b/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch @@ -0,0 +1,49 @@ +From 961abf2d35e296fe2b12b2b48c5d3fc67c0bc779 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 24 Feb 2017 12:23:22 +0100 +Subject: [PATCH 76/90] tcurl test: add support for raw output + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 36e49a842e257ac9bde71728ee3bef4299b6e6e2) +--- + src/tests/tcurl_test_tool.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index e5fc9705db415650d849b89c3d18e41574b7e28b..7d3bc19f0ec7e118e251247536d25c58fe009f54 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -36,6 +36,7 @@ struct tool_ctx { + struct tool_options { + int debug; + int verbose; ++ int raw; + + enum tcurl_http_method method; + const char *socket_path; +@@ -173,6 +174,13 @@ prepare_requests(TALLOC_CTX *mem_ctx, + goto done; + } + ++ if (opts->raw) { ++ ret = tcurl_req_enable_rawoutput(requests[i]); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + i++; + } + +@@ -270,6 +278,7 @@ int main(int argc, const char *argv[]) + { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL }, + { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL }, + { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, ++ { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL }, + { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL }, + POPT_TABLEEND + }; +-- +2.9.3 + diff --git a/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch b/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch new file mode 100644 index 0000000..d1e5926 --- /dev/null +++ b/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch @@ -0,0 +1,62 @@ +From 4a0d05defd8da2fb7e618e485909b9807b83acbf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Mon, 27 Feb 2017 12:58:06 +0100 +Subject: [PATCH 77/90] tcurl test: add support for tls settings + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 886e0f75e6f4c7877a23a3625f8a20c09109b09d) +--- + src/tests/tcurl_test_tool.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 7d3bc19f0ec7e118e251247536d25c58fe009f54..9cec000fbf2e4eca2fdc5213c8b3b4cb10f1df1b 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -37,9 +37,14 @@ struct tool_options { + int debug; + int verbose; + int raw; ++ int tls; ++ int verify_peer; ++ int verify_host; + + enum tcurl_http_method method; + const char *socket_path; ++ const char *capath; ++ const char *cacert; + }; + + static void request_done(struct tevent_req *req) +@@ -181,6 +186,14 @@ prepare_requests(TALLOC_CTX *mem_ctx, + } + } + ++ if (opts->tls) { ++ ret = tcurl_req_verify_peer(requests[i], opts->capath, opts->cacert, ++ opts->verify_peer, opts->verify_host); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + i++; + } + +@@ -280,6 +293,12 @@ int main(int argc, const char *argv[]) + { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, + { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL }, + { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL }, ++ /* TLS */ ++ { "tls", '\0', POPT_ARG_NONE, &opts.tls, '\0', "Enable TLS", NULL }, ++ { "verify-peer", '\0', POPT_ARG_NONE, &opts.verify_peer, '\0', "Verify peer when TLS is enabled", NULL }, ++ { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL }, ++ { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL }, ++ { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL }, + POPT_TABLEEND + }; + +-- +2.9.3 + diff --git a/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch b/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch new file mode 100644 index 0000000..fd6ce23 --- /dev/null +++ b/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch @@ -0,0 +1,112 @@ +From 1c543722b2b1c55b06c3cc02ace987fc68bc26d7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 28 Feb 2017 13:32:31 +0100 +Subject: [PATCH 78/90] tcurl: add support for http basic auth + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit c2ea75da72b426d98ba489039e220d417bfb4c2a) +--- + src/tests/tcurl_test_tool.c | 14 ++++++++++++++ + src/util/tev_curl.c | 24 ++++++++++++++++++++++++ + src/util/tev_curl.h | 15 +++++++++++++++ + 3 files changed, 53 insertions(+) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 9cec000fbf2e4eca2fdc5213c8b3b4cb10f1df1b..4ceef8e06040ea0abd4d112a5b7845f436c69488 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -45,6 +45,9 @@ struct tool_options { + const char *socket_path; + const char *capath; + const char *cacert; ++ ++ const char *username; ++ const char *password; + }; + + static void request_done(struct tevent_req *req) +@@ -194,6 +197,14 @@ prepare_requests(TALLOC_CTX *mem_ctx, + } + } + ++ if (opts->username != NULL && opts->password != NULL) { ++ ret = tcurl_req_http_basic_auth(requests[i], opts->username, ++ opts->password); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + i++; + } + +@@ -299,6 +310,9 @@ int main(int argc, const char *argv[]) + { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL }, + { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL }, + { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL }, ++ /* BASIC AUTH */ ++ { "username", '\0', POPT_ARG_STRING, &opts.username, '\0', "Username for basic authentication", NULL }, ++ { "password", '\0', POPT_ARG_STRING, &opts.password, '\0', "Password for basic authentication", NULL }, + POPT_TABLEEND + }; + +diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c +index c155f4c038d4215933ee30d41c694ad4a14ae132..8faf07c714b636a0351be365597de68d2f68a1be 100644 +--- a/src/util/tev_curl.c ++++ b/src/util/tev_curl.c +@@ -1092,3 +1092,27 @@ errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req, + + return EOK; + } ++ ++errno_t tcurl_req_http_basic_auth(struct tcurl_request *tcurl_req, ++ const char *username, ++ const char *password) ++{ ++ errno_t ret; ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_USERNAME, username); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ ret = tcurl_set_option(tcurl_req, CURLOPT_PASSWORD, password); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ return EOK; ++} +diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h +index 933abcb9b531412737e8fcf391644d828b125cf8..c733127b3686b5665f53cf53ea72674e0d7af64e 100644 +--- a/src/util/tev_curl.h ++++ b/src/util/tev_curl.h +@@ -243,4 +243,19 @@ errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req, + const char *cert, + const char *key); + ++/** ++ * @brief Force HTTP basic authentication with @username and @password. ++ * ++ * @param[in] tcurl_request ++ * @param[in] username ++ * @param[in] password ++ * ++ * @returns errno code ++ * ++ * @see tcurl_http ++ */ ++errno_t tcurl_req_http_basic_auth(struct tcurl_request *tcurl_req, ++ const char *username, ++ const char *password); ++ + #endif /* __TEV_CURL_H */ +-- +2.9.3 + diff --git a/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch b/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch new file mode 100644 index 0000000..ac764da --- /dev/null +++ b/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch @@ -0,0 +1,63 @@ +From 8047207b470ea417b11919e84931a751485d2326 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 10 Mar 2017 12:11:12 +0100 +Subject: [PATCH 79/90] tcurl test: allow to set custom headers + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit d1ed11fc50922aab2332758a9300f3fbf814f112) +--- + src/tests/tcurl_test_tool.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 4ceef8e06040ea0abd4d112a5b7845f436c69488..63a3e26b561781795873c2a4d72ac071a4da9939 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -40,6 +40,7 @@ struct tool_options { + int tls; + int verify_peer; + int verify_host; ++ const char **headers; + + enum tcurl_http_method method; + const char *socket_path; +@@ -121,13 +122,14 @@ prepare_requests(TALLOC_CTX *mem_ctx, + size_t *_num_requests) + { + struct tcurl_request **requests; ++ struct sss_iobuf *body; ++ const char **headers; + const char *arg; + const char *url; +- struct sss_iobuf *body; + errno_t ret; + size_t i; + +- static const char *headers[] = { ++ static const char *default_headers[] = { + "Content-type: application/octet-stream", + NULL, + }; +@@ -137,6 +139,8 @@ prepare_requests(TALLOC_CTX *mem_ctx, + return ENOMEM; + } + ++ headers = opts->headers == NULL ? default_headers : opts->headers; ++ + i = 0; + while ((arg = poptGetArg(pc)) != NULL) { + if (i >= MAXREQ) { +@@ -302,6 +306,9 @@ int main(int argc, const char *argv[]) + { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL }, + { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL }, + { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL }, ++#ifdef POPT_ARG_ARGV ++ { "header", 'h', POPT_ARG_ARGV, &opts.headers, '\0', "Add HTTP header", NULL }, ++#endif + { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL }, + { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL }, + /* TLS */ +-- +2.9.3 + diff --git a/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch b/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch new file mode 100644 index 0000000..8635100 --- /dev/null +++ b/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch @@ -0,0 +1,53 @@ +From f63d4b3749fd76796a26f1c1f07bdddcb681a768 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Mon, 13 Mar 2017 13:30:48 +0100 +Subject: [PATCH 80/90] tcurl test: add support for client certificate + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit ae6b11229d9961e26922918183c7c1de7780b8d6) +--- + src/tests/tcurl_test_tool.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c +index 63a3e26b561781795873c2a4d72ac071a4da9939..fbc2790357b131ebb21b4be041688e5f699d73e7 100644 +--- a/src/tests/tcurl_test_tool.c ++++ b/src/tests/tcurl_test_tool.c +@@ -47,6 +47,9 @@ struct tool_options { + const char *capath; + const char *cacert; + ++ const char *clientcert; ++ const char *clientkey; ++ + const char *username; + const char *password; + }; +@@ -201,6 +204,14 @@ prepare_requests(TALLOC_CTX *mem_ctx, + } + } + ++ if (opts->clientcert != NULL) { ++ ret = tcurl_req_set_client_cert(requests[i], opts->clientcert, ++ opts->clientkey); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + if (opts->username != NULL && opts->password != NULL) { + ret = tcurl_req_http_basic_auth(requests[i], opts->username, + opts->password); +@@ -317,6 +328,8 @@ int main(int argc, const char *argv[]) + { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL }, + { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL }, + { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL }, ++ { "clientcert", '\0', POPT_ARG_STRING, &opts.clientcert, '\0', "Path to client's certificate", NULL }, ++ { "clientkey", '\0', POPT_ARG_STRING, &opts.clientkey, '\0', "Path to client's private key", NULL }, + /* BASIC AUTH */ + { "username", '\0', POPT_ARG_STRING, &opts.username, '\0', "Username for basic authentication", NULL }, + { "password", '\0', POPT_ARG_STRING, &opts.password, '\0', "Password for basic authentication", NULL }, +-- +2.9.3 + diff --git a/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch b/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch new file mode 100644 index 0000000..f804036 --- /dev/null +++ b/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch @@ -0,0 +1,115 @@ +From c9358747b25b257d82b050967812e54860fe7685 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 28 Mar 2017 15:24:01 +0200 +Subject: [PATCH 81/90] ci: do not build secrets on rhel6 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We require newer libcurl version than is available on rhel6. We don't +ship secrets responder in rhel6 so we just disable its build. + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 6698d40512e55e7c2d03e14c227c51b1edc77ffa) +--- + contrib/ci/configure.sh | 1 + + contrib/sssd.spec.in | 15 +++++++++++++++ + src/tests/intg/test_secrets.py | 4 ++++ + 3 files changed, 20 insertions(+) + +diff --git a/contrib/ci/configure.sh b/contrib/ci/configure.sh +index 7590743c2aa5fe31bcdf1a3e92a3f482dbec699b..9d18d0c187561a2dc3bc47d3e8913626e7ff3046 100644 +--- a/contrib/ci/configure.sh ++++ b/contrib/ci/configure.sh +@@ -38,6 +38,7 @@ if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-6.*- || + "--disable-cifs-idmap-plugin" + "--with-syslog=syslog" + "--without-python3-bindings" ++ "--without-secrets" + "--without-kcm" + ) + fi +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index af14d4e3d6b9ffeb4696f1517113b8daa575cb99..39a974edebba3dbcd7625d1729b4a7330eaa8a27 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -112,6 +112,12 @@ + %global enable_systemtap_opt --enable-systemtap + %endif + ++%if (0%{?fedora} || 0%{?epel} >= 7) ++ %global with_secrets 1 ++%else ++ %global with_secret_responder --without-secrets ++%endif ++ + %if (0%{?fedora} >= 23 || 0%{?rhel} >= 7) + %global with_kcm 1 + %global with_kcm_option --with-kcm +@@ -220,8 +226,10 @@ BuildRequires: libsmbclient-devel + %if (0%{?enable_systemtap} == 1) + BuildRequires: systemtap-sdt-devel + %endif ++%if (0%{?with_secrets} == 1) + BuildRequires: http-parser-devel + BuildRequires: jansson-devel ++%endif + BuildRequires: libuuid-devel + BuildRequires: libcurl-devel + +@@ -727,6 +735,7 @@ autoreconf -ivf + %{?with_python3_option} \ + %{?enable_polkit_rules_option} \ + %{?enable_systemtap_opt} \ ++ %{?with_secret_responder} \ + %{?with_kcm_option} \ + %{?experimental} + +@@ -865,7 +874,9 @@ done + %{_libexecdir}/%{servicename}/sssd_nss + %{_libexecdir}/%{servicename}/sssd_pam + %{_libexecdir}/%{servicename}/sssd_autofs ++%if (0%{?with_secrets} == 1) + %{_libexecdir}/%{servicename}/sssd_secrets ++%endif + %{_libexecdir}/%{servicename}/sssd_ssh + %{_libexecdir}/%{servicename}/sssd_sudo + %{_libexecdir}/%{servicename}/p11_child +@@ -900,7 +911,9 @@ done + %dir %{_localstatedir}/cache/krb5rcache + %attr(700,sssd,sssd) %dir %{dbpath} + %attr(755,sssd,sssd) %dir %{mcpath} ++%if (0%{?with_secrets} == 1) + %attr(700,root,root) %dir %{secdbpath} ++%endif + %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd + %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group + %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups +@@ -933,7 +946,9 @@ done + %{_mandir}/man5/sssd.conf.5* + %{_mandir}/man5/sssd-simple.5* + %{_mandir}/man5/sssd-sudo.5* ++%if (0%{?with_secrets} == 1) + %{_mandir}/man5/sssd-secrets.5* ++%endif + %{_mandir}/man5/sss_rpcidmapd.5* + %{_mandir}/man8/sssd.8* + %{_mandir}/man8/sss_cache.8* +diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py +index d71c1904558cc6f8a6eee36c4049582705bc30ac..202f43e61cb0e4986394ad2b32da5abdcb0be3e9 100644 +--- a/src/tests/intg/test_secrets.py ++++ b/src/tests/intg/test_secrets.py +@@ -46,6 +46,10 @@ def create_sssd_secrets_fixture(request): + raise Exception("failed to regenerate confdb") + + resp_path = os.path.join(config.LIBEXEC_PATH, "sssd", "sssd_secrets") ++ if not os.access(resp_path, os.X_OK): ++ # It would be cleaner to use pytest.mark.skipif on the package level ++ # but upstream insists on supporting RHEL-6. ++ pytest.skip("No Secrets responder, skipping") + + secpid = os.fork() + assert secpid >= 0 +-- +2.9.3 + diff --git a/SOURCES/0082-build-make-curl-required-by-secrets.patch b/SOURCES/0082-build-make-curl-required-by-secrets.patch new file mode 100644 index 0000000..0880b3c --- /dev/null +++ b/SOURCES/0082-build-make-curl-required-by-secrets.patch @@ -0,0 +1,65 @@ +From 1ea81a335baa08746df7daf2707c070271990937 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 22 Mar 2017 12:32:31 +0100 +Subject: [PATCH 82/90] build: make curl required by secrets +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Also remove --disable-libcurl since it doesn't make sense. + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 793f2573b2beaf8b48eab850429482acf68ec2b1) +--- + configure.ac | 6 +++++- + src/external/libcurl.m4 | 16 ++-------------- + 2 files changed, 7 insertions(+), 15 deletions(-) + +diff --git a/configure.ac b/configure.ac +index cf5e2557ef0a1bd6374200aa33abea6c509d03aa..80d8ea9ff5785b0d76edbb04f454d0dd8c8a1e6d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -201,9 +201,13 @@ if test x$with_secrets = xyes; then + fi + + if test x$with_kcm = xyes; then +- m4_include([src/external/libcurl.m4]) + m4_include([src/external/libuuid.m4]) + fi ++ ++if test x$with_kcm = xyes -o x$with_secrets = xyes; then ++ m4_include([src/external/libcurl.m4]) ++fi ++ + # This variable is defined by external/libcurl.m4, but conditionals + # must be always evaluated + AM_CONDITIONAL([BUILD_WITH_LIBCURL], +diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4 +index b420b04ad806bd1251f086b773ffe480d39f8bd3..42be308cd1e4b04e736daf887be9b75ea92db80e 100644 +--- a/src/external/libcurl.m4 ++++ b/src/external/libcurl.m4 +@@ -1,17 +1,5 @@ +-AC_ARG_ENABLE([curl], +- [AS_HELP_STRING([--disable-curl-support], +- [do not build with libcurl support])], +- [enable_libcurl=$enableval], +- [enable_libcurl=yes]) +- +-found_libcurl="no" +-AS_IF([test x$enable_libcurl = xyes], +- [PKG_CHECK_MODULES([CURL], +- [libcurl], +- [found_libcurl=yes], +- [AC_MSG_ERROR([ +-The libcurl development library was not found.]) +- ])]) ++PKG_CHECK_MODULES([CURL], [libcurl], [found_libcurl=yes], ++ [AC_MSG_ERROR([The libcurl development library was not found.])]) + + AS_IF([test x"$found_libcurl" = xyes], + CFLAGS="$CFLAGS $CURL_CFLAGS" +-- +2.9.3 + diff --git a/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch b/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch new file mode 100644 index 0000000..7292536 --- /dev/null +++ b/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch @@ -0,0 +1,459 @@ +From a53c4afd13d92572b8c0ebb93d0dbe3f7c7bc680 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 22 Feb 2017 10:38:56 +0100 +Subject: [PATCH 83/90] secrets: use tcurl in proxy provider + +We switch from http-parser to libcurl for an http client. This gaves us many +features for free such as tls and http basic authentication support instead +of implementing it on our own. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3192 + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit df99d709c8cbef3c378c111944d83b7345e4c1ea) +--- + Makefile.am | 3 + + src/responder/secrets/providers.c | 20 +++ + src/responder/secrets/proxy.c | 246 ++++++++++++++++++++++----------- + src/responder/secrets/secsrv_private.h | 5 + + 4 files changed, 191 insertions(+), 83 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 573b37c52fdeab1add4ea057e1e1844ea4d348a5..4a414f77df999b8b1d81f663fcc18dbd2d6d2dc4 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1486,6 +1486,8 @@ sssd_secrets_SOURCES = \ + src/responder/secrets/local.c \ + src/responder/secrets/proxy.c \ + src/util/sss_sockets.c \ ++ src/util/sss_iobuf.c \ ++ src/util/tev_curl.c \ + $(SSSD_RESPONDER_OBJ) \ + $(SSSD_RESOLV_OBJ) \ + $(NULL) +@@ -1497,6 +1499,7 @@ sssd_secrets_LDADD = \ + $(SYSTEMD_DAEMON_LIBS) \ + $(CARES_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ ++ $(CURL_LIBS) \ + $(NULL) + endif + +diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c +index 94831c73036d269addca45c0117811a2c68873fd..80a443d91135447ec8ce8d424b692a6d7e26a907 100644 +--- a/src/responder/secrets/providers.c ++++ b/src/responder/secrets/providers.c +@@ -22,6 +22,7 @@ + #include "responder/secrets/secsrv_private.h" + #include "responder/secrets/secsrv_local.h" + #include "responder/secrets/secsrv_proxy.h" ++#include "util/sss_iobuf.h" + #include + + typedef int (*url_mapper_fn)(struct sec_req_ctx *secreq, +@@ -387,6 +388,25 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply, + return EOK; + } + ++errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx, ++ struct sec_data *reply, ++ int response_code, ++ struct sss_iobuf *response) ++{ ++ DEBUG(SSSDBG_TRACE_LIBS, "HTTP reply %d\n", response_code); ++ ++ reply->data = (char *)sss_iobuf_get_data(response); ++ reply->length = sss_iobuf_get_len(response); ++ ++ talloc_steal(mem_ctx, reply->data); ++ ++ if (reply->data == NULL) { ++ return EINVAL; ++ } ++ ++ return EOK; ++} ++ + enum sec_http_status_codes sec_errno_to_http_status(errno_t err) + { + DEBUG(SSSDBG_TRACE_LIBS, "Request errno: %d\n", err); +diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c +index 3ed03e6086d0de0f6f80de227ffc65ef4067db4f..fe2f0134e233d9a98f499fe563abe0af69762514 100644 +--- a/src/responder/secrets/proxy.c ++++ b/src/responder/secrets/proxy.c +@@ -23,10 +23,15 @@ + #include "util/crypto/sss_crypto.h" + #include "resolv/async_resolv.h" + #include "util/sss_sockets.h" ++#include "util/sss_iobuf.h" ++#include "util/tev_curl.h" ++ ++#define SEC_PROXY_TIMEOUT 5 + + struct proxy_context { + struct resolv_ctx *resctx; + struct confdb_ctx *cdb; ++ struct tcurl_ctx *tcurl; + }; + + enum proxy_auth_type { +@@ -216,103 +221,177 @@ int proxy_sec_map_url(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq, + return EOK; + } + +-int proxy_sec_map_headers(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq, +- struct proxy_cfg *pcfg, char **req_headers) ++static errno_t proxy_http_append_header(TALLOC_CTX *mem_ctx, ++ const char *name, ++ const char *value, ++ const char ***_headers, ++ size_t *_num_headers) + { +- int ret; +- +- for (int i = 0; i < secreq->num_headers; i++) { +- bool forward = false; +- for (int j = 0; pcfg->fwd_headers[j]; j++) { +- if (strcasecmp(secreq->headers[i].name, +- pcfg->fwd_headers[j]) == 0) { +- forward = true; ++ const char **headers = *_headers; ++ size_t num_headers = *_num_headers; ++ ++ num_headers++; ++ headers = talloc_realloc(mem_ctx, headers, const char *, ++ num_headers + 1); ++ if (headers == NULL) { ++ return ENOMEM; ++ } ++ ++ headers[num_headers - 1] = talloc_asprintf(headers, "%s: %s", name, value); ++ if (headers[num_headers - 1] == NULL) { ++ return ENOMEM; ++ } ++ ++ headers[num_headers] = NULL; ++ ++ *_headers = headers; ++ *_num_headers = num_headers; ++ ++ return EOK; ++} ++ ++static const char ** ++proxy_http_create_headers(TALLOC_CTX *mem_ctx, ++ struct sec_req_ctx *secreq, ++ struct proxy_cfg *pcfg) ++{ ++ TALLOC_CTX *tmp_ctx; ++ const char **headers; ++ size_t num_headers; ++ errno_t ret; ++ int i, j; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return NULL; ++ } ++ ++ headers = talloc_zero_array(tmp_ctx, const char *, 1); ++ if (headers == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ num_headers = 0; ++ for (i = 0; i < secreq->num_headers; i++) { ++ for (j = 0; pcfg->fwd_headers[j]; j++) { ++ if (strcasecmp(secreq->headers[i].name, pcfg->fwd_headers[j]) == 0) { ++ DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s: %s\n", ++ secreq->headers[i].name, secreq->headers[i].value); ++ ++ ret = proxy_http_append_header(tmp_ctx, secreq->headers[i].name, ++ secreq->headers[i].value, ++ &headers, &num_headers); ++ if (ret != EOK) { ++ goto done; ++ } ++ + break; + } + } +- if (forward) { +- DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s:%s\n", +- secreq->headers[i].name, secreq->headers[i].value); +- +- ret = sec_http_append_header(mem_ctx, req_headers, +- secreq->headers[i].name, +- secreq->headers[i].value); +- if (ret) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Couldn't append header %s\n", secreq->headers[i].name); +- return ret; +- } +- } + } + + if (pcfg->auth_type == PAT_HEADER) { +- DEBUG(SSSDBG_TRACE_LIBS, +- "Forwarding header %s\n", pcfg->auth.header.name); ++ DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s\n", ++ pcfg->auth.header.name); + +- ret = sec_http_append_header(mem_ctx, req_headers, +- pcfg->auth.header.name, +- pcfg->auth.header.value); +- if (ret) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Couldn't append header %s\n", pcfg->auth.header.name); +- return ret; ++ ret = proxy_http_append_header(tmp_ctx, pcfg->auth.header.name, ++ pcfg->auth.header.value, ++ &headers, &num_headers); ++ if (ret != EOK) { ++ goto done; + } + } + +- return EOK; ++ talloc_steal(mem_ctx, headers); ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ if (ret != EOK) { ++ return NULL; ++ } ++ ++ return headers; + } + +-static int proxy_http_create_request(TALLOC_CTX *mem_ctx, +- struct sec_req_ctx *secreq, +- struct proxy_cfg *pcfg, +- const char *http_uri, +- struct sec_data **http_req) ++static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx, ++ struct sec_req_ctx *secreq, ++ struct proxy_cfg *pcfg, ++ const char *url, ++ struct tcurl_request **_tcurl_req) + { +- struct sec_data *req; +- int ret; ++ TALLOC_CTX *tmp_ctx; ++ struct tcurl_request *tcurl_req; ++ enum tcurl_http_method method; ++ struct sss_iobuf *body; ++ const char **headers; ++ errno_t ret; + +- req = talloc_zero(mem_ctx, struct sec_data); +- if (!req) return ENOMEM; ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; ++ } + +- /* Request-Line */ +- req->data = talloc_asprintf(req, "%s %s HTTP/1.1\r\n", +- http_method_str(secreq->method), http_uri); +- if (!req->data) { ++ headers = proxy_http_create_headers(tmp_ctx, secreq, pcfg); ++ if (headers == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct HTTP headers!\n"); + ret = ENOMEM; + goto done; + } + +- /* Headers */ +- ret = proxy_sec_map_headers(req, secreq, pcfg, &req->data); +- if (ret) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't map headers\n"); ++ body = sss_iobuf_init_readonly(tmp_ctx, (uint8_t *)secreq->body.data, ++ secreq->body.length); ++ if (body == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create HTTP body!\n"); ++ ret = ENOMEM; + goto done; + } + +- /* CRLF separator before body */ +- req->data = talloc_strdup_append_buffer(req->data, "\r\n"); +- +- req->length = strlen(req->data); ++ switch (secreq->method) { ++ case HTTP_GET: ++ method = TCURL_HTTP_GET; ++ break; ++ case HTTP_PUT: ++ method = TCURL_HTTP_PUT; ++ break; ++ case HTTP_POST: ++ method = TCURL_HTTP_POST; ++ break; ++ case HTTP_DELETE: ++ method = TCURL_HTTP_DELETE; ++ break; ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected HTTP method: %d\n", ++ secreq->method); ++ ret = EINVAL; ++ goto done; ++ } + +- /* Message-Body */ +- if (secreq->body.length > 0) { +- req->data = talloc_realloc_size(req, req->data, +- req->length + secreq->body.length); +- if (!req->data) { +- ret = ENOMEM; +- goto done; +- } ++ tcurl_req = tcurl_http(tmp_ctx, method, NULL, url, headers, body); ++ if (tcurl_req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create TCURL request!\n"); ++ ret = ENOMEM; ++ goto done; ++ } + +- memcpy(&req->data[req->length], +- secreq->body.data, secreq->body.length); +- req->length += secreq->body.length; ++ /* TCURL will return response buffer also with headers. */ ++ ret = tcurl_req_enable_rawoutput(tcurl_req); ++ if (ret != EOK) { ++ goto done; + } + +- *http_req = req; ++ talloc_steal(tcurl_req, body); ++ *_tcurl_req = talloc_steal(mem_ctx, tcurl_req); ++ + ret = EOK; + + done: +- if (ret) talloc_free(req); ++ talloc_free(tmp_ctx); + return ret; + } + +@@ -911,8 +990,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx, + { + struct tevent_req *req, *subreq; + struct proxy_secret_state *state; ++ struct tcurl_request *tcurl_req; + struct proxy_context *pctx; +- struct sec_data *http_req; + char *http_uri; + int ret; + +@@ -942,9 +1021,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx, + goto done; + } + +- + ret = proxy_http_create_request(state, state->secreq, state->pcfg, +- http_uri, &http_req); ++ http_uri, &tcurl_req); + if (ret) { + DEBUG(SSSDBG_CRIT_FAILURE, + "proxy_http_create_request failed [%d]: %s\n", +@@ -952,10 +1030,9 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx, + goto done; + } + +- +- subreq = proxy_http_req_send(pctx, state, ev, state->secreq, +- http_uri, http_req); +- if (!subreq) { ++ subreq = tcurl_request_send(mem_ctx, ev, pctx->tcurl, tcurl_req, ++ SEC_PROXY_TIMEOUT); ++ if (subreq == NULL) { + ret = ENOMEM; + goto done; + } +@@ -981,32 +1058,30 @@ static void proxy_secret_req_done(struct tevent_req *subreq) + { + struct tevent_req *req; + struct proxy_secret_state *state; +- struct proxy_http_reply *reply = NULL; ++ struct sss_iobuf *response; ++ int http_code; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct proxy_secret_state); + +- ret = proxy_http_req_recv(subreq, state, &reply); ++ ret = tcurl_request_recv(state, subreq, &response, &http_code); + talloc_zfree(subreq); + + if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "proxy_http request failed [%d]: %s\n", ++ DEBUG(SSSDBG_OP_FAILURE, "proxy_http request failed [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + +- ret = sec_http_reply_with_headers(state->secreq, &state->secreq->reply, +- reply->status_code, reply->reason_phrase, +- reply->headers, reply->num_headers, +- &reply->body); ++ ret = sec_http_reply_iobuf(state->secreq, &state->secreq->reply, ++ http_code, response); + if (ret == EOK) { + tevent_req_done(req); + } else { + DEBUG(SSSDBG_OP_FAILURE, +- "sec_http_reply_with_headers request failed [%d]: %s\n", ++ "sec_http_reply_iobuf request failed [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + } +@@ -1034,6 +1109,11 @@ int proxy_secrets_provider_handle(struct sec_ctx *sctx, + + pctx->resctx = sctx->resctx; + pctx->cdb = sctx->rctx->cdb; ++ pctx->tcurl = tcurl_init(pctx, sctx->rctx->ev); ++ if (pctx->tcurl == NULL) { ++ talloc_free(pctx); ++ return ENOMEM; ++ } + + handle->context = pctx; + +diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h +index a8544f656517a17fe4576247779bff4850beaf97..2e68628f61a0a8e79cd48fb5a510221e6fc36c70 100644 +--- a/src/responder/secrets/secsrv_private.h ++++ b/src/responder/secrets/secsrv_private.h +@@ -25,6 +25,7 @@ + #include "config.h" + #include "responder/common/responder.h" + #include "responder/secrets/secsrv.h" ++#include "util/sss_iobuf.h" + #include + + struct sec_kvp { +@@ -129,6 +130,10 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply, + int status_code, const char *reason, + struct sec_kvp *headers, int num_headers, + struct sec_data *body); ++errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx, ++ struct sec_data *reply, ++ int response_code, ++ struct sss_iobuf *response); + enum sec_http_status_codes sec_errno_to_http_status(errno_t err); + + int sec_json_to_simple_secret(TALLOC_CTX *mem_ctx, +-- +2.9.3 + diff --git a/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch b/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch new file mode 100644 index 0000000..7245f94 --- /dev/null +++ b/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch @@ -0,0 +1,612 @@ +From cfb82199afe237b4e892aaf2816db63279d7cb21 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 28 Feb 2017 14:14:40 +0100 +Subject: [PATCH 84/90] secrets: remove http-parser code in proxy provider + +We switche to libcurl in previous patch. This just removes the unused code. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3192 + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 06744bf5a47d5971a338281c8243b11cf72dac90) +--- + src/responder/secrets/proxy.c | 581 ------------------------------------------ + 1 file changed, 581 deletions(-) + +diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c +index fe2f0134e233d9a98f499fe563abe0af69762514..3c495716010ac468c9e2f1fb6356529a8dbdc614 100644 +--- a/src/responder/secrets/proxy.c ++++ b/src/responder/secrets/proxy.c +@@ -395,587 +395,6 @@ done: + return ret; + } + +-struct proxy_http_request { +- struct sec_data *data; +- size_t written; +-}; +- +-struct proxy_http_reply { +- http_parser parser; +- bool complete; +- +- int status_code; +- char *reason_phrase; +- struct sec_kvp *headers; +- int num_headers; +- struct sec_data body; +- +- size_t received; +-}; +- +-struct proxy_http_req_state { +- struct tevent_context *ev; +- +- char *proxyname; +- int port; +- +- struct resolv_hostent *hostent; +- int hostidx; +- +- int sd; +- struct tevent_fd *fde; +- +- struct proxy_http_request request; +- struct proxy_http_reply *reply; +-}; +- +-static int proxy_http_req_state_destroy(void *data); +-static void proxy_http_req_gethostname_done(struct tevent_req *subreq); +-static void proxy_http_req_connect_step(struct tevent_req *req); +-static void proxy_http_req_connect_done(struct tevent_req *subreq); +-static void proxy_fd_handler(struct tevent_context *ev, struct tevent_fd *fde, +- uint16_t flags, void *ptr); +- +-struct tevent_req *proxy_http_req_send(struct proxy_context *pctx, +- TALLOC_CTX *mem_ctx, +- struct tevent_context *ev, +- struct sec_req_ctx *secreq, +- const char *http_uri, +- struct sec_data *http_req) +-{ +- struct proxy_http_req_state *state; +- struct http_parser_url parsed; +- struct tevent_req *req, *subreq; +- int ret; +- +- req = tevent_req_create(mem_ctx, &state, struct proxy_http_req_state); +- if (!req) return NULL; +- +- state->ev = ev; +- state->request.data = http_req; +- state->sd = -1; +- talloc_set_destructor((TALLOC_CTX *)state, +- proxy_http_req_state_destroy); +- +- /* STEP1: reparse URL to get hostname and port */ +- ret = http_parser_parse_url(http_uri, strlen(http_uri), 0, &parsed); +- if (ret) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse URL [%s]: %d: %s\n", +- http_uri, ret, sss_strerror(ret)); +- goto done; +- } +- +- if (!(parsed.field_set & (1 << UF_HOST))) { +- DEBUG(SSSDBG_CRIT_FAILURE, "No UF_HOST flag found\n"); +- ret = EINVAL; +- goto done; +- } +- state->proxyname = +- talloc_strndup(state, +- &http_uri[parsed.field_data[UF_HOST].off], +- parsed.field_data[UF_HOST].len); +- if (!state->proxyname) { +- ret = ENOMEM; +- goto done; +- } +- DEBUG(SSSDBG_TRACE_LIBS, "proxy name: %s\n", state->proxyname); +- +- if (parsed.field_set & (1 << UF_PORT)) { +- state->port = parsed.port; +- } else if (parsed.field_set & (1 << UF_SCHEMA)) { +- uint16_t off = parsed.field_data[UF_SCHEMA].off; +- uint16_t len = parsed.field_data[UF_SCHEMA].len; +- +- if ((len == 5) && +- (strncmp("https", &http_uri[off], len) == 0)) { +- state->port = 443; +- } else if ((len == 4) && +- (strncmp("http", &http_uri[off], len) == 0)) { +- state->port = 80; +- } +- } +- DEBUG(SSSDBG_TRACE_LIBS, "proxy port: %d\n", state->port); +- +- /* STEP2: resolve hostname first */ +- subreq = resolv_gethostbyname_send(state, ev, pctx->resctx, +- state->proxyname, IPV4_FIRST, +- default_host_dbs); +- if (subreq == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- tevent_req_set_callback(subreq, proxy_http_req_gethostname_done, req); +- +- return req; +- +-done: +- if (ret == EOK) { +- tevent_req_done(req); +- } else { +- tevent_req_error(req, ret); +- } +- tevent_req_post(req, ev); +- +- return req; +-} +- +-static void proxy_http_req_gethostname_done(struct tevent_req *subreq) +-{ +- struct tevent_req *req; +- struct proxy_http_req_state *state; +- int resolv_status; +- int ret; +- +- req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct proxy_http_req_state); +- +- ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL, +- &state->hostent); +- talloc_zfree(subreq); +- if (ret != EOK) { +- if (ret == ENOENT) { +- /* Empty result, just quit */ +- DEBUG(SSSDBG_TRACE_INTERNAL, "No hostent found\n"); +- } else { +- DEBUG(SSSDBG_OP_FAILURE, +- "Could not resolve fqdn for this machine, error [%d]: %s, " +- "resolver returned: [%d]: %s\n", ret, strerror(ret), +- resolv_status, resolv_strerror(resolv_status)); +- } +- goto done; +- } +- +- /* EOK */ +- DEBUG(SSSDBG_TRACE_INTERNAL, "Found fqdn: %s\n", state->hostent->name); +- +- /* STEP3: connect to one of the servers */ +- proxy_http_req_connect_step(req); +- return; +- +-done: +- if (ret == EOK) { +- tevent_req_done(req); +- } else { +- tevent_req_error(req, ret); +- } +-} +- +-static void proxy_http_req_connect_step(struct tevent_req *req) +-{ +- struct proxy_http_req_state *state; +- struct sockaddr_storage *sockaddr; +- char *ipaddr; +- struct tevent_req *subreq; +- int ret; +- +- state = tevent_req_data(req, struct proxy_http_req_state); +- +- if (!state->hostent->addr_list[state->hostidx]) { +- DEBUG(SSSDBG_CRIT_FAILURE, "No more addresses to try.\n"); +- ret = ERR_SEC_NO_PROXY; +- goto done; +- } +- +- sockaddr = resolv_get_sockaddr_address_index(state, state->hostent, +- state->port, state->hostidx); +- if (sockaddr == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "resolv_get_sockaddr_address() failed\n"); +- ret = EIO; +- goto done; +- } +- +- if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) { +- ipaddr = resolv_get_string_address_index(state, state->hostent, +- state->hostidx); +- if (!ipaddr) { +- ret = EFAULT; +- goto done; +- } +- DEBUG(SSSDBG_TRACE_FUNC, "Connecting to %s:%d\n", +- ipaddr, state->port); +- } +- +- /* increase idx for next attempt */ +- state->hostidx++; +- +- subreq = sssd_async_socket_init_send(state, state->ev, sockaddr, +- sizeof(struct sockaddr_storage), +- SEC_NET_TIMEOUT); +- if (!subreq) { +- ret = EIO; +- goto done; +- } +- tevent_req_set_callback(subreq, proxy_http_req_connect_done, req); +- return; +- +-done: +- if (ret == EOK) { +- tevent_req_done(req); +- } else { +- tevent_req_error(req, ret); +- } +-} +- +-static void proxy_http_req_connect_done(struct tevent_req *subreq) +-{ +- struct tevent_req *req; +- struct proxy_http_req_state *state; +- int ret; +- +- req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct proxy_http_req_state); +- +- ret = sssd_async_socket_init_recv(subreq, &state->sd); +- talloc_zfree(subreq); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "sssd_async_socket_init request failed: [%d]: %s.\n", +- ret, sss_strerror(ret)); +- +- /* try next server if any */ +- proxy_http_req_connect_step(req); +- return; +- } +- +- /* EOK */ +- DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s\n", state->hostent->name); +- +- state->fde = tevent_add_fd(state->ev, state, state->sd, +- TEVENT_FD_WRITE, proxy_fd_handler, +- req); +- if (!state->fde) { +- ret = EIO; +- goto done; +- } +- +- return; +- +-done: +- if (ret == EOK) { +- tevent_req_done(req); +- } else { +- tevent_req_error(req, ret); +- } +-} +- +- +-int proxy_http_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, +- struct proxy_http_reply **reply) +-{ +- struct proxy_http_req_state *state = +- tevent_req_data(req, struct proxy_http_req_state); +- +- TEVENT_REQ_RETURN_ON_ERROR(req); +- +- *reply = talloc_move(mem_ctx, &state->reply); +- +- return EOK; +-} +- +-static int proxy_http_req_state_destroy(void *data) +-{ +- struct proxy_http_req_state *state = +- talloc_get_type(data, struct proxy_http_req_state); +- +- if (!state) return 0; +- +- if (state->sd != -1) { +- DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd); +- close(state->sd); +- state->sd = -1; +- } +- +- return 0; +-} +- +-static int proxy_wire_send(int fd, struct proxy_http_request *req) +-{ +- struct sec_data data; +- int ret; +- +- data.data = req->data->data + req->written; +- data.length = req->data->length - req->written; +- +- ret = sec_send_data(fd, &data); +- if (ret != EOK && ret != EAGAIN) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "sec_send_data failed [%d]: %s\n", ret, sss_strerror(ret)); +- return ret; +- } +- +- req->written = req->data->length - data.length; +- return ret; +-} +- +-static void proxy_fd_send(void *data) +-{ +- struct proxy_http_req_state *state; +- struct tevent_req * req; +- int ret; +- +- req = talloc_get_type(data, struct tevent_req); +- state = tevent_req_data(req, struct proxy_http_req_state); +- +- ret = proxy_wire_send(state->sd, &state->request); +- if (ret == EAGAIN) { +- /* not all data was sent, loop again */ +- return; +- } +- if (ret != EOK) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting!\n"); +- tevent_req_error(req, ret); +- return; +- } +- +- /* ok all sent, wait for reply now */ +- TEVENT_FD_NOT_WRITEABLE(state->fde); +- TEVENT_FD_READABLE(state->fde); +- return; +-} +- +-static bool ph_received_data(struct proxy_http_reply *reply, size_t length) +-{ +- reply->received += length; +- if (reply->received > SEC_REQUEST_MAX_SIZE) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Request too big, aborting!\n"); +- return true; +- } +- return false; +-} +- +-static void ph_append_string(TALLOC_CTX *memctx, char **dest, +- const char *src, size_t len) +-{ +- if (*dest) { +- *dest = talloc_strndup_append_buffer(*dest, src, len); +- } else { +- *dest = talloc_strndup(memctx, src, len); +- } +-} +- +-static int ph_on_message_begin(http_parser *parser) +-{ +- DEBUG(SSSDBG_TRACE_INTERNAL, "HTTP Message parsing begins\n"); +- return 0; +-} +- +-#if ((HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)) +-static int ph_on_status(http_parser *parser, const char *at, size_t length) +-{ +- struct proxy_http_reply *reply = +- talloc_get_type(parser->data, struct proxy_http_reply); +- +- if (ph_received_data(reply, length)) return -1; +- +- ph_append_string(reply, &reply->reason_phrase, at, length); +- if (!reply->reason_phrase) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to store reason phrase, aborting client!\n"); +- return -1; +- } +- +- return 0; +-} +-#endif +- +-static int ph_on_header_field(http_parser *parser, +- const char *at, size_t length) +-{ +- struct proxy_http_reply *reply = +- talloc_get_type(parser->data, struct proxy_http_reply); +- int n = reply->num_headers; +- +- if (ph_received_data(reply, length)) return -1; +- +- if (!reply->headers) { +- reply->headers = talloc_zero_array(reply, struct sec_kvp, 10); +- } else if ((n % 10 == 0) && +- (reply->headers[n - 1].value)) { +- reply->headers = talloc_realloc(reply, reply->headers, +- struct sec_kvp, n + 10); +- if (reply->headers) { +- memset(&reply->headers[n], 0, sizeof(struct sec_kvp) * 10); +- } +- } +- if (!reply->headers) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to store headers, aborting client!\n"); +- return -1; +- } +- +- if (!n || reply->headers[n - 1].value) { +- /* new field */ +- n++; +- } +- ph_append_string(reply->headers, &reply->headers[n - 1].name, at, length); +- if (!reply->headers[n - 1].name) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to store header name, aborting client!\n"); +- return -1; +- } +- +- return 0; +-} +- +-static int ph_on_header_value(http_parser *parser, +- const char *at, size_t length) +-{ +- struct proxy_http_reply *reply = +- talloc_get_type(parser->data, struct proxy_http_reply); +- int n = reply->num_headers; +- +- if (ph_received_data(reply, length)) return -1; +- +- if (!reply->headers) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Invalid headers pointer, aborting client!\n"); +- return -1; +- } +- +- if (reply->headers[n].name && !reply->headers[n].value) { +- /* we increment on new value */ +- n = ++reply->num_headers; +- } +- +- ph_append_string(reply->headers, &reply->headers[n - 1].value, at, length); +- if (!reply->headers[n - 1].value) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to store header value, aborting client!\n"); +- return -1; +- } +- +- return 0; +-} +- +-static int ph_on_headers_complete(http_parser *parser) +-{ +- /* TODO: if message has no body we should return 1 */ +- return 0; +-} +- +-static int ph_on_body(http_parser *parser, const char *at, size_t length) +-{ +- struct proxy_http_reply *reply = +- talloc_get_type(parser->data, struct proxy_http_reply); +- +- if (ph_received_data(reply, length)) return -1; +- +- /* FIXME: body may be binary */ +- ph_append_string(reply, &reply->body.data, at, length); +- if (!reply->body.data) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to store body, aborting!\n"); +- return -1; +- } +- reply->body.length += length; +- +- return 0; +-} +- +-static int ph_on_message_complete(http_parser *parser) +-{ +- struct proxy_http_reply *reply = +- talloc_get_type(parser->data, struct proxy_http_reply); +- +- reply->status_code = parser->status_code; +- reply->complete = true; +- +- return 0; +-} +- +-static http_parser_settings ph_callbacks = { +- .on_message_begin = ph_on_message_begin, +-#if ((HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)) +- .on_status = ph_on_status, +-#endif +- .on_header_field = ph_on_header_field, +- .on_header_value = ph_on_header_value, +- .on_headers_complete = ph_on_headers_complete, +- .on_body = ph_on_body, +- .on_message_complete = ph_on_message_complete +-}; +- +-static void proxy_fd_recv(void *data) +-{ +- char buffer[SEC_PACKET_MAX_RECV_SIZE]; +- struct sec_data packet = { buffer, +- SEC_PACKET_MAX_RECV_SIZE }; +- struct proxy_http_req_state *state; +- struct tevent_req *req; +- bool must_complete = false; +- int ret; +- +- req = talloc_get_type(data, struct tevent_req); +- state = tevent_req_data(req, struct proxy_http_req_state); +- +- if (!state->reply) { +- /* A new reply */ +- state->reply = talloc_zero(state, struct proxy_http_reply); +- if (!state->reply) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Failed to allocate reply, aborting!\n"); +- tevent_req_error(req, ENOMEM); +- return; +- } +- http_parser_init(&state->reply->parser, HTTP_RESPONSE); +- state->reply->parser.data = state->reply; +- } +- +- ret = sec_recv_data(state->sd, &packet); +- switch (ret) { +- case ENODATA: +- DEBUG(SSSDBG_TRACE_ALL, "Server closed connection.\n"); +- /* if we got no content length and the request is not complete, +- * then 0 length will indicate EOF to the parser, otherwise we +- * have an error */ +- must_complete = true; +- break; +- case EAGAIN: +- DEBUG(SSSDBG_TRACE_ALL, +- "Interrupted before any data could be read, retry later\n"); +- return; +- case EOK: +- /* all fine */ +- break; +- default: +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to receive data (%d, %s), aborting\n", +- ret, sss_strerror(ret)); +- tevent_req_error(req, EIO); +- return; +- } +- +- ret = http_parser_execute(&state->reply->parser, &ph_callbacks, +- packet.data, packet.length); +- if (ret != packet.length) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to parse request, aborting!\n"); +- tevent_req_error(req, EIO); +- return; +- } +- +- if (!state->reply->complete) { +- if (must_complete) { +- tevent_req_error(req, EIO); +- } +- return; +- } +- +- /* do not read anymore, server is done sending */ +- TEVENT_FD_NOT_READABLE(state->fde); +- tevent_req_done(req); +-} +- +-static void proxy_fd_handler(struct tevent_context *ev, struct tevent_fd *fde, +- uint16_t flags, void *data) +-{ +- if (flags & TEVENT_FD_READ) { +- proxy_fd_recv(data); +- } else if (flags & TEVENT_FD_WRITE) { +- proxy_fd_send(data); +- } +-} +- + struct proxy_secret_state { + struct tevent_context *ev; + struct sec_req_ctx *secreq; +-- +2.9.3 + diff --git a/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch b/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch new file mode 100644 index 0000000..dd9bea7 --- /dev/null +++ b/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch @@ -0,0 +1,254 @@ +From d35f47a4e50feeb2b54c1621d0c2f5b15cd275eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 28 Feb 2017 11:47:32 +0100 +Subject: [PATCH 85/90] secrets: allow to configure certificate check + +Some users may want to use TLS with unverified peer (for example if +they use self-signed certificate) or if unverified hostname (if +certificate hostname does not match with the real hostname). On the +other side it may be useful to point to a directory containing custom +certificate authorities. + +This patch add three new options to secrets responder: +verify_peer => peer's certificate must be valid +verify_host => hostnames must match +capath => path to directory containing CA certs +cacert => ca certificate +cert => client certificate +key => client private key + +Resolves: +https://pagure.io/SSSD/sssd/issue/3192 + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 720e1a5b95a953a0f1c8315bbb7c9c1edf9fb417) +--- + src/config/SSSDConfig/__init__.py.in | 6 +++ + src/config/cfg_rules.ini | 6 +++ + src/config/etc/sssd.api.conf | 6 +++ + src/man/sssd-secrets.5.xml | 76 ++++++++++++++++++++++++++++++++++++ + src/responder/secrets/proxy.c | 55 ++++++++++++++++++++++++++ + 5 files changed, 149 insertions(+) + +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index 211338778e81c1c60ffb3cdbc67c9619343d7798..75515ab5c68822538728900482296b9159e1547e 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -137,6 +137,12 @@ option_strings = { + 'forward_headers': _('The list of the headers to forward to the Custodia server together with the request'), + 'username': _('The username to use when authenticating to a Custodia server using basic_auth'), + 'password': _('The password to use when authenticating to a Custodia server using basic_auth'), ++ 'verify_peer': _('If true peer\'s certificate is verified if proxy_url uses https protocol'), ++ 'verify_host': _('If false peer\'s certificate may contain different hostname then proxy_url when https protocol is used'), ++ 'capath': _('Path to directory where certificate authority certificates are stored'), ++ 'cacert': _('Path to file containing server\'s CA certificate'), ++ 'cert': _('Path to file containing client\'s certificate'), ++ 'key': _('Path to file containing client\'s private key'), + + # [provider] + 'id_provider' : _('Identity provider'), +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 1a749db754cedd87f263f7ae596d6f8238bb4357..e47ff33242d6a9e5979fe0eb8eea14c2af28685a 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -265,6 +265,12 @@ option = auth_header_value + option = forward_headers + option = username + option = password ++option = verify_peer ++option = verify_host ++option = capath ++option = cacert ++option = cert ++option = key + + # KCM responder + [rule/allowed_kcm_options] +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index a1a0c2992925a4c7df86832117eec2a0cf7894c9..f86589ecefa0b9e046aba781ded107f8e94395d6 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -114,6 +114,12 @@ auth_header_value = str, None, false + forward_headers = list, None, false + username = str, None, false + password = str, None, false ++verify_peer = bool, None, false ++verify_host = bool, None, false ++capath = str, None, false ++cacert = str, None, false ++cert = str, None, false ++key = str, None, false + + [provider] + #Available provider types +diff --git a/src/man/sssd-secrets.5.xml b/src/man/sssd-secrets.5.xml +index 80e9c405921e1fb46a3d172d9873deebfa5ed2ce..44a86c3fb56a8bdebebd01e9f49ad171986282a4 100644 +--- a/src/man/sssd-secrets.5.xml ++++ b/src/man/sssd-secrets.5.xml +@@ -273,6 +273,82 @@ systemctl enable sssd-secrets.service + + + ++ ++ verify_peer (boolean) ++ ++ ++ Whether peer's certificate should be verified and valid ++ if HTTPS protocol is used with the proxy provider. ++ ++ ++ Default: true ++ ++ ++ ++ ++ verify_host (boolean) ++ ++ ++ Whether peer's hostname must match with hostname in ++ its certificate if HTTPS protocol is used with the ++ proxy provider. ++ ++ ++ Default: true ++ ++ ++ ++ ++ capath (string) ++ ++ ++ Path to directory containing stored certificate authority ++ certificates. System default path is used if this option is ++ not set. ++ ++ ++ Default: not set ++ ++ ++ ++ ++ cacert (string) ++ ++ ++ Path to file containing server's certificate authority ++ certificate. If this option is not set then the CA's ++ certificate is looked up in capath. ++ ++ ++ Default: not set ++ ++ ++ ++ ++ cert (string) ++ ++ ++ Path to file containing client's certificate if required ++ by the server. This file may also contain private key or ++ the private key may be in separate file set with ++ key. ++ ++ ++ Default: not set ++ ++ ++ ++ ++ key (string) ++ ++ ++ Path to file containing client's private key. ++ ++ ++ Default: not set ++ ++ ++ + + + +diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c +index 3c495716010ac468c9e2f1fb6356529a8dbdc614..240a1de1e431d511a1eca24d8b463c37ba893e7b 100644 +--- a/src/responder/secrets/proxy.c ++++ b/src/responder/secrets/proxy.c +@@ -59,6 +59,13 @@ struct proxy_cfg { + struct pat_basic_auth basic; + struct pat_header header; + } auth; ++ ++ char *key; ++ char *cert; ++ char *cacert; ++ char *capath; ++ bool verify_peer; ++ bool verify_host; + }; + + static int proxy_get_config_string(struct proxy_context *pctx, +@@ -129,6 +136,38 @@ static int proxy_sec_get_cfg(struct proxy_context *pctx, + } + } + ++ ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_peer", ++ true, &cfg->verify_peer); ++ if (ret) goto done; ++ DEBUG(SSSDBG_CONF_SETTINGS, "verify_peer: %s\n", ++ (&cfg->verify_peer ? "true" : "false")); ++ ++ ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_host", ++ true, &cfg->verify_host); ++ if (ret) goto done; ++ DEBUG(SSSDBG_CONF_SETTINGS, "verify_host: %s\n", ++ (&cfg->verify_host ? "true" : "false")); ++ ++ ret = proxy_get_config_string(pctx, cfg, false, secreq, ++ "capath", &cfg->capath); ++ if (ret) goto done; ++ DEBUG(SSSDBG_CONF_SETTINGS, "capath: %s\n", cfg->capath); ++ ++ ret = proxy_get_config_string(pctx, cfg, false, secreq, ++ "cacert", &cfg->cacert); ++ if (ret) goto done; ++ DEBUG(SSSDBG_CONF_SETTINGS, "cacert: %s\n", cfg->cacert); ++ ++ ret = proxy_get_config_string(pctx, cfg, false, secreq, ++ "cert", &cfg->cert); ++ if (ret) goto done; ++ DEBUG(SSSDBG_CONF_SETTINGS, "cert: %s\n", cfg->cert); ++ ++ ret = proxy_get_config_string(pctx, cfg, false, secreq, ++ "key", &cfg->key); ++ if (ret) goto done; ++ DEBUG(SSSDBG_CONF_SETTINGS, "key: %s\n", cfg->key); ++ + ret = confdb_get_string_as_list(pctx->cdb, cfg, secreq->cfg_section, + "forward_headers", &cfg->fwd_headers); + if ((ret != 0) && (ret != ENOENT)) goto done; +@@ -385,6 +424,22 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx, + goto done; + } + ++ /* Set TLS settings to verify peer. ++ * This has no effect for HTTP protocol so we can set it anyway. */ ++ ret = tcurl_req_verify_peer(tcurl_req, pcfg->capath, pcfg->cacert, ++ pcfg->verify_peer, pcfg->verify_host); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ /* Set client's certificate if required. */ ++ if (pcfg->cert != NULL) { ++ ret = tcurl_req_set_client_cert(tcurl_req, pcfg->cert, pcfg->key); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + talloc_steal(tcurl_req, body); + *_tcurl_req = talloc_steal(mem_ctx, tcurl_req); + +-- +2.9.3 + diff --git a/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch b/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch new file mode 100644 index 0000000..f048f94 --- /dev/null +++ b/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch @@ -0,0 +1,39 @@ +From 28d590900ab20dec3dc447562aefaa5e2771c48e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 28 Feb 2017 13:58:20 +0100 +Subject: [PATCH 86/90] secrets: support HTTP basic authentication with proxy + provider + +Even though configuration options auth_type = basic, username and password +are read they were not used anywhere prior this patch. + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit af026ea6a6e812b7d6c5c889dda64ba7b7c433ee) +--- + src/responder/secrets/proxy.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c +index 240a1de1e431d511a1eca24d8b463c37ba893e7b..fd96e985c897e2cb470a9b5d6eecbd34350fb7d2 100644 +--- a/src/responder/secrets/proxy.c ++++ b/src/responder/secrets/proxy.c +@@ -440,6 +440,15 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx, + } + } + ++ /* Set basic authentication if required. */ ++ if (pcfg->auth_type == PAT_BASIC_AUTH) { ++ ret = tcurl_req_http_basic_auth(tcurl_req, pcfg->auth.basic.username, ++ pcfg->auth.basic.password); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + talloc_steal(tcurl_req, body); + *_tcurl_req = talloc_steal(mem_ctx, tcurl_req); + +-- +2.9.3 + diff --git a/SOURCES/0087-secrets-fix-debug-message.patch b/SOURCES/0087-secrets-fix-debug-message.patch new file mode 100644 index 0000000..19e563b --- /dev/null +++ b/SOURCES/0087-secrets-fix-debug-message.patch @@ -0,0 +1,29 @@ +From 265c8ea3b9564a53e38df08b89e0fbfb4e7dbfb9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 15 Mar 2017 13:27:59 +0100 +Subject: [PATCH 87/90] secrets: fix debug message + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit db826f57b4c2ee814823057cc536386889f7aa1d) +--- + src/responder/secrets/secsrv_cmd.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/responder/secrets/secsrv_cmd.c b/src/responder/secrets/secsrv_cmd.c +index 70679ec0398fca25cfb0525772f539526a0eb3ff..b88680c3d7c3105d160de5c78e6d981b852318b9 100644 +--- a/src/responder/secrets/secsrv_cmd.c ++++ b/src/responder/secrets/secsrv_cmd.c +@@ -451,7 +451,8 @@ int sec_send_data(int fd, struct sec_data *data) + + data->length -= len; + data->data += len; +- DEBUG(SSSDBG_TRACE_INTERNAL, "sent %zu bytes\n", data->length); ++ DEBUG(SSSDBG_TRACE_INTERNAL, "sent %zu bytes, %zu bytes remaining\n", ++ len, data->length); + return EOK; + } + +-- +2.9.3 + diff --git a/SOURCES/0088-secrets-always-add-Content-Length-header.patch b/SOURCES/0088-secrets-always-add-Content-Length-header.patch new file mode 100644 index 0000000..d941ad3 --- /dev/null +++ b/SOURCES/0088-secrets-always-add-Content-Length-header.patch @@ -0,0 +1,112 @@ +From 07271dbd7c8f28a6aace48787040580973eb5a4e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 15 Mar 2017 15:15:08 +0100 +Subject: [PATCH 88/90] secrets: always add Content-Length header + +If custodia server does not reply with Content-Length header, curl may +wait for non-existing body of http reply if such body does not exist +(for example during POST operation when creating a container). + +Reviewed-by: Simo Sorce +Reviewed-by: Jakub Hrozek +(cherry picked from commit 13d720de13e490850c1139eea865bcd5195a2630) +--- + src/responder/secrets/providers.c | 72 ++++++++++++++++++++++++++++++++++++--- + 1 file changed, 68 insertions(+), 4 deletions(-) + +diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c +index 80a443d91135447ec8ce8d424b692a6d7e26a907..a27fb720b394e7c76d1b65f656146bcd00755449 100644 +--- a/src/responder/secrets/providers.c ++++ b/src/responder/secrets/providers.c +@@ -388,20 +388,84 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply, + return EOK; + } + ++static errno_t ++sec_http_iobuf_split(struct sss_iobuf *response, ++ const char **headers, ++ const char **body) ++{ ++ const char *data = (const char *)sss_iobuf_get_data(response); ++ char *delim; ++ ++ /* The last header ends with \r\n and then comes \r\n again as a separator ++ * of body from headers. We can use this to find this point. */ ++ delim = strstr(data, "\r\n\r\n"); ++ if (delim == NULL) { ++ return EINVAL; ++ } ++ ++ /* Skip to the body delimiter. */ ++ delim = delim + sizeof("\r\n") - 1; ++ ++ /* Replace \r\n with zeros turning data into: ++ * from HEADER\r\nBODY into HEADER\0\0BODY format. */ ++ delim[0] = '\0'; ++ delim[1] = '\0'; ++ ++ /* Split the buffer. */ ++ *headers = data; ++ *body = delim + 2; ++ ++ return 0; ++} ++ ++static const char * ++sec_http_iobuf_add_content_length(TALLOC_CTX *mem_ctx, ++ const char *headers, ++ size_t body_len) ++{ ++ /* If Content-Length is already present we do nothing. */ ++ if (strstr(headers, "Content-Length:") != NULL) { ++ return headers; ++ } ++ ++ return talloc_asprintf(mem_ctx, "%sContent-Length: %zu\r\n", ++ headers, body_len); ++} ++ + errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx, + struct sec_data *reply, + int response_code, + struct sss_iobuf *response) + { ++ const char *headers; ++ const char *body; ++ size_t body_len; ++ errno_t ret; ++ + DEBUG(SSSDBG_TRACE_LIBS, "HTTP reply %d\n", response_code); + +- reply->data = (char *)sss_iobuf_get_data(response); +- reply->length = sss_iobuf_get_len(response); ++ ret = sec_http_iobuf_split(response, &headers, &body); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Unexpected HTTP reply, returning what we got from server\n"); ++ reply->data = (char *)sss_iobuf_get_data(response); ++ reply->length = sss_iobuf_get_len(response); + +- talloc_steal(mem_ctx, reply->data); ++ return EOK; ++ } + ++ /* Add Content-Length header if not present so client does not await ++ * not-existing incoming data. */ ++ body_len = strlen(body); ++ headers = sec_http_iobuf_add_content_length(mem_ctx, headers, body_len); ++ if (headers == NULL) { ++ return ENOMEM; ++ } ++ ++ reply->length = strlen(headers) + sizeof("\r\n") - 1 + body_len; ++ reply->data = talloc_asprintf(mem_ctx, "%s\r\n%s", headers, body); + if (reply->data == NULL) { +- return EINVAL; ++ return ENOMEM; + } + + return EOK; +-- +2.9.3 + diff --git a/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch b/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch new file mode 100644 index 0000000..2524495 --- /dev/null +++ b/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch @@ -0,0 +1,40 @@ +From ce191dc1922d894573eee828c88c325f64515d3e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 28 Mar 2017 15:26:52 +0200 +Subject: [PATCH 89/90] sss_iobuf: fix 'read' shadows a global declaration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 18e4fe9d836e8f7bee52724374ffc0011172329f) +--- + src/util/sss_iobuf.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c +index fc288d2df2bfaaba393dd490d4da8976de804cb5..518713e4cc3dd99627a3a4450f235cbbc69ed3a2 100644 +--- a/src/util/sss_iobuf.c ++++ b/src/util/sss_iobuf.c +@@ -188,15 +188,15 @@ errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf, + size_t len, + uint8_t *_buf) + { +- size_t read; ++ size_t read_bytes; + errno_t ret; + +- ret = sss_iobuf_read(iobuf, len, _buf, &read); ++ ret = sss_iobuf_read(iobuf, len, _buf, &read_bytes); + if (ret != EOK) { + return ret; + } + +- if (read != len) { ++ if (read_bytes != len) { + return ENOBUFS; + } + +-- +2.9.3 + diff --git a/SOURCES/0090-configure-fix-typo.patch b/SOURCES/0090-configure-fix-typo.patch new file mode 100644 index 0000000..f7a0f28 --- /dev/null +++ b/SOURCES/0090-configure-fix-typo.patch @@ -0,0 +1,29 @@ +From a87cb169e5700bf9a3e74d4a1980e8e5c8e24692 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 29 Mar 2017 13:28:49 +0200 +Subject: [PATCH 90/90] configure: fix typo +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit dc186bfe90665c13d589b3b4efd9009293e62c46) +--- + src/external/libhttp_parser.m4 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/external/libhttp_parser.m4 b/src/external/libhttp_parser.m4 +index 504bdf0f66c95b3d224c677a205a46e6f8b44726..3a5ef0dbbc63423ad8e960d72e97ec4fb4481dd1 100644 +--- a/src/external/libhttp_parser.m4 ++++ b/src/external/libhttp_parser.m4 +@@ -17,6 +17,6 @@ AS_IF([test x"$found_http_parser" != xyes], + ], + [-L$sss_extra_libdir -lhttp_parser_strict])], + [AC_MSG_ERROR([ +-You must have the header file http_parse.h installed to build sssd ++You must have the header file http_parser.h installed to build sssd + with secrets responder. If you want to build sssd without secret responder + then specify --without-secrets when running configure.])])]) +-- +2.9.3 + diff --git a/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch b/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch new file mode 100644 index 0000000..e511e0d --- /dev/null +++ b/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch @@ -0,0 +1,105 @@ +From a5a6f0ab816be0dfd24b97a59c161adbe15ef406 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Jan 2017 14:50:20 +0100 +Subject: [PATCH 91/96] pam_test_client: add service and environment to PAM + test client +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3292 + +Reviewed-by: Pavel Březina +(cherry picked from commit 7be6624d9eda369e9a4d70c8ee4939b3622229b3) +--- + src/sss_client/pam_test_client.c | 50 ++++++++++++++++++++++++++++++---------- + 1 file changed, 38 insertions(+), 12 deletions(-) + +diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c +index 29d1fcbf01682668d51bf154736aec673bd46501..ea032a75b195a9bf8078ed7d248da154ab0c8430 100644 +--- a/src/sss_client/pam_test_client.c ++++ b/src/sss_client/pam_test_client.c +@@ -48,34 +48,44 @@ static struct pam_conv conv = { + # error "Missing text based pam conversation function" + #endif + ++#define DEFAULT_ACTION "acct" ++#define DEFAULT_SERVICE "system-auth" ++ + int main(int argc, char *argv[]) { + + pam_handle_t *pamh; + char *user; + char *action; ++ char *service; + int ret; ++ size_t c; ++ char **pam_env; + + if (argc == 1) { +- fprintf(stderr, "missing action and user name, using default\n"); +- action = strdup("auth"); +- user = strdup("dummy"); ++ fprintf(stderr, "Usage: pam_test_client USERNAME " ++ "[auth|acct|setc|chau|open|clos] [pam_service]\n"); ++ return 0; + } else if (argc == 2) { +- fprintf(stdout, "using first argument as action and default user name\n"); +- action = strdup(argv[1]); +- user = strdup("dummy"); +- } else { +- action = strdup(argv[1]); +- user = strdup(argv[2]); ++ fprintf(stderr, "using first argument as user name and default action " ++ "and service\n"); ++ } else if (argc == 3) { ++ fprintf(stderr, "using first argument as user name, second as action " ++ "and default service\n"); + } + +- if (action == NULL || user == NULL) { ++ user = strdup(argv[1]); ++ action = argc > 2 ? strdup(argv[2]) : strdup(DEFAULT_ACTION); ++ service = argc > 3 ? strdup(argv[3]) : strdup(DEFAULT_SERVICE); ++ ++ if (action == NULL || user == NULL || service == NULL) { + fprintf(stderr, "Out of memory!\n"); + return 1; + } + +- fprintf(stdout, "action: %s\nuser: %s\n", action,user); ++ fprintf(stdout, "user: %s\naction: %s\nservice: %s\n", ++ user, action, service); + +- ret = pam_start("sss_test", user, &conv, &pamh); ++ ret = pam_start(service, user, &conv, &pamh); + if (ret != PAM_SUCCESS) { + fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret)); + return 1; +@@ -109,7 +119,23 @@ int main(int argc, char *argv[]) { + fprintf(stderr, "unknown action\n"); + } + ++ fprintf(stderr, "PAM Environment:\n"); ++ pam_env = pam_getenvlist(pamh); ++ if (pam_env != NULL && pam_env[0] != NULL) { ++ for (c = 0; pam_env[c] != NULL; c++) { ++ fprintf(stderr, " - %s\n", pam_env[c]); ++ free(pam_env[c]); ++ } ++ } else { ++ fprintf(stderr, " - no env -\n"); ++ } ++ free(pam_env); ++ + pam_end(pamh, ret); + ++ free(user); ++ free(action); ++ free(service); ++ + return 0; + } +-- +2.9.3 + diff --git a/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch b/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch new file mode 100644 index 0000000..fc139dc --- /dev/null +++ b/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch @@ -0,0 +1,142 @@ +From 109c99463219be59fbf168a4075a74585193aef9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 25 Jan 2017 16:50:00 +0100 +Subject: [PATCH 92/96] pam_test_client: add SSSD getpwnam lookup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3292 + +Reviewed-by: Pavel Březina +(cherry picked from commit 435b3678de25d22eb8a6e892109d26c32f0760a4) +--- + Makefile.am | 10 ++++-- + src/sss_client/pam_test_client.c | 76 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 84 insertions(+), 2 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 4a414f77df999b8b1d81f663fcc18dbd2d6d2dc4..368ebe54b8617cb5bafb079322582d5346b6c4df 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -3460,8 +3460,14 @@ if BUILD_WITH_LIBCURL + noinst_PROGRAMS += tcurl-test-tool + endif + +-pam_test_client_SOURCES = src/sss_client/pam_test_client.c +-pam_test_client_LDADD = $(PAM_LIBS) $(PAM_MISC_LIBS) ++pam_test_client_SOURCES = \ ++ src/sss_client/pam_test_client.c \ ++ $(NULL) ++pam_test_client_LDADD = \ ++ $(PAM_LIBS) \ ++ $(PAM_MISC_LIBS) \ ++ $(LIBADD_DL) \ ++ $(NULL) + + if BUILD_AUTOFS + autofs_test_client_SOURCES = \ +diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c +index ea032a75b195a9bf8078ed7d248da154ab0c8430..69af612270492968b56d1c11de2bf56ebf57471f 100644 +--- a/src/sss_client/pam_test_client.c ++++ b/src/sss_client/pam_test_client.c +@@ -25,6 +25,11 @@ + #include + #include + #include ++#include ++#include ++#include ++#include ++#include + + #include + +@@ -51,6 +56,70 @@ static struct pam_conv conv = { + #define DEFAULT_ACTION "acct" + #define DEFAULT_SERVICE "system-auth" + ++#define DEFAULT_BUFSIZE 4096 ++ ++static int sss_getpwnam_check(const char *user) ++{ ++ void *dl_handle = NULL; ++ enum nss_status (*sss_getpwnam_r)(const char *name, struct passwd *result, ++ char *buffer, size_t buflen, ++ int *errnop); ++ struct passwd pwd = { 0 }; ++ enum nss_status status; ++ char *buffer = NULL; ++ size_t buflen; ++ int nss_errno; ++ int ret; ++ ++ dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW); ++ if (dl_handle == NULL) { ++ fprintf(stderr, "dlopen failed with [%s].\n", dlerror()); ++ ret = EIO; ++ goto done; ++ } ++ ++ sss_getpwnam_r = dlsym(dl_handle, "_nss_sss_getpwnam_r"); ++ if (sss_getpwnam_r == NULL) { ++ fprintf(stderr, "dlsym failed with [%s].\n", dlerror()); ++ ret = EIO; ++ goto done; ++ } ++ ++ buflen = DEFAULT_BUFSIZE; ++ buffer = malloc(buflen); ++ if (buffer == NULL) { ++ fprintf(stderr, "malloc failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ status = sss_getpwnam_r(user, &pwd, buffer, buflen, &nss_errno); ++ if (status != NSS_STATUS_SUCCESS) { ++ fprintf(stderr, "sss_getpwnam_r failed with [%d].\n", status); ++ ret = EIO; ++ goto done; ++ } ++ ++ fprintf(stdout, "SSSD nss user lookup result:\n"); ++ fprintf(stdout, " - user name: %s\n", pwd.pw_name); ++ fprintf(stdout, " - user id: %d\n", pwd.pw_uid); ++ fprintf(stdout, " - group id: %d\n", pwd.pw_gid); ++ fprintf(stdout, " - gecos: %s\n", pwd.pw_gecos); ++ fprintf(stdout, " - home directory: %s\n", pwd.pw_dir); ++ fprintf(stdout, " - shell: %s\n", pwd.pw_shell); ++ ++ ret = 0; ++ ++done: ++ if (dl_handle != NULL) { ++ dlclose(dl_handle); ++ } ++ ++ free(buffer); ++ ++ return ret; ++} ++ + int main(int argc, char *argv[]) { + + pam_handle_t *pamh; +@@ -85,6 +154,13 @@ int main(int argc, char *argv[]) { + fprintf(stdout, "user: %s\naction: %s\nservice: %s\n", + user, action, service); + ++ if (*user != '\0') { ++ ret = sss_getpwnam_check(user); ++ if (ret != 0) { ++ fprintf(stderr, "User name lookup with [%s] failed.\n", user); ++ } ++ } ++ + ret = pam_start(service, user, &conv, &pamh); + if (ret != PAM_SUCCESS) { + fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret)); +-- +2.9.3 + diff --git a/SOURCES/0093-sss_sifp-update-method-names.patch b/SOURCES/0093-sss_sifp-update-method-names.patch new file mode 100644 index 0000000..0dac7de --- /dev/null +++ b/SOURCES/0093-sss_sifp-update-method-names.patch @@ -0,0 +1,54 @@ +From 52622fbb51d972ba1f02ff0c7dff2e9fa7adf96c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 16 Mar 2017 11:37:41 +0100 +Subject: [PATCH 93/96] sss_sifp: update method names +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3292 + +Reviewed-by: Pavel Březina +(cherry picked from commit 40ff10d73063949ca699670ca212e96b809d5fcd) +--- + Makefile.am | 2 +- + src/lib/sifp/sss_sifp_common.c | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 368ebe54b8617cb5bafb079322582d5346b6c4df..b16a71cc9e07f21d02b4ceb3f41a8e9de0591ec9 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1221,7 +1221,7 @@ libsss_simpleifp_la_LIBADD = \ + $(DHASH_LIBS) + libsss_simpleifp_la_LDFLAGS = \ + -Wl,--version-script,$(srcdir)/src/lib/sifp/sss_simpleifp.exports \ +- -version-info 1:0:1 ++ -version-info 1:1:1 + + dist_noinst_DATA += src/lib/sifp/sss_simpleifp.exports + +diff --git a/src/lib/sifp/sss_sifp_common.c b/src/lib/sifp/sss_sifp_common.c +index bd1dc6a3108329d2c795dc0a259637e71964be9f..8913d0be3d43bd8707829001a5b476d9ab864fd8 100644 +--- a/src/lib/sifp/sss_sifp_common.c ++++ b/src/lib/sifp/sss_sifp_common.c +@@ -168,7 +168,7 @@ sss_sifp_fetch_user_by_uid(sss_sifp_ctx *ctx, + uint64_t _uid = uid; + + return sss_sifp_fetch_object_by_attr(ctx, IFP_PATH_USERS, IFACE_IFP_USERS, +- IFACE_IFP_USERS_USER, "UserByID", ++ IFACE_IFP_USERS_USER, "ByID", + DBUS_TYPE_UINT64, &_uid, _user); + } + +@@ -178,6 +178,6 @@ sss_sifp_fetch_user_by_name(sss_sifp_ctx *ctx, + sss_sifp_object **_user) + { + return sss_sifp_fetch_object_by_name(ctx, IFP_PATH_USERS, IFACE_IFP_USERS, +- IFACE_IFP_USERS_USER, "UserByName", ++ IFACE_IFP_USERS_USER, "ByName", + name, _user); + } +-- +2.9.3 + diff --git a/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch b/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch new file mode 100644 index 0000000..6d20370 --- /dev/null +++ b/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch @@ -0,0 +1,131 @@ +From acefbdd65a083b5d9577d9f683ac64e358c2f9c0 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 16 Mar 2017 11:38:20 +0100 +Subject: [PATCH 94/96] pam_test_client: add InfoPipe user lookup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3292 + +Reviewed-by: Pavel Březina +(cherry picked from commit 9be97c9cc69e5e6e568d7e21f61a46c3ae2dc387) +--- + Makefile.am | 1 + + src/sss_client/pam_test_client.c | 71 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/Makefile.am b/Makefile.am +index b16a71cc9e07f21d02b4ceb3f41a8e9de0591ec9..c4d252357356c2d5452a414fd360fc5370b2c775 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -3467,6 +3467,7 @@ pam_test_client_LDADD = \ + $(PAM_LIBS) \ + $(PAM_MISC_LIBS) \ + $(LIBADD_DL) \ ++ libsss_simpleifp.la \ + $(NULL) + + if BUILD_AUTOFS +diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c +index 69af612270492968b56d1c11de2bf56ebf57471f..40ef3f6d480c0108c985fce7e34e983d145f237e 100644 +--- a/src/sss_client/pam_test_client.c ++++ b/src/sss_client/pam_test_client.c +@@ -30,9 +30,12 @@ + #include + #include + #include ++#include + + #include + ++#include "lib/sifp/sss_sifp.h" ++ + #ifdef HAVE_SECURITY_PAM_MISC_H + # include + #elif defined(HAVE_SECURITY_OPENPAM_H) +@@ -58,6 +61,69 @@ static struct pam_conv conv = { + + #define DEFAULT_BUFSIZE 4096 + ++static int get_ifp_user(const char *user) ++{ ++ sss_sifp_ctx *sifp; ++ sss_sifp_error error; ++ sss_sifp_object *user_obj; ++ const char *tmp_str; ++ uint32_t tmp_uint32; ++ size_t c; ++ ++ struct ifp_user_attr { ++ const char *name; ++ bool is_string; ++ } ifp_user_attr[] = { ++ { "name", true }, ++ { "uidNumber", false }, ++ { "gidNumber", false }, ++ { "gecos", true }, ++ { "homeDirectory", true }, ++ { "loginShell", true }, ++ { NULL, false } ++ }; ++ ++ error = sss_sifp_init(&sifp); ++ if (error != SSS_SIFP_OK) { ++ fprintf(stderr, "Unable to connect to the InfoPipe"); ++ return EFAULT; ++ } ++ ++ error = sss_sifp_fetch_user_by_name(sifp, user, &user_obj); ++ if (error != SSS_SIFP_OK) { ++ fprintf(stderr, "Unable to get user object"); ++ return EIO; ++ } ++ ++ fprintf(stdout, "SSSD InfoPipe user lookup result:\n"); ++ for (c = 0; ifp_user_attr[c].name != NULL; c++) { ++ if (ifp_user_attr[c].is_string) { ++ error = sss_sifp_find_attr_as_string(user_obj->attrs, ++ ifp_user_attr[c].name, ++ &tmp_str); ++ } else { ++ error = sss_sifp_find_attr_as_uint32(user_obj->attrs, ++ ifp_user_attr[c].name, ++ &tmp_uint32); ++ } ++ if (error != SSS_SIFP_OK) { ++ fprintf(stderr, "Unable to get user name attr"); ++ return EIO; ++ } ++ ++ if (ifp_user_attr[c].is_string) { ++ fprintf(stdout, " - %s: %s\n", ifp_user_attr[c].name, tmp_str); ++ } else { ++ fprintf(stdout, " - %s: %"PRIu32"\n", ifp_user_attr[c].name, ++ tmp_uint32); ++ } ++ } ++ ++ sss_sifp_free_object(sifp, &user_obj); ++ sss_sifp_free(&sifp); ++ return 0; ++} ++ + static int sss_getpwnam_check(const char *user) + { + void *dl_handle = NULL; +@@ -159,6 +225,11 @@ int main(int argc, char *argv[]) { + if (ret != 0) { + fprintf(stderr, "User name lookup with [%s] failed.\n", user); + } ++ ++ ret = get_ifp_user(user); ++ if (ret != 0) { ++ fprintf(stderr, "InforPipe User lookup with [%s] failed.\n", user); ++ } + } + + ret = pam_start(service, user, &conv, &pamh); +-- +2.9.3 + diff --git a/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch b/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch new file mode 100644 index 0000000..d942a65 --- /dev/null +++ b/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch @@ -0,0 +1,359 @@ +From 1bc25dba8f4725ef34e394d8e8eee42dbdaed924 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 30 Mar 2017 16:21:15 +0200 +Subject: [PATCH 95/96] sssctl: integrate pam_test_client into sssctl +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Pavel Březina +(cherry picked from commit 4a9160e2b3b9c531e2b4a7884f49bfbb4a07a992) +--- + Makefile.am | 16 +-- + po/POTFILES.in | 1 - + src/tools/sssctl/sssctl.c | 1 + + src/tools/sssctl/sssctl.h | 4 + + .../sssctl/sssctl_user_checks.c} | 122 +++++++++++---------- + 5 files changed, 72 insertions(+), 72 deletions(-) + rename src/{sss_client/pam_test_client.c => tools/sssctl/sssctl_user_checks.c} (62%) + +diff --git a/Makefile.am b/Makefile.am +index c4d252357356c2d5452a414fd360fc5370b2c775..f5ac363a35e4aae51e8b70bad27c7fc824be10f2 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1724,11 +1724,15 @@ sssctl_SOURCES = \ + src/tools/sssctl/sssctl_domains.c \ + src/tools/sssctl/sssctl_sifp.c \ + src/tools/sssctl/sssctl_config.c \ ++ src/tools/sssctl/sssctl_user_checks.c \ + $(SSSD_TOOLS_OBJ) \ + $(NULL) + sssctl_LDADD = \ + $(TOOLS_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ ++ $(PAM_LIBS) \ ++ $(PAM_MISC_LIBS) \ ++ $(LIBADD_DL) \ + libsss_simpleifp.la \ + $(NULL) + sssctl_CFLAGS = \ +@@ -3449,7 +3453,7 @@ endif # BUILD_KCM + + endif # HAVE_CMOCKA + +-noinst_PROGRAMS = pam_test_client ++noinst_PROGRAMS = + if BUILD_SUDO + noinst_PROGRAMS += sss_sudo_cli + endif +@@ -3460,16 +3464,6 @@ if BUILD_WITH_LIBCURL + noinst_PROGRAMS += tcurl-test-tool + endif + +-pam_test_client_SOURCES = \ +- src/sss_client/pam_test_client.c \ +- $(NULL) +-pam_test_client_LDADD = \ +- $(PAM_LIBS) \ +- $(PAM_MISC_LIBS) \ +- $(LIBADD_DL) \ +- libsss_simpleifp.la \ +- $(NULL) +- + if BUILD_AUTOFS + autofs_test_client_SOURCES = \ + src/sss_client/autofs/autofs_test_client.c \ +diff --git a/po/POTFILES.in b/po/POTFILES.in +index ee532def223fdd5db632ad98fd11a57e38d0e125..f4e4e095f9e4025d129b6b13422bdd0bc07c8e1a 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -9,7 +9,6 @@ src/sss_client/common.c + src/sss_client/nss_group.c + src/sss_client/nss_passwd.c + src/sss_client/pam_sss.c +-src/sss_client/pam_test_client.c + src/sss_client/ssh/sss_ssh_authorizedkeys.c + src/sss_client/ssh/sss_ssh_knownhostsproxy.c + src/tools/sss_useradd.c +diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c +index e1cf46382cd1dee54cd372ca500368f149411b78..509d2e1a00d3b57b541590ce7db5f94d2ff43add 100644 +--- a/src/tools/sssctl/sssctl.c ++++ b/src/tools/sssctl/sssctl.c +@@ -263,6 +263,7 @@ int main(int argc, const char **argv) + SSS_TOOL_DELIMITER("SSSD Status:"), + SSS_TOOL_COMMAND("domain-list", "List available domains", 0, sssctl_domain_list), + SSS_TOOL_COMMAND("domain-status", "Print information about domain", 0, sssctl_domain_status), ++ SSS_TOOL_COMMAND("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks), + SSS_TOOL_DELIMITER("Information about cached content:"), + SSS_TOOL_COMMAND("user-show", "Information about cached user", 0, sssctl_user_show), + SSS_TOOL_COMMAND("group-show", "Information about cached group", 0, sssctl_group_show), +diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h +index 5270a9ec62dfb288511af179a99e9a542ea26ec4..22626e2210252e5e3fadeb6c5d01d4620cd60e5b 100644 +--- a/src/tools/sssctl/sssctl.h ++++ b/src/tools/sssctl/sssctl.h +@@ -121,4 +121,8 @@ errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline, + errno_t sssctl_config_check(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt); ++ ++errno_t sssctl_user_checks(struct sss_cmdline *cmdline, ++ struct sss_tool_ctx *tool_ctx, ++ void *pvt); + #endif /* _SSSCTL_H_ */ +diff --git a/src/sss_client/pam_test_client.c b/src/tools/sssctl/sssctl_user_checks.c +similarity index 62% +rename from src/sss_client/pam_test_client.c +rename to src/tools/sssctl/sssctl_user_checks.c +index 40ef3f6d480c0108c985fce7e34e983d145f237e..7c7b564bd29100382c9bbef7a3131c379e9aa97e 100644 +--- a/src/sss_client/pam_test_client.c ++++ b/src/tools/sssctl/sssctl_user_checks.c +@@ -35,6 +35,9 @@ + #include + + #include "lib/sifp/sss_sifp.h" ++#include "util/util.h" ++#include "tools/common/sss_tools.h" ++#include "tools/sssctl/sssctl.h" + + #ifdef HAVE_SECURITY_PAM_MISC_H + # include +@@ -85,17 +88,17 @@ static int get_ifp_user(const char *user) + + error = sss_sifp_init(&sifp); + if (error != SSS_SIFP_OK) { +- fprintf(stderr, "Unable to connect to the InfoPipe"); ++ fprintf(stderr, _("Unable to connect to the InfoPipe")); + return EFAULT; + } + + error = sss_sifp_fetch_user_by_name(sifp, user, &user_obj); + if (error != SSS_SIFP_OK) { +- fprintf(stderr, "Unable to get user object"); ++ fprintf(stderr, _("Unable to get user object")); + return EIO; + } + +- fprintf(stdout, "SSSD InfoPipe user lookup result:\n"); ++ fprintf(stdout, _("SSSD InfoPipe user lookup result:\n")); + for (c = 0; ifp_user_attr[c].name != NULL; c++) { + if (ifp_user_attr[c].is_string) { + error = sss_sifp_find_attr_as_string(user_obj->attrs, +@@ -107,7 +110,7 @@ static int get_ifp_user(const char *user) + &tmp_uint32); + } + if (error != SSS_SIFP_OK) { +- fprintf(stderr, "Unable to get user name attr"); ++ fprintf(stderr, _("Unable to get user name attr")); + return EIO; + } + +@@ -118,6 +121,7 @@ static int get_ifp_user(const char *user) + tmp_uint32); + } + } ++ fprintf(stdout, "\n"); + + sss_sifp_free_object(sifp, &user_obj); + sss_sifp_free(&sifp); +@@ -139,14 +143,14 @@ static int sss_getpwnam_check(const char *user) + + dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW); + if (dl_handle == NULL) { +- fprintf(stderr, "dlopen failed with [%s].\n", dlerror()); ++ fprintf(stderr, _("dlopen failed with [%s].\n"), dlerror()); + ret = EIO; + goto done; + } + + sss_getpwnam_r = dlsym(dl_handle, "_nss_sss_getpwnam_r"); + if (sss_getpwnam_r == NULL) { +- fprintf(stderr, "dlsym failed with [%s].\n", dlerror()); ++ fprintf(stderr, _("dlsym failed with [%s].\n"), dlerror()); + ret = EIO; + goto done; + } +@@ -154,25 +158,25 @@ static int sss_getpwnam_check(const char *user) + buflen = DEFAULT_BUFSIZE; + buffer = malloc(buflen); + if (buffer == NULL) { +- fprintf(stderr, "malloc failed.\n"); ++ fprintf(stderr, _("malloc failed.\n")); + ret = ENOMEM; + goto done; + } + + status = sss_getpwnam_r(user, &pwd, buffer, buflen, &nss_errno); + if (status != NSS_STATUS_SUCCESS) { +- fprintf(stderr, "sss_getpwnam_r failed with [%d].\n", status); ++ fprintf(stderr, _("sss_getpwnam_r failed with [%d].\n"), status); + ret = EIO; + goto done; + } + +- fprintf(stdout, "SSSD nss user lookup result:\n"); +- fprintf(stdout, " - user name: %s\n", pwd.pw_name); +- fprintf(stdout, " - user id: %d\n", pwd.pw_uid); +- fprintf(stdout, " - group id: %d\n", pwd.pw_gid); +- fprintf(stdout, " - gecos: %s\n", pwd.pw_gecos); +- fprintf(stdout, " - home directory: %s\n", pwd.pw_dir); +- fprintf(stdout, " - shell: %s\n", pwd.pw_shell); ++ fprintf(stdout, _("SSSD nss user lookup result:\n")); ++ fprintf(stdout, _(" - user name: %s\n"), pwd.pw_name); ++ fprintf(stdout, _(" - user id: %d\n"), pwd.pw_uid); ++ fprintf(stdout, _(" - group id: %d\n"), pwd.pw_gid); ++ fprintf(stdout, _(" - gecos: %s\n"), pwd.pw_gecos); ++ fprintf(stdout, _(" - home directory: %s\n"), pwd.pw_dir); ++ fprintf(stdout, _(" - shell: %s\n\n"), pwd.pw_shell); + + ret = 0; + +@@ -186,87 +190,89 @@ done: + return ret; + } + +-int main(int argc, char *argv[]) { ++errno_t sssctl_user_checks(struct sss_cmdline *cmdline, ++ struct sss_tool_ctx *tool_ctx, ++ void *pvt) ++{ + + pam_handle_t *pamh; +- char *user; +- char *action; +- char *service; ++ const char *user = NULL; ++ const char *action = DEFAULT_ACTION; ++ const char *service = DEFAULT_SERVICE; + int ret; + size_t c; + char **pam_env; + +- if (argc == 1) { +- fprintf(stderr, "Usage: pam_test_client USERNAME " +- "[auth|acct|setc|chau|open|clos] [pam_service]\n"); +- return 0; +- } else if (argc == 2) { +- fprintf(stderr, "using first argument as user name and default action " +- "and service\n"); +- } else if (argc == 3) { +- fprintf(stderr, "using first argument as user name, second as action " +- "and default service\n"); +- } +- +- user = strdup(argv[1]); +- action = argc > 2 ? strdup(argv[2]) : strdup(DEFAULT_ACTION); +- service = argc > 3 ? strdup(argv[3]) : strdup(DEFAULT_SERVICE); ++ /* Parse command line. */ ++ struct poptOption options[] = { ++ { "action", 'a', POPT_ARG_STRING, &action, 0, ++ _("PAM action [auth|acct|setc|chau|open|clos], default: " ++ DEFAULT_ACTION), NULL }, ++ { "service", 's', POPT_ARG_STRING, &service, 0, ++ _("PAM service, default: " DEFAULT_SERVICE), NULL }, ++ POPT_TABLEEND ++ }; + +- if (action == NULL || user == NULL || service == NULL) { +- fprintf(stderr, "Out of memory!\n"); +- return 1; ++ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL, ++ NULL, NULL, "USERNAME", _("Specify user name."), ++ &user, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n"); ++ return ret; + } + +- fprintf(stdout, "user: %s\naction: %s\nservice: %s\n", ++ fprintf(stdout, _("user: %s\naction: %s\nservice: %s\n\n"), + user, action, service); + + if (*user != '\0') { + ret = sss_getpwnam_check(user); + if (ret != 0) { +- fprintf(stderr, "User name lookup with [%s] failed.\n", user); ++ fprintf(stderr, _("User name lookup with [%s] failed.\n"), user); + } + + ret = get_ifp_user(user); + if (ret != 0) { +- fprintf(stderr, "InforPipe User lookup with [%s] failed.\n", user); ++ fprintf(stderr, _("InforPipe User lookup with [%s] failed.\n"), ++ user); + } + } + + ret = pam_start(service, user, &conv, &pamh); + if (ret != PAM_SUCCESS) { +- fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_start failed: %s\n"), pam_strerror(pamh, ret)); + return 1; + } + + if ( strncmp(action, "auth", 4)== 0 ) { +- fprintf(stdout, "testing pam_authenticate\n"); ++ fprintf(stdout, _("testing pam_authenticate\n\n")); + ret = pam_authenticate(pamh, 0); +- fprintf(stderr, "pam_authenticate: %s\n", pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_authenticate: %s\n\n"), pam_strerror(pamh, ret)); + } else if ( strncmp(action, "chau", 4)== 0 ) { +- fprintf(stdout, "testing pam_chauthtok\n"); ++ fprintf(stdout, _("testing pam_chauthtok\n\n")); + ret = pam_chauthtok(pamh, 0); +- fprintf(stderr, "pam_chauthtok: %s\n", pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_chauthtok: %s\n\n"), pam_strerror(pamh, ret)); + } else if ( strncmp(action, "acct", 4)== 0 ) { +- fprintf(stdout, "testing pam_acct_mgmt\n"); ++ fprintf(stdout, _("testing pam_acct_mgmt\n\n")); + ret = pam_acct_mgmt(pamh, 0); +- fprintf(stderr, "pam_acct_mgmt: %s\n", pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_acct_mgmt: %s\n\n"), pam_strerror(pamh, ret)); + } else if ( strncmp(action, "setc", 4)== 0 ) { +- fprintf(stdout, "testing pam_setcred\n"); ++ fprintf(stdout, _("testing pam_setcred\n\n")); + ret = pam_setcred(pamh, 0); +- fprintf(stderr, "pam_setcred: %d[%s]\n", ret, pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_setcred: [%s]\n\n"), pam_strerror(pamh, ret)); + } else if ( strncmp(action, "open", 4)== 0 ) { +- fprintf(stdout, "testing pam_open_session\n"); ++ fprintf(stdout, _("testing pam_open_session\n\n")); + ret = pam_open_session(pamh, 0); +- fprintf(stderr, "pam_open_session: %s\n", pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_open_session: %s\n\n"), pam_strerror(pamh, ret)); + } else if ( strncmp(action, "clos", 4)== 0 ) { +- fprintf(stdout, "testing pam_close_session\n"); ++ fprintf(stdout, _("testing pam_close_session\n\n")); + ret = pam_close_session(pamh, 0); +- fprintf(stderr, "pam_close_session: %s\n", pam_strerror(pamh, ret)); ++ fprintf(stderr, _("pam_close_session: %s\n\n"), ++ pam_strerror(pamh, ret)); + } else { +- fprintf(stderr, "unknown action\n"); ++ fprintf(stderr, _("unknown action\n")); + } + +- fprintf(stderr, "PAM Environment:\n"); ++ fprintf(stderr, _("PAM Environment:\n")); + pam_env = pam_getenvlist(pamh); + if (pam_env != NULL && pam_env[0] != NULL) { + for (c = 0; pam_env[c] != NULL; c++) { +@@ -274,15 +280,11 @@ int main(int argc, char *argv[]) { + free(pam_env[c]); + } + } else { +- fprintf(stderr, " - no env -\n"); ++ fprintf(stderr, _(" - no env -\n")); + } + free(pam_env); + + pam_end(pamh, ret); + +- free(user); +- free(action); +- free(service); +- + return 0; + } +-- +2.9.3 + diff --git a/SOURCES/0096-i18n-adding-sssctl-files.patch b/SOURCES/0096-i18n-adding-sssctl-files.patch new file mode 100644 index 0000000..84984cf --- /dev/null +++ b/SOURCES/0096-i18n-adding-sssctl-files.patch @@ -0,0 +1,34 @@ +From e66f1df979b47519f9f51b6064154dae7ba5b396 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 7 Apr 2017 14:24:10 +0200 +Subject: [PATCH 96/96] i18n: adding sssctl files +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Pavel Březina +(cherry picked from commit dbeae483464e42238a84c6a5b8c3c4f5312ae643) +--- + po/POTFILES.in | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/po/POTFILES.in b/po/POTFILES.in +index f4e4e095f9e4025d129b6b13422bdd0bc07c8e1a..33e7ed7f9e9bc19f33fca8a1f2649b69b79a882f 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -23,4 +23,12 @@ src/tools/sss_cache.c + src/tools/sss_debuglevel.c + src/tools/tools_util.c + src/tools/tools_util.h ++src/tools/sssctl/sssctl.c ++src/tools/sssctl/sssctl_cache.c ++src/tools/sssctl/sssctl_config.c ++src/tools/sssctl/sssctl_data.c ++src/tools/sssctl/sssctl_domains.c ++src/tools/sssctl/sssctl_logs.c ++src/tools/sssctl/sssctl_sifp.c ++src/tools/sssctl/sssctl_user_checks.c + src/util/util.h +-- +2.9.3 + diff --git a/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch b/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch new file mode 100644 index 0000000..a38530b --- /dev/null +++ b/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch @@ -0,0 +1,69 @@ +From 3a07827a3722fd2166b94af1f5790273fbac01eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Mon, 3 Apr 2017 12:56:01 +0200 +Subject: [PATCH 97/99] responders: do not leak selinux context on clients + destruction +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The SELinux context created in get_client_cred is not talloc bound and +we were leaking it if available with each client's destruction. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3360 + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 05c2c3047912fca1c1a35ab1c8d3157b05383495) +--- + src/responder/common/responder_common.c | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 154d7dc7718c437d10e152fcba98161e2034fb14..67e1deefdfde19c95a68029b11099579d851513f 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -97,7 +97,7 @@ static errno_t get_client_cred(struct cli_ctx *cctx) + SEC_CTX secctx; + int ret; + +- cctx->creds = talloc(cctx, struct cli_creds); ++ cctx->creds = talloc_zero(cctx, struct cli_creds); + if (!cctx->creds) return ENOMEM; + + #ifdef HAVE_UCRED +@@ -464,6 +464,22 @@ static void client_fd_handler(struct tevent_context *ev, + + static errno_t setup_client_idle_timer(struct cli_ctx *cctx); + ++static int cli_ctx_destructor(struct cli_ctx *cctx) ++{ ++ if (cctx->creds == NULL) { ++ return 0; ++ } ++ ++ if (cctx->creds->selinux_ctx == NULL) { ++ return 0; ++ } ++ ++ SELINUX_context_free(cctx->creds->selinux_ctx); ++ cctx->creds->selinux_ctx = NULL; ++ ++ return 0; ++} ++ + struct accept_fd_ctx { + struct resp_ctx *rctx; + bool is_private; +@@ -520,6 +536,8 @@ static void accept_fd_handler(struct tevent_context *ev, + return; + } + ++ talloc_set_destructor(cctx, cli_ctx_destructor); ++ + len = sizeof(cctx->addr); + cctx->cfd = accept(fd, (struct sockaddr *)&cctx->addr, &len); + if (cctx->cfd == -1) { +-- +2.9.3 + diff --git a/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch b/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch new file mode 100644 index 0000000..5a49b15 --- /dev/null +++ b/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch @@ -0,0 +1,89 @@ +From 250777f65dc23917c436d3ecf0fe21abc65db65e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Mon, 3 Apr 2017 12:09:44 +0200 +Subject: [PATCH 98/99] ipa_s2n_get_acct_info_send: provide correct req_input + name +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To avoid crash. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3358 + +Reviewed-by: Sumit Bose +Reviewed-by: Lukáš Slebodník +(cherry picked from commit b07bcd8b99590bd404733fa7ff1add37c55126bc) +--- + src/providers/ipa/ipa_s2n_exop.c | 40 ++++++++++++++++++++++++++++++++++++---- + 1 file changed, 36 insertions(+), 4 deletions(-) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 8a3391b4093f1547d84fe44a0f24b1d063d1e28c..2173db357700499a6140aa61841e443139981483 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -1054,6 +1054,33 @@ static const char *ipa_s2n_reqtype2str(enum request_types request_type) + return "Unknown request type"; + } + ++static const char *ipa_s2n_reqinp2str(TALLOC_CTX *mem_ctx, ++ struct req_input *req_input) ++{ ++ const char *str = NULL; ++ ++ switch (req_input->type) { ++ case REQ_INP_NAME: ++ str = talloc_strdup(mem_ctx, req_input->inp.name); ++ break; ++ case REQ_INP_SECID: ++ str = talloc_strdup(mem_ctx, req_input->inp.secid); ++ break; ++ case REQ_INP_CERT: ++ str = talloc_strdup(mem_ctx, req_input->inp.cert); ++ break; ++ case REQ_INP_ID: ++ str = talloc_asprintf(mem_ctx, "%u", req_input->inp.id); ++ break; ++ } ++ ++ if (str == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); ++ } ++ ++ return str; ++} ++ + struct ipa_s2n_get_list_state { + struct tevent_context *ev; + struct ipa_id_ctx *ipa_ctx; +@@ -1410,6 +1437,7 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx, + struct tevent_req *req; + struct tevent_req *subreq; + struct berval *bv_req = NULL; ++ const char *input; + int ret = EFAULT; + bool is_v1 = false; + +@@ -1454,10 +1482,14 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx, + goto fail; + } + +- DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for trust user [%s] " +- "to IPA server\n", +- ipa_s2n_reqtype2str(state->request_type), +- req_input->inp.name); ++ if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) { ++ input = ipa_s2n_reqinp2str(state, req_input); ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Sending request_type: [%s] for trust user [%s] to IPA server\n", ++ ipa_s2n_reqtype2str(state->request_type), ++ input); ++ talloc_zfree(input); ++ } + + subreq = ipa_s2n_exop_send(state, state->ev, state->sh, is_v1, + state->exop_timeout, bv_req); +-- +2.9.3 + diff --git a/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch b/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch new file mode 100644 index 0000000..2862d4d --- /dev/null +++ b/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch @@ -0,0 +1,39 @@ +From be05d577626835e3c72d71fc60e6abfa564c7cbe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 14 Mar 2017 15:43:41 +0100 +Subject: [PATCH 99/99] config-check: Message when sssd.conf is missing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +sssctl config-check should print a message for user +if no sssd.conf was found. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3330 + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 955574eeb3a3b937abc3df150e9bbbb79b75c889) +--- + src/tools/sssctl/sssctl_config.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c +index 630df3c8ff5368ef253bb9753380e94c8c0a307d..7e3ebf5428ce3fef232eee7334c7fd90e904b2d3 100644 +--- a/src/tools/sssctl/sssctl_config.c ++++ b/src/tools/sssctl/sssctl_config.c +@@ -63,7 +63,10 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline, + + /* Open config file */ + ret = sss_ini_config_file_open(init_data, SSSD_CONFIG_FILE); +- if (ret != EOK) { ++ if (ret == ENOENT) { ++ ERROR("File %1$s does not exist.\n", SSSD_CONFIG_FILE); ++ goto done; ++ } else if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "sss_ini_config_file_open failed: %s [%d]\n", + sss_strerror(ret), +-- +2.9.3 + diff --git a/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch b/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch new file mode 100644 index 0000000..66fee16 --- /dev/null +++ b/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch @@ -0,0 +1,44 @@ +From 556eb1200a3754935f573ccffee87554bf9e9296 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 10 Apr 2017 13:45:27 +0200 +Subject: [PATCH 100/101] sbus: check connection for NULL before unregister it +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There seem to be code paths where the data is a added to the hash before +the connection is properly initialized, to avoid core dump during shut +down we only call dbus_conection_unregister_object_path() if there is a +connection. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3367 + +Reviewed-by: Pavel Březina +(cherry picked from commit 35186217d44d0138a1aedf7a4db72249b2c40e66) +--- + src/sbus/sssd_dbus_interface.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/sbus/sssd_dbus_interface.c b/src/sbus/sssd_dbus_interface.c +index 1a11c6abcf23053e3b8c77f4d469d7c202a88eb8..c9007a4814e09e26fedaf605ca7313234d5ebf2c 100644 +--- a/src/sbus/sssd_dbus_interface.c ++++ b/src/sbus/sssd_dbus_interface.c +@@ -490,7 +490,13 @@ sbus_opath_hash_delete_cb(hash_entry_t *item, + conn = talloc_get_type(pvt, struct sbus_connection); + path = sbus_opath_get_base_path(NULL, item->key.str); + +- dbus_connection_unregister_object_path(conn->dbus.conn, path); ++ /* There seem to be code paths where the data is added to the hash ++ * before the connection is properly initialized, to avoid core dump ++ * during shut down we only call dbus_connection_unregister_object_path() ++ * if there is a connection. */ ++ if (conn->dbus.conn != NULL) { ++ dbus_connection_unregister_object_path(conn->dbus.conn, path); ++ } + } + + hash_table_t * +-- +2.9.3 + diff --git a/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch b/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch new file mode 100644 index 0000000..1ae6353 --- /dev/null +++ b/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch @@ -0,0 +1,211 @@ +From 9b7c29b67ec845b2004d6bcac2bcceabfd855f1e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Wed, 8 Feb 2017 12:01:37 +0100 +Subject: [PATCH 101/101] selinux: Do not fail if SELinux is not managed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previously we failed if semanage_is_managed returned 0 or -1 (not +managed or error). With this patch we only fail in case of error and +continue normally if selinux is not managed by libsemanage at all. + +Resolves: +https://fedorahosted.org/sssd/ticket/3297 + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 78a08d30b5fbf6e1e3b589e0cf67022e0c1faa33) +--- + Makefile.am | 1 + + src/providers/ipa/selinux_child.c | 9 ++++-- + src/util/sss_semanage.c | 61 +++++++++++++++++++++++++-------------- + src/util/util_errors.c | 1 + + src/util/util_errors.h | 1 + + 5 files changed, 49 insertions(+), 24 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index f5ac363a35e4aae51e8b70bad27c7fc824be10f2..370d6442ec58a14946ad288a23c696f25ca98f47 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -4040,6 +4040,7 @@ selinux_child_SOURCES = \ + src/util/atomic_io.c \ + src/util/util.c \ + src/util/util_ext.c \ ++ src/util/util_errors.c + $(NULL) + selinux_child_CFLAGS = \ + $(AM_CFLAGS) \ +diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c +index 380005c7ad3269fc8113c62ceef30b076455b5dd..f8dd3954a7244df2dcbb910aabf8888f41306c09 100644 +--- a/src/providers/ipa/selinux_child.c ++++ b/src/providers/ipa/selinux_child.c +@@ -174,14 +174,19 @@ static bool seuser_needs_update(struct input_buffer *ibuf) + + ret = get_seuser(ibuf, ibuf->username, &db_seuser, &db_mls_range); + DEBUG(SSSDBG_TRACE_INTERNAL, +- "get_seuser: ret: %d seuser: %s mls: %s\n", +- ret, db_seuser ? db_seuser : "unknown", ++ "get_seuser: ret: %d msg: [%s] seuser: %s mls: %s\n", ++ ret, sss_strerror(ret), ++ db_seuser ? db_seuser : "unknown", + db_mls_range ? db_mls_range : "unknown"); + if (ret == EOK && db_seuser && db_mls_range && + strcmp(db_seuser, ibuf->seuser) == 0 && + strcmp(db_mls_range, ibuf->mls_range) == 0) { + needs_update = false; + } ++ /* OR */ ++ if (ret == ERR_SELINUX_NOT_MANAGED) { ++ needs_update = false; ++ } + + talloc_free(db_seuser); + talloc_free(db_mls_range); +diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c +index fe06bee1dfec3abca3aa3cd5e85e55386ac11343..0da97aad4d8eba733b131c2749932e03ca4242c4 100644 +--- a/src/util/sss_semanage.c ++++ b/src/util/sss_semanage.c +@@ -73,7 +73,7 @@ static void sss_semanage_close(semanage_handle_t *handle) + semanage_handle_destroy(handle); + } + +-static semanage_handle_t *sss_semanage_init(void) ++static int sss_semanage_init(semanage_handle_t **_handle) + { + int ret; + semanage_handle_t *handle = NULL; +@@ -81,7 +81,8 @@ static semanage_handle_t *sss_semanage_init(void) + handle = semanage_handle_create(); + if (!handle) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n"); +- return NULL; ++ ret = EIO; ++ goto done; + } + + semanage_msg_set_callback(handle, +@@ -89,28 +90,41 @@ static semanage_handle_t *sss_semanage_init(void) + NULL); + + ret = semanage_is_managed(handle); +- if (ret != 1) { +- DEBUG(SSSDBG_CRIT_FAILURE, "SELinux policy not managed\n"); +- goto fail; ++ if (ret == 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, "SELinux policy not managed via libsemanage\n"); ++ ret = ERR_SELINUX_NOT_MANAGED; ++ goto done; ++ } else if (ret == -1) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Call to semanage_is_managed failed\n"); ++ ret = EIO; ++ goto done; + } + + ret = semanage_access_check(handle); + if (ret < SEMANAGE_CAN_READ) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot read SELinux policy store\n"); +- goto fail; ++ ret = EACCES; ++ goto done; + } + + ret = semanage_connect(handle); + if (ret != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot estabilish SELinux management connection\n"); +- goto fail; ++ ret = EIO; ++ goto done; + } + +- return handle; +-fail: +- sss_semanage_close(handle); +- return NULL; ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ sss_semanage_close(handle); ++ } else { ++ *_handle = handle; ++ } ++ ++ return ret; + } + + static int sss_semanage_user_add(semanage_handle_t *handle, +@@ -228,10 +242,11 @@ int set_seuser(const char *login_name, const char *seuser_name, + return EOK; + } + +- handle = sss_semanage_init(); +- if (!handle) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init SELinux management\n"); +- ret = EIO; ++ ret = sss_semanage_init(&handle); ++ if (ret == ERR_SELINUX_NOT_MANAGED) { ++ goto done; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n"); + goto done; + } + +@@ -295,10 +310,11 @@ int del_seuser(const char *login_name) + int ret; + int exists = 0; + +- handle = sss_semanage_init(); +- if (!handle) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init SELinux management\n"); +- ret = EIO; ++ ret = sss_semanage_init(&handle); ++ if (ret == ERR_SELINUX_NOT_MANAGED) { ++ goto done; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n"); + goto done; + } + +@@ -377,10 +393,11 @@ int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name, + semanage_seuser_t *sm_user = NULL; + semanage_seuser_key_t *sm_key = NULL; + +- sm_handle = sss_semanage_init(); +- if (sm_handle == NULL) { ++ ret = sss_semanage_init(&sm_handle); ++ if (ret == ERR_SELINUX_NOT_MANAGED) { ++ goto done; ++ } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n"); +- ret = EIO; + goto done; + } + +diff --git a/src/util/util_errors.c b/src/util/util_errors.c +index 466a3b4062f39b29d831a5d8a62dc8d576eb2e97..97eaf160f20bcc8cfe52254070a2d182e19addd4 100644 +--- a/src/util/util_errors.c ++++ b/src/util/util_errors.c +@@ -75,6 +75,7 @@ struct err_string error_to_str[] = { + { "Cannot connect to system bus" }, /* ERR_NO_SYSBUS */ + { "LDAP search returned a referral" }, /* ERR_REFERRAL */ + { "Error setting SELinux user context" }, /* ERR_SELINUX_CONTEXT */ ++ { "SELinux is not managed by libsemanage" }, /* ERR_SELINUX_NOT_MANAGED */ + { "Username format not allowed by re_expression" }, /* ERR_REGEX_NOMATCH */ + { "Time specification not supported" }, /* ERR_TIMESPEC_NOT_SUPPORTED */ + { "Invalid SSSD configuration detected" }, /* ERR_INVALID_CONFIG */ +diff --git a/src/util/util_errors.h b/src/util/util_errors.h +index 2f90c0a5d65325a431a8e4d9a480170808c9198e..4a250bf0339ba689680c155fa8e6d43f42c2467e 100644 +--- a/src/util/util_errors.h ++++ b/src/util/util_errors.h +@@ -97,6 +97,7 @@ enum sssd_errors { + ERR_NO_SYSBUS, + ERR_REFERRAL, + ERR_SELINUX_CONTEXT, ++ ERR_SELINUX_NOT_MANAGED, + ERR_REGEX_NOMATCH, + ERR_TIMESPEC_NOT_SUPPORTED, + ERR_INVALID_CONFIG, +-- +2.9.3 + diff --git a/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch b/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch new file mode 100644 index 0000000..c91d390 --- /dev/null +++ b/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch @@ -0,0 +1,49 @@ +From b2dcfa00dcb7b315a739d35ff6722a25b0ab5556 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 14 Mar 2017 10:34:00 +0100 +Subject: [PATCH 102/102] UTIL: Use max 15 characters for AD host UPN +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We do not want to use host principal with AD +"host/name.domain.tld@DOMAIN.TLD" because it does not work. +We need to use correct user principal for AD hosts. And we cannot +rely all fallback "*$" because of other principals in keytab. + +The NetBIOS naming convention allows for 16 characters in a NetBIOS +name. Microsoft, however, limits NetBIOS names to 15 characters and +uses the 16th character as a NetBIOS suffix. +https://support.microsoft.com/en-us/help/163409/netbios-suffixes-16th-character-of-the-netbios-name + +Resolves: +https://pagure.io/SSSD/sssd/issue/3329 + +Reviewed-by: Michal Židek +(cherry picked from commit c6f1bc32774a7cf2f8678499dfbced420be3a3a1) +--- + src/util/sss_krb5.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c +index d461cf881566af37f31524c16f6a5f1511a5dc89..a3f066e8add5b7d7575c1e0f537c5729e4a0dad0 100644 +--- a/src/util/sss_krb5.c ++++ b/src/util/sss_krb5.c +@@ -51,7 +51,13 @@ sss_krb5_get_primary(TALLOC_CTX *mem_ctx, + *c = toupper(*c); + } + +- primary = talloc_asprintf(mem_ctx, "%s$", shortname); ++ /* The samAccountName is recommended to be less than 20 characters. ++ * This is only for users and groups. For machine accounts, ++ * the real limit is caused by NetBIOS protocol. ++ * NetBIOS names are limited to 16 (15 + $) ++ * https://support.microsoft.com/en-us/help/163409/netbios-suffixes-16th-character-of-the-netbios-name ++ */ ++ primary = talloc_asprintf(mem_ctx, "%.15s$", shortname); + talloc_free(shortname); + return primary; + } +-- +2.9.3 + diff --git a/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch b/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch new file mode 100644 index 0000000..89b507c --- /dev/null +++ b/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch @@ -0,0 +1,300 @@ +From 84be2901aeb36ac60760cc11c424b717df360e87 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 19 Apr 2017 17:44:40 +0200 +Subject: [PATCH 103/104] Move sized_output_name() and sized_domain_name() into + responder common code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +These functions are used to format a name into a format that the user +configured for output, including case sensitiveness, replacing +whitespace and qualified format. They were used only in the NSS +responder, which typically returns strings to the NSS client library and +then the user. + +But it makes sense to just reuse the same code in the IFP responder as +well, since it does essentially the same job. + +The patch also renames sized_member_name to sized_domain_name. +Previously, the function was only used to format a group member, the IFP +responder would use the same function to format a group the user is a +member of. + +Related to: + https://pagure.io/SSSD/sssd/issue/3268 + +Reviewed-by: Pavel Březina +(cherry picked from commit 7c074ba2f923985ab0d4f9d6a5e01ff3f2f0a7a8) +--- + src/responder/common/responder.h | 21 ++++++++ + src/responder/common/responder_common.c | 90 +++++++++++++++++++++++++++++++++ + src/responder/nss/nss_private.h | 11 ---- + src/responder/nss/nss_protocol_grent.c | 2 +- + src/responder/nss/nss_utils.c | 87 ------------------------------- + 5 files changed, 112 insertions(+), 99 deletions(-) + +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index 4210307489fe25829a1674f254ecc7d185029698..dfe1ec455e355de263c3550306e53fea3ada85df 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -393,4 +393,25 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx, + + errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx); + ++/** ++ * Helper functions to format output names ++ */ ++ ++/* Format orig_name into a sized_string in output format as prescribed ++ * by the name_dom domain ++ */ ++int sized_output_name(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *orig_name, ++ struct sss_domain_info *name_dom, ++ struct sized_string **_name); ++ ++/* Format orig_name into a sized_string in output format as prescribed ++ * by the domain read from the fully qualified name. ++ */ ++int sized_domain_name(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *member_name, ++ struct sized_string **_name); ++ + #endif /* __SSS_RESPONDER_H__ */ +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 67e1deefdfde19c95a68029b11099579d851513f..ac6320b08de09bc6c7e8dd1af72e0a493a449f7a 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -1651,3 +1651,93 @@ done: + + return ret; + } ++ ++/** ++ * Helper functions to format output names ++ */ ++int sized_output_name(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *orig_name, ++ struct sss_domain_info *name_dom, ++ struct sized_string **_name) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ errno_t ret; ++ char *username; ++ struct sized_string *name; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve, ++ rctx->override_space); ++ if (username == NULL) { ++ ret = EIO; ++ goto done; ++ } ++ ++ if (name_dom->fqnames) { ++ username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username); ++ if (username == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n"); ++ ret = EIO; ++ goto done; ++ } ++ } ++ ++ name = talloc_zero(tmp_ctx, struct sized_string); ++ if (name == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ to_sized_string(name, username); ++ name->str = talloc_steal(name, username); ++ *_name = talloc_steal(mem_ctx, name); ++ ret = EOK; ++done: ++ talloc_zfree(tmp_ctx); ++ return ret; ++} ++ ++int sized_domain_name(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *member_name, ++ struct sized_string **_name) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ errno_t ret; ++ char *domname; ++ struct sss_domain_info *member_dom; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sss_parse_internal_fqname(tmp_ctx, member_name, NULL, &domname); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n"); ++ goto done; ++ } ++ ++ if (domname == NULL) { ++ ret = ERR_WRONG_NAME_FORMAT; ++ goto done; ++ } ++ ++ member_dom = find_domain_by_name(get_domains_head(rctx->domains), ++ domname, true); ++ if (member_dom == NULL) { ++ ret = ERR_DOMAIN_NOT_FOUND; ++ goto done; ++ } ++ ++ ret = sized_output_name(mem_ctx, rctx, member_name, ++ member_dom, _name); ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} +diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h +index acb3c4aa504e538ca56dca8d43ee04b0f60954a9..13de83226177bbaa8b8237e3e27b7e72da369194 100644 +--- a/src/responder/nss/nss_private.h ++++ b/src/responder/nss/nss_private.h +@@ -140,17 +140,6 @@ const char * + nss_get_name_from_msg(struct sss_domain_info *domain, + struct ldb_message *msg); + +-int sized_output_name(TALLOC_CTX *mem_ctx, +- struct resp_ctx *rctx, +- const char *orig_name, +- struct sss_domain_info *name_dom, +- struct sized_string **_name); +- +-int sized_member_name(TALLOC_CTX *mem_ctx, +- struct resp_ctx *rctx, +- const char *member_name, +- struct sized_string **_name); +- + const char * + nss_get_pwfield(struct nss_ctx *nctx, + struct sss_domain_info *dom); +diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c +index 283ab9f6731bc4c8261ca79075ab030005bf70db..fae1d47d7b217beafba75740e2e6d9cb8cdbc1d0 100644 +--- a/src/responder/nss/nss_protocol_grent.c ++++ b/src/responder/nss/nss_protocol_grent.c +@@ -163,7 +163,7 @@ nss_protocol_fill_members(struct sss_packet *packet, + } + } + +- ret = sized_member_name(tmp_ctx, rctx, member_name, &name); ++ ret = sized_domain_name(tmp_ctx, rctx, member_name, &name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to get sized name [%d]: %s\n", + ret, sss_strerror(ret)); +diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c +index f839930a275db56e8d729888af870562d7b6f260..2cd9c33b42f7e018ea89d2df206637f35646489e 100644 +--- a/src/responder/nss/nss_utils.c ++++ b/src/responder/nss/nss_utils.c +@@ -53,93 +53,6 @@ nss_get_name_from_msg(struct sss_domain_info *domain, + return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + } + +-int sized_output_name(TALLOC_CTX *mem_ctx, +- struct resp_ctx *rctx, +- const char *orig_name, +- struct sss_domain_info *name_dom, +- struct sized_string **_name) +-{ +- TALLOC_CTX *tmp_ctx = NULL; +- errno_t ret; +- char *username; +- struct sized_string *name; +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- return ENOMEM; +- } +- +- username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve, +- rctx->override_space); +- if (username == NULL) { +- ret = EIO; +- goto done; +- } +- +- if (name_dom->fqnames) { +- username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username); +- if (username == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n"); +- ret = EIO; +- goto done; +- } +- } +- +- name = talloc_zero(tmp_ctx, struct sized_string); +- if (name == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- to_sized_string(name, username); +- name->str = talloc_steal(name, username); +- *_name = talloc_steal(mem_ctx, name); +- ret = EOK; +-done: +- talloc_zfree(tmp_ctx); +- return ret; +-} +- +-int sized_member_name(TALLOC_CTX *mem_ctx, +- struct resp_ctx *rctx, +- const char *member_name, +- struct sized_string **_name) +-{ +- TALLOC_CTX *tmp_ctx = NULL; +- errno_t ret; +- char *domname; +- struct sss_domain_info *member_dom; +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- return ENOMEM; +- } +- +- ret = sss_parse_internal_fqname(tmp_ctx, member_name, NULL, &domname); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n"); +- goto done; +- } +- +- if (domname == NULL) { +- ret = ERR_WRONG_NAME_FORMAT; +- goto done; +- } +- +- member_dom = find_domain_by_name(get_domains_head(rctx->domains), +- domname, true); +- if (member_dom == NULL) { +- ret = ERR_DOMAIN_NOT_FOUND; +- goto done; +- } +- +- ret = sized_output_name(mem_ctx, rctx, member_name, +- member_dom, _name); +-done: +- talloc_free(tmp_ctx); +- return ret; +-} +- + const char * + nss_get_pwfield(struct nss_ctx *nctx, + struct sss_domain_info *dom) +-- +2.9.3 + diff --git a/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch b/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch new file mode 100644 index 0000000..8d252f9 --- /dev/null +++ b/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch @@ -0,0 +1,95 @@ +From 956d7e794d6c07eec3c0009253c8a86320c3e741 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 19 Apr 2017 17:46:03 +0200 +Subject: [PATCH 104/104] IFP: Use sized_domain_name to format the groups the + user is a member of +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Resolves: + https://pagure.io/SSSD/sssd/issue/3268 + +Uses the common function sized_domain_name() to format a group the user +is a member of to the appropriate format. + +To see the code is working correctly, run: + dbus-send --system --print-reply --dest=org.freedesktop.sssd.infopipe + /org/freedesktop/sssd/infopipe + org.freedesktop.sssd.infopipe.GetUserGroups + string:trusted_user + +Where trusted_user is a user from a trusted domain that is a member of groups +from the joined domain and a trusted domain as well. The groups from the +joined domain should not be qualified, the groups from the trusted +domain should be qualified. + +Reviewed-by: Pavel Březina +(cherry picked from commit c9a73bb6ffa010ef206896a0d1c2801bc056fa45) +--- + src/responder/ifp/ifpsrv_cmd.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index d10f35e41dbb1623a0b9de37a4c43363cbefc1a3..e4d6c42ef35ef372472803d3d26b17d4181021a8 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -369,10 +369,11 @@ ifp_user_get_groups_reply(struct sss_domain_info *domain, + struct ifp_req *ireq, + struct ldb_result *res) + { +- int i, num; ++ int i, gri, num; + const char *name; + const char **groupnames; +- char *out_name; ++ struct sized_string *group_name; ++ errno_t ret; + + /* one less, the first one is the user entry */ + num = res->count - 1; +@@ -381,6 +382,7 @@ ifp_user_get_groups_reply(struct sss_domain_info *domain, + return sbus_request_finish(ireq->dbus_req, NULL); + } + ++ gri = 0; + for (i = 0; i < num; i++) { + name = sss_view_ldb_msg_find_attr_as_string(domain, + res->msgs[i + 1], +@@ -390,22 +392,21 @@ ifp_user_get_groups_reply(struct sss_domain_info *domain, + continue; + } + +- out_name = sss_output_name(ireq, name, domain->case_preserve, +- ireq->ifp_ctx->rctx->override_space); +- if (out_name == NULL) { ++ ret = sized_domain_name(ireq, ireq->ifp_ctx->rctx, name, &group_name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Unable to get sized name for %s [%d]: %s\n", ++ name, ret, sss_strerror(ret)); + continue; + } + +- if (domain->fqnames) { +- groupnames[i] = sss_tc_fqname(groupnames, domain->names, +- domain, out_name); +- if (out_name == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n"); +- continue; +- } +- } else { +- groupnames[i] = talloc_steal(groupnames, out_name); ++ groupnames[gri] = talloc_strndup(groupnames, ++ group_name->str, group_name->len); ++ if (groupnames[gri] == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "talloc_strndup failed\n"); ++ continue; + } ++ gri++; + + DEBUG(SSSDBG_TRACE_FUNC, "Adding group %s\n", groupnames[i]); + } +-- +2.9.3 + diff --git a/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch b/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch new file mode 100644 index 0000000..e0094c0 --- /dev/null +++ b/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch @@ -0,0 +1,313 @@ +From b7d2310e9ddd79bfdea2bc334bd11d4df9be37a2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 12 Apr 2017 10:43:25 +0200 +Subject: [PATCH 105/110] RESPONDER: Fallback to global domain resolution order + in case the view doesn't have this option set +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current code has been ignoring the domain resolution order set +globally on IPA in case there's a view but this doesn't have any domain +resolution order set. + +It happens because we haven't been checking whether the view attribute +didn't exist and then we ended up populating the list cache_req domains' +list assuming that no order has been set instead of falling back to the +next preferred method. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit a3faad0e4dc1ca4473746c3822ecfc5aed876e6d) +--- + src/responder/common/cache_req/cache_req_domain.c | 14 ++- + src/responder/common/cache_req/cache_req_domain.h | 5 +- + src/responder/common/responder_common.c | 108 +++++++++++++--------- + 3 files changed, 74 insertions(+), 53 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index bbabd695f1c6b6c29b7e61f571382ab9adfb0ea2..86a88efd54ca0f4a0748b44ece1b8515438d4628 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -120,20 +120,21 @@ done: + return cr_domains; + } + +-struct cache_req_domain * ++errno_t + cache_req_domain_new_list_from_domain_resolution_order( + TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, +- const char *domain_resolution_order) ++ const char *domain_resolution_order, ++ struct cache_req_domain **_cr_domains) + { + TALLOC_CTX *tmp_ctx; +- struct cache_req_domain *cr_domains = NULL; ++ struct cache_req_domain *cr_domains; + char **list = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +- return NULL; ++ return ENOMEM; + } + + if (domain_resolution_order != NULL) { +@@ -160,7 +161,10 @@ cache_req_domain_new_list_from_domain_resolution_order( + goto done; + } + ++ *_cr_domains = cr_domains; ++ ret = EOK; ++ + done: + talloc_free(tmp_ctx); +- return cr_domains; ++ return ret; + } +diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h +index 41c50e8c293d7b032cb2f05482c40e93e4f723dc..000087e5ca2074f22169a4af627810f4f287e430 100644 +--- a/src/responder/common/cache_req/cache_req_domain.h ++++ b/src/responder/common/cache_req/cache_req_domain.h +@@ -34,11 +34,12 @@ struct cache_req_domain * + cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, + const char *name); + +-struct cache_req_domain * ++errno_t + cache_req_domain_new_list_from_domain_resolution_order( + TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, +- const char *domain_resolution_order); ++ const char *domain_resolution_order, ++ struct cache_req_domain **_cr_domains); + + void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains); + +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index ac6320b08de09bc6c7e8dd1af72e0a493a449f7a..62b71b5104fdbb585d086d44d2ca2ab9717dd788 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -1486,10 +1486,11 @@ fail: + } + + /* ====== Helper functions for the domain resolution order ======= */ +-static struct cache_req_domain * ++static errno_t + sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, +- struct sysdb_ctx *sysdb) ++ struct sysdb_ctx *sysdb, ++ struct cache_req_domain **_cr_domains) + { + TALLOC_CTX *tmp_ctx; + struct cache_req_domain *cr_domains = NULL; +@@ -1498,7 +1499,7 @@ sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx, + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +- return NULL; ++ return ENOMEM; + } + + ret = sysdb_get_view_domain_resolution_order(tmp_ctx, sysdb, +@@ -1510,12 +1511,13 @@ sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx, + goto done; + } + +- /* Using mem_ctx (which is rctx) directly here to avoid copying +- * this memory around. */ +- cr_domains = cache_req_domain_new_list_from_domain_resolution_order( +- mem_ctx, domains, domain_resolution_order); +- if (cr_domains == NULL) { +- ret = ENOMEM; ++ if (ret == ENOENT) { ++ goto done; ++ } ++ ++ ret = cache_req_domain_new_list_from_domain_resolution_order( ++ mem_ctx, domains, domain_resolution_order, &cr_domains); ++ if (ret != EOK) { + DEBUG(SSSDBG_DEFAULT, + "cache_req_domain_new_list_from_domain_resolution_order() " + "failed [%d]: [%s].\n", +@@ -1523,25 +1525,31 @@ sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx, + goto done; + } + ++ *_cr_domains = cr_domains; ++ ++ ret = EOK; ++ + done: + talloc_free(tmp_ctx); +- return cr_domains; ++ return ret; + } + +-static struct cache_req_domain * ++static errno_t + sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + struct sysdb_ctx *sysdb, +- const char *domain) ++ const char *domain, ++ struct cache_req_domain **_cr_domains) + { + TALLOC_CTX *tmp_ctx; +- struct cache_req_domain *cr_domains = NULL; + const char *domain_resolution_order = NULL; + errno_t ret; + ++ *_cr_domains = NULL; ++ + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +- return NULL; ++ return ENOMEM; + } + + ret = sysdb_domain_get_domain_resolution_order(tmp_ctx, sysdb, domain, +@@ -1554,11 +1562,13 @@ sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx, + goto done; + } + +- /* Using mem_ctx (which is rctx) directly here to avoid copying +- * this memory around. */ +- cr_domains = cache_req_domain_new_list_from_domain_resolution_order( +- mem_ctx, domains, domain_resolution_order); +- if (cr_domains == NULL) { ++ if (ret == ENOENT) { ++ goto done; ++ } ++ ++ ret = cache_req_domain_new_list_from_domain_resolution_order( ++ mem_ctx, domains, domain_resolution_order, _cr_domains); ++ if (ret != EOK) { + DEBUG(SSSDBG_DEFAULT, + "cache_req_domain_new_list_from_domain_resolution_order() " + "failed [%d]: [%s].\n", +@@ -1566,9 +1576,11 @@ sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx, + goto done; + } + ++ ret = EOK; ++ + done: + talloc_free(tmp_ctx); +- return cr_domains; ++ return ret; + } + + errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) +@@ -1578,16 +1590,16 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + errno_t ret; + + if (rctx->domain_resolution_order != NULL) { +- cr_domains = cache_req_domain_new_list_from_domain_resolution_order( +- rctx, rctx->domains, rctx->domain_resolution_order); +- +- if (cr_domains == NULL) { ++ ret = cache_req_domain_new_list_from_domain_resolution_order( ++ rctx, rctx->domains, ++ rctx->domain_resolution_order, &cr_domains); ++ if (ret == EOK) { ++ goto done; ++ } else { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to use domain_resolution_order set in the config file.\n" + "Trying to fallback to use ipaDomainOrderResolution setup by " + "IPA.\n"); +- } else { +- goto done; + } + } + +@@ -1598,9 +1610,9 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + } + + if (dom == NULL) { +- cr_domains = cache_req_domain_new_list_from_domain_resolution_order( +- rctx, rctx->domains, NULL); +- if (cr_domains == NULL) { ++ ret = cache_req_domain_new_list_from_domain_resolution_order( ++ rctx, rctx->domains, NULL, &cr_domains); ++ if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to flatten the list of domains.\n"); + } +@@ -1608,44 +1620,48 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + } + + if (dom->has_views) { +- cr_domains = sss_resp_new_cr_domains_from_ipa_id_view(rctx, +- rctx->domains, +- dom->sysdb); +- if (cr_domains == NULL) { ++ ret = sss_resp_new_cr_domains_from_ipa_id_view(rctx, rctx->domains, ++ dom->sysdb, ++ &cr_domains); ++ if (ret == EOK) { ++ goto done; ++ } ++ ++ if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to use ipaDomainResolutionOrder set for the " + "view \"%s\".\n" + "Trying to fallback to use ipaDomainOrderResolution " + "set in ipaConfig for the domain: %s.\n", + dom->view_name, dom->name); +- } else { +- goto done; + } + } + +- cr_domains = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains, +- dom->sysdb, +- dom->name); +- if (cr_domains == NULL) { ++ ret = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains, ++ dom->sysdb, dom->name, ++ &cr_domains); ++ if (ret == EOK) { ++ goto done; ++ } ++ ++ if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to use ipaDomainResolutionOrder set in ipaConfig " + "for the domain: \"%s\".\n" + "No ipaDomainResolutionOrder will be followed.\n", + dom->name); +- } else { +- goto done; + } + +- cr_domains = cache_req_domain_new_list_from_domain_resolution_order( +- rctx, rctx->domains, NULL); +- if (cr_domains == NULL) { ++ ret = cache_req_domain_new_list_from_domain_resolution_order( ++ rctx, rctx->domains, NULL, &cr_domains); ++ if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to flatten the list of domains.\n"); + goto done; + } + +-done: +- ret = cr_domains != NULL ? EOK : ENOMEM; ++ ret = EOK; + ++done: + cache_req_domain_list_zfree(&rctx->cr_domains); + rctx->cr_domains = cr_domains; + +-- +2.9.3 + diff --git a/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch b/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch new file mode 100644 index 0000000..fce885a --- /dev/null +++ b/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch @@ -0,0 +1,164 @@ +From b4b409f2c5bd0f0b26015b0562ae0ee0e831da82 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 17 Apr 2017 09:32:39 +0200 +Subject: [PATCH 106/110] NSS/TESTS: Improve non-fqnames tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With the changes that are about to happen we have to have the subdomain's +fqnames flag set by the time we populate the cr_domains list (as it +actually occurs with the real code), as this list may set its own fqnames +flag based on the subdomain's fqnames flag. + +Currently the flag is set to false only when running the tests itself so +the cr_domains list doesn't get populate properly (although it still +works with the current code). + +For the changes that are comming, let's introduce a new setup function +that ensures that the subdomain's fqnames flag is set up in the right +time. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit ed518f61f1a5d4cf5d87eec492c158725a73d6a1) +--- + src/tests/cmocka/test_nss_srv.c | 45 +++++++++++++++++++++++++++-------------- + 1 file changed, 30 insertions(+), 15 deletions(-) + +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 2f526660cbbbf2443dbae4e213c1336feb6c661e..8c72f44f1869558893627e1f2f91b5f3b96c6317 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -1709,8 +1709,6 @@ void test_nss_getgrnam_members_subdom_nonfqnames(void **state) + { + errno_t ret; + +- nss_test_ctx->subdom->fqnames = false; +- + mock_input_user_or_group("testsubdomgroup"); + mock_account_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); +@@ -1802,8 +1800,6 @@ void test_nss_getgrnam_mix_dom_nonfqnames(void **state) + { + errno_t ret; + +- nss_test_ctx->subdom->fqnames = false; +- + ret = store_group_member(nss_test_ctx, + testgroup_members.gr_name, + nss_test_ctx->tctx->dom, +@@ -1917,6 +1913,7 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state) + assert_int_equal(ret, EOK); + } + ++ + void test_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state) + { + errno_t ret; +@@ -1929,10 +1926,6 @@ void test_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state) + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + +- nss_test_ctx->tctx->dom->fqnames = false; +- nss_test_ctx->subdom->fqnames = false; +- +- + mock_input_user_or_group("testgroup_members"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); +@@ -2044,8 +2037,6 @@ void test_nss_getgrnam_mix_subdom_nonfqnames(void **state) + { + errno_t ret; + +- nss_test_ctx->subdom->fqnames = false; +- + ret = store_group_member(nss_test_ctx, + testsubdomgroup.gr_name, + nss_test_ctx->subdom, +@@ -3417,9 +3408,11 @@ static int nss_test_setup_extra_attr(void **state) + return 0; + } + +-static int nss_subdom_test_setup(void **state) ++static int nss_subdom_test_setup_common(void **state, bool nonfqnames) + { + const char *const testdom[4] = { TEST_SUBDOM_NAME, "TEST.SUB", "test", "S-3" }; ++ struct sss_domain_info *dom; ++ + struct sss_domain_info *subdomain; + errno_t ret; + +@@ -3440,6 +3433,17 @@ static int nss_subdom_test_setup(void **state) + nss_test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + ++ if (nonfqnames) { ++ for (dom = nss_test_ctx->rctx->domains; ++ dom != NULL; ++ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { ++ if (strcmp(dom->name, subdomain->name) == 0) { ++ dom->fqnames = false; ++ break; ++ } ++ } ++ } ++ + ret = sss_resp_populate_cr_domains(nss_test_ctx->rctx); + assert_int_equal(ret, EOK); + assert_non_null(nss_test_ctx->rctx->cr_domains); +@@ -3475,6 +3479,17 @@ static int nss_subdom_test_setup(void **state) + assert_int_equal(ret, EOK); + + return 0; ++ ++} ++ ++static int nss_subdom_test_setup(void **state) ++{ ++ return nss_subdom_test_setup_common(state, false); ++} ++ ++static int nss_subdom_test_setup_nonfqnames(void **state) ++{ ++ return nss_subdom_test_setup_common(state, true); + } + + static int nss_fqdn_fancy_test_setup(void **state) +@@ -4192,25 +4207,25 @@ int main(int argc, const char *argv[]) + nss_subdom_test_setup, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom_nonfqnames, +- nss_subdom_test_setup, ++ nss_subdom_test_setup_nonfqnames, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom, + nss_subdom_test_setup, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_nonfqnames, +- nss_subdom_test_setup, ++ nss_subdom_test_setup_nonfqnames, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn, + nss_subdom_test_setup, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn_nonfqnames, +- nss_subdom_test_setup, ++ nss_subdom_test_setup_nonfqnames, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom, + nss_subdom_test_setup, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom_nonfqnames, +- nss_subdom_test_setup, ++ nss_subdom_test_setup_nonfqnames, + nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getgrnam_space, + nss_test_setup, nss_test_teardown), +-- +2.9.3 + diff --git a/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch b/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch new file mode 100644 index 0000000..352e52d --- /dev/null +++ b/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch @@ -0,0 +1,139 @@ +From 7c6fd66fa9ca942bc240b49f903d9d3d85340c4c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 11 Apr 2017 17:19:29 +0200 +Subject: [PATCH 107/110] CACHE_REQ: Allow configurationless shortname lookups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Configurationless shortnames lookups must be allowed when a domains' +resolution order is present and the (head) domain is not enforcing the +usage of fully-qualified-names. + +With this patch SSSD does not require any kind of changes from client +side for taking advantage of shortname lookups. + +Related: +https://pagure.io/SSSD/sssd/issue/3001 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit dae798231fc2c575f213785768bc24ed765ba243) +--- + src/responder/common/cache_req/cache_req.c | 2 +- + src/responder/common/cache_req/cache_req_domain.c | 48 +++++++++++++++++++++++ + src/responder/common/cache_req/cache_req_domain.h | 1 + + 3 files changed, 50 insertions(+), 1 deletion(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index 3a5fecf34427437bbf95317e05c5bd8b07b4537d..797325a30e6c1ed5f1d4b4c147c65391d5204b52 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -480,7 +480,7 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req) + * qualified names on domain less search. We do not descend into + * subdomains here since those are implicitly qualified. + */ +- if (state->check_next && !allow_no_fqn && domain->fqnames) { ++ if (state->check_next && !allow_no_fqn && state->cr_domain->fqnames) { + state->cr_domain = state->cr_domain->next; + continue; + } +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index 86a88efd54ca0f4a0748b44ece1b8515438d4628..bfdd2b7f640178f6d0a0d92f2fed329c856b478c 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -60,6 +60,48 @@ void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains) + *cr_domains = NULL; + } + ++static bool ++cache_req_domain_use_fqnames(struct sss_domain_info *domain, ++ bool enforce_non_fqnames) ++{ ++ struct sss_domain_info *head; ++ ++ head = get_domains_head(domain); ++ ++ /* ++ * In order to decide whether fully_qualified_names must be used on the ++ * lookups we have to take into consideration: ++ * - use_fully_qualified_name value of the head of the domains; ++ * (head->fqnames) ++ * - the presence of a domains' resolution order list; ++ * (non_fqnames_enforced) ++ * ++ * The relationship between those two can be described by: ++ * - head->fqnames: ++ * - true: in this case doesn't matter whether it's enforced or not, ++ * fully-qualified-names will _always_ be used ++ * - false: in this case (which is also the default case), the usage ++ * depends on it being enforced; ++ * ++ * - enforce_non_fqnames: ++ * - true: in this case, the usage of fully-qualified-names is not ++ * needed; ++ * - false: in this case, the usage of fully-qualified-names will be ++ * done accordingly to what's set for the domain itself. ++ */ ++ switch (head->fqnames) { ++ case true: ++ return true; ++ case false: ++ switch (enforce_non_fqnames) { ++ case true: ++ return false; ++ case false: ++ return domain->fqnames; ++ } ++ } ++} ++ + static struct cache_req_domain * + cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, +@@ -71,9 +113,11 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + char *name; + int flag = SSS_GND_ALL_DOMAINS; + int i; ++ bool enforce_non_fqnames = false; + errno_t ret; + + if (resolution_order != NULL) { ++ enforce_non_fqnames = true; + for (i = 0; resolution_order[i] != NULL; i++) { + name = resolution_order[i]; + for (dom = domains; dom; dom = get_next_domain(dom, flag)) { +@@ -87,6 +131,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + goto done; + } + cr_domain->domain = dom; ++ cr_domain->fqnames = ++ cache_req_domain_use_fqnames(dom, enforce_non_fqnames); + + DLIST_ADD_END(cr_domains, cr_domain, + struct cache_req_domain *); +@@ -106,6 +152,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + goto done; + } + cr_domain->domain = dom; ++ cr_domain->fqnames = ++ cache_req_domain_use_fqnames(dom, enforce_non_fqnames); + + DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); + } +diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h +index 000087e5ca2074f22169a4af627810f4f287e430..5bcbb9b493caf05bf71aac5cf7633ded91f22e73 100644 +--- a/src/responder/common/cache_req/cache_req_domain.h ++++ b/src/responder/common/cache_req/cache_req_domain.h +@@ -25,6 +25,7 @@ + + struct cache_req_domain { + struct sss_domain_info *domain; ++ bool fqnames; + + struct cache_req_domain *prev; + struct cache_req_domain *next; +-- +2.9.3 + diff --git a/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch b/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch new file mode 100644 index 0000000..86ba24f --- /dev/null +++ b/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch @@ -0,0 +1,42 @@ +From 3d55506f2e6584d412ca07f2d0d77375aae48ba9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 24 Apr 2017 21:04:58 +0200 +Subject: [PATCH 108/110] CACHE_REQ_DOMAIN: Add some comments to + cache_req_domain_new_list_from_string_list() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit f9bac02756aa05cc9c6ac07ae581dba67240c1a4) +--- + src/responder/common/cache_req/cache_req_domain.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index bfdd2b7f640178f6d0a0d92f2fed329c856b478c..6d37db0f109d5343eb6d7f4272bea522d4c34cf7 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -116,6 +116,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + bool enforce_non_fqnames = false; + errno_t ret; + ++ /* Firstly, in case a domains' resolution order is passed ... iterate over ++ * the list adding its domains to the flatten cache req domains' list */ + if (resolution_order != NULL) { + enforce_non_fqnames = true; + for (i = 0; resolution_order[i] != NULL; i++) { +@@ -141,6 +143,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + } + } + ++ /* Then iterate through all the other domains (and subdomains) and add them ++ * to the flatten cache req domains' list */ + for (dom = domains; dom; dom = get_next_domain(dom, flag)) { + if (string_in_list(dom->name, resolution_order, false)) { + continue; +-- +2.9.3 + diff --git a/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch b/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch new file mode 100644 index 0000000..a1abb10 --- /dev/null +++ b/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch @@ -0,0 +1,54 @@ +From 326442dc734de72b950a47c5fe2b3ac6a1dfc35e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 24 Apr 2017 21:09:02 +0200 +Subject: [PATCH 109/110] RESPONDER_COMMON: Improve domaiN_resolution_order + debug messages +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Indicate whether a domain_resolution_order has been used and where +it came from. + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit 213048fd9a5e800deb74cb5b7f0eaf465945c640) +--- + src/responder/common/responder_common.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 62b71b5104fdbb585d086d44d2ca2ab9717dd788..7496d293fddb3e947d59a4f2aaeb2c83234dfcc7 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -1594,6 +1594,8 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + rctx, rctx->domains, + rctx->domain_resolution_order, &cr_domains); + if (ret == EOK) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Using domain_resolution_order from sssd.conf\n"); + goto done; + } else { + DEBUG(SSSDBG_MINOR_FAILURE, +@@ -1624,6 +1626,8 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + dom->sysdb, + &cr_domains); + if (ret == EOK) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Using domain_resolution_order from IPA ID View\n"); + goto done; + } + +@@ -1641,6 +1645,8 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx) + dom->sysdb, dom->name, + &cr_domains); + if (ret == EOK) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Using domain_resolution_order from IPA Config\n"); + goto done; + } + +-- +2.9.3 + diff --git a/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch b/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch new file mode 100644 index 0000000..8507ad0 --- /dev/null +++ b/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch @@ -0,0 +1,50 @@ +From 3671f188ff9e379022d62eaf7171f397f04ac153 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 25 Apr 2017 14:25:12 +0200 +Subject: [PATCH 110/110] CACHE_REQ_DOMAIN: debug the set domain resolution + order +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit b78febe4c579f86f8007a27599605d1eb9f97a62) +--- + src/responder/common/cache_req/cache_req_domain.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index 6d37db0f109d5343eb6d7f4272bea522d4c34cf7..2c238c9966d322bb542fa2047313ee9e5144edee 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -191,6 +191,10 @@ cache_req_domain_new_list_from_domain_resolution_order( + + if (domain_resolution_order != NULL) { + if (strcmp(domain_resolution_order, ":") != 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Domain resolution order list (split by ':'): \"%s\"\n", ++ domain_resolution_order); ++ + ret = split_on_separator(tmp_ctx, domain_resolution_order, ':', + true, true, &list, NULL); + if (ret != EOK) { +@@ -199,7 +203,14 @@ cache_req_domain_new_list_from_domain_resolution_order( + ret, sss_strerror(ret)); + goto done; + } ++ } else { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Domain resolution order list: ':' " ++ "(do not use any specific order)\n"); + } ++ } else { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Domain resolution order list: not set\n"); + } + + cr_domains = cache_req_domain_new_list_from_string_list(mem_ctx, domains, +-- +2.9.3 + diff --git a/SOURCES/0111-SECRETS-remove-unused-variable.patch b/SOURCES/0111-SECRETS-remove-unused-variable.patch new file mode 100644 index 0000000..1e1d87c --- /dev/null +++ b/SOURCES/0111-SECRETS-remove-unused-variable.patch @@ -0,0 +1,33 @@ +From 429c282e54feb0e1c9ac27d23be6a8c1d4119976 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Wed, 19 Apr 2017 17:56:20 +0200 +Subject: [PATCH 111/118] SECRETS: remove unused variable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Pavel Březina +(cherry picked from commit 0e8f0c06cad5805b1a1161f60e3f2cdb7a5a2921) +--- + src/responder/secrets/proxy.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c +index fd96e985c897e2cb470a9b5d6eecbd34350fb7d2..9c2aa425d414728d10aa830f640632e98def3c1c 100644 +--- a/src/responder/secrets/proxy.c ++++ b/src/responder/secrets/proxy.c +@@ -570,11 +570,6 @@ static void proxy_secret_req_done(struct tevent_req *subreq) + } + } + +-struct provider_handle proxy_secrets_handle = { +- .fn = proxy_secret_req, +- .context = NULL, +-}; +- + int proxy_secrets_provider_handle(struct sec_ctx *sctx, + struct provider_handle **out_handle) + { +-- +2.9.3 + diff --git a/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch b/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch new file mode 100644 index 0000000..77c2a5b --- /dev/null +++ b/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch @@ -0,0 +1,42 @@ +From 396849b6160594dbb6dedec5d1bd7fbc3af12cdd Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 21 Apr 2017 12:39:44 +0200 +Subject: [PATCH 112/118] IPA: Improve DEBUG message if a group has no + ipaNTSecurityIdentifier +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There was an issue in a production deployment where the admin selected a +GID outside the IDM range for a group that contained a user from the +trusted domain. This resulted in not adding a SID for the IPA group, +which in turn meant the group couldn't be resolved on the client. + +This patch just improves the DEBUG message so that it's clearer for the +admins where the issue is. + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit ef019268d2d112ebff3577e551cd19478d73d93b) +--- + src/providers/ipa/ipa_s2n_exop.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 2173db357700499a6140aa61841e443139981483..55ec904ca3188c7cf10ac41972e9ecf94ebf44bb 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -1308,7 +1308,10 @@ static void ipa_s2n_get_list_next(struct tevent_req *subreq) + ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR, + &sid_str); + if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Object [%s] has no SID, please check the " ++ "ipaNTSecurityIdentifier attribute on the server-side", ++ state->attrs->a.name); + goto fail; + } + +-- +2.9.3 + diff --git a/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch b/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch new file mode 100644 index 0000000..a2ccf0b --- /dev/null +++ b/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch @@ -0,0 +1,45 @@ +From 522dffca552146c0af74325b6ceab0ca950bbc1a Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Tue, 25 Apr 2017 13:02:10 -0400 +Subject: [PATCH 113/118] IPA: Improve s2n debug message for missing + ipaNTSecurityIdentifier +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch improves the log message to be more information for +the SSSD user troubleshooting issues. + +If the IDM POSIX group used for AD trust HBAC/SUDO operation is missing +the ipaNTSecurityIdentifier it can cause client s2n operations failures +resolving the group which resulted in the inability to login for the AD +user. + +Reviewed-by: Pavel Březina +(cherry picked from commit 0c5f463e9629ac08d647c70cffb30bccdd57ae96) +--- + src/providers/ipa/ipa_s2n_exop.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 55ec904ca3188c7cf10ac41972e9ecf94ebf44bb..f5f4401f86615dc7f81f844e1096ad43e965c384 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -2580,7 +2580,13 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom, + ret = sysdb_attrs_get_string(attrs->sysdb_attrs, SYSDB_SID_STR, &sid_str); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "Cannot find SID of object with override.\n"); ++ "Cannot find SID of object.\n"); ++ if (name != NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Object [%s] has no SID, please check the " ++ "ipaNTSecurityIdentifier attribute on the server-side.\n", ++ name); ++ } + goto done; + } + +-- +2.9.3 + diff --git a/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch b/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch new file mode 100644 index 0000000..9d2968e --- /dev/null +++ b/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch @@ -0,0 +1,128 @@ +From 8441030009c22daa835f89dbc36365415524b320 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 31 Mar 2017 17:12:56 +0200 +Subject: [PATCH 114/118] CONFDB: Fix standalone application domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When a standalone application domain was configured, for example: + +------------------------------------------------- +[sssd] +domains = appdomain + +[application/appdomain] +id_provider=ldap +ldap_uri = ldap://dc.ipa.test +ldap_search_base = cn=accounts,dc=ipa,dc=test +ldap_schema = rfc2307bis +sudo_provider = none + +ldap_sasl_mech = gssapi +krb5_realm = IPA.TEST +krb5_server = dc.ipa.test + +ldap_user_uid_number = telephonenumber +ldap_user_gid_number = mobile +ldap_user_extra_attrs = location:l +------------------------------------------------- + +We would, when unrolling the application section into a domain section, +first add a domain stub, equivalent to: +----------------------------- +[domain/appdomain] +domain_type = application +----------------------------- + +Which in config.ldb also contains cn. Then, whem we would add the parameters +from the [application] section, but try to add the cn again. + +This didn't happen when inheriting from a POSIX domain, because there we +would set LDB_FLAG_REPLACE for any attributes that exist in the inherited +domain. + +This patch skips the cn attribute both when replacing an inherited +domain's attributes and when writing a standalone application domain. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3355 + +Reviewed-by: Pavel Březina +(cherry picked from commit 734e73257fff1c1884b72b8cf988f6d75c3a7567) +--- + src/confdb/confdb.c | 26 ++++++++++++++++++++++---- + 1 file changed, 22 insertions(+), 4 deletions(-) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index 88e114457deac3ca50c291a131122624fb6f6fe4..5bb593de03cc2fb26218b883fd1d753e31bedc2d 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -1909,7 +1909,7 @@ static int confdb_add_app_domain(TALLOC_CTX *mem_ctx, + + cdb_path = talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL, name); + if (cdb_path == NULL) { +- return ENOMEM; ++ return ENOMEM; + } + + val[0] = CONFDB_DOMAIN_TYPE_APP; +@@ -1933,6 +1933,7 @@ static int confdb_merge_parent_domain(const char *name, + struct ldb_message *replace_msg = NULL; + struct ldb_message *app_msg = NULL; + struct ldb_dn *domain_dn; ++ struct ldb_message_element *el = NULL; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); +@@ -1974,6 +1975,12 @@ static int confdb_merge_parent_domain(const char *name, + replace_msg->elements[i].flags = LDB_FLAG_MOD_ADD; + } + ++ el = ldb_msg_find_element(replace_msg, "cn"); ++ if (el != NULL) { ++ /* Don't add second cn */ ++ ldb_msg_remove_element(replace_msg, el); ++ } ++ + ret = ldb_modify(cdb->ldb, replace_msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); +@@ -1993,7 +2000,14 @@ static int confdb_merge_parent_domain(const char *name, + app_msg->dn = domain_dn; + + for (unsigned i = 0; i < app_section->msgs[0]->num_elements; i++) { +- struct ldb_message_element *el = NULL; ++ struct ldb_message_element *app_el = &app_section->msgs[0]->elements[i]; ++ ++ /* These elements will be skipped when replacing attributes in ++ * a domain to avoid EEXIST errors ++ */ ++ if (strcasecmp(app_el->name, "cn") == 0) { ++ continue; ++ } + + if (replace_msg != NULL) { + el = ldb_msg_find_element(replace_msg, +@@ -2013,12 +2027,16 @@ static int confdb_merge_parent_domain(const char *name, + ret = ldb_msg_add(app_msg, + &app_section->msgs[0]->elements[i], + ldb_flag); +- if (ret != EOK) { ++ if (ret != LDB_SUCCESS) { + continue; + } + } + +- ret = ldb_modify(cdb->ldb, app_msg); ++ /* We use permissive modification here because adding cn or ++ * distinguishedName from the app_section to the application ++ * message would throw EEXIST ++ */ ++ ret = sss_ldb_modify_permissive(cdb->ldb, app_msg); + if (ret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(ret); + DEBUG(SSSDBG_OP_FAILURE, +-- +2.9.3 + diff --git a/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch b/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch new file mode 100644 index 0000000..f46b9e2 --- /dev/null +++ b/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch @@ -0,0 +1,48 @@ +From dc8a5bc411403b3d216947e317dfce9dbc5f79d3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 4 Apr 2017 14:35:47 +0200 +Subject: [PATCH 115/118] utils: add sss_domain_is_forest_root() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3361 + +Reviewed-by: Pavel Březina +(cherry picked from commit 712e5b2e4465812c00a8667c75813322373bc657) +--- + src/util/domain_info_utils.c | 5 +++++ + src/util/util.h | 1 + + 2 files changed, 6 insertions(+) + +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index 2af7852f03f89b61f5b9fd8a244e98fb27b7e6a2..541058a16d585155b3b51511740f7db45281e2fd 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -844,6 +844,11 @@ void sss_domain_set_state(struct sss_domain_info *dom, + "Domain %s is %s\n", dom->name, domain_state_str(dom)); + } + ++bool sss_domain_is_forest_root(struct sss_domain_info *dom) ++{ ++ return (dom->forest_root == dom); ++} ++ + bool is_email_from_domain(const char *email, struct sss_domain_info *dom) + { + const char *p; +diff --git a/src/util/util.h b/src/util/util.h +index 436550f5078cc173b8ed8cb58836d366f813146b..4ef13ced48addc19403402d7d880176da24ceec6 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -539,6 +539,7 @@ enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom); + void sss_domain_set_state(struct sss_domain_info *dom, + enum sss_domain_state state); + bool is_email_from_domain(const char *email, struct sss_domain_info *dom); ++bool sss_domain_is_forest_root(struct sss_domain_info *dom); + const char *sss_domain_type_str(struct sss_domain_info *dom); + + struct sss_domain_info* +-- +2.9.3 + diff --git a/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch b/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch new file mode 100644 index 0000000..28f65d5 --- /dev/null +++ b/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch @@ -0,0 +1,104 @@ +From 5ca331e80520035d7de2680cd2803fa508d15287 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 3 Apr 2017 21:27:32 +0200 +Subject: [PATCH 116/118] ad: handle forest root not listed in + ad_enabled_domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Although users and groups from the forest root should be ignored SSSD +will still try to get information about the forest topology from a DC +from the forest root. So even if the forest root domain is disabled we +should makes sure it is usable for those searches. + +Resolves https://pagure.io/SSSD/sssd/issue/3361 + +Reviewed-by: Pavel Březina +(cherry picked from commit feeabf273aa7af580552366ce58655e6a482a0cd) +--- + src/providers/ad/ad_subdomains.c | 39 ++++++++++++++++++++++++++++++++++++--- + 1 file changed, 36 insertions(+), 3 deletions(-) + +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index bc659b2cb0a02723437d24d0021ec3592381e84c..ef166446e837c3f7cd824c1abf4b5cc587aec9da 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -433,6 +433,14 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx, + if (c >= num_subdomains) { + /* ok this subdomain does not exist anymore, let's clean up */ + sss_domain_set_state(dom, DOM_DISABLED); ++ ++ /* Just disable the forest root but do not remove sdap data */ ++ if (sss_domain_is_forest_root(dom)) { ++ DEBUG(SSSDBG_TRACE_ALL, ++ "Skipping removal of forest root sdap data.\n"); ++ continue; ++ } ++ + ret = sysdb_subdomain_delete(dom->sysdb, dom->name); + if (ret != EOK) { + goto done; +@@ -633,6 +641,7 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx) + const char *path; + errno_t ret; + bool canonicalize = false; ++ struct sss_domain_info *dom; + + path = dp_opt_get_string(subdoms_ctx->ad_id_ctx->ad_options->basic, + AD_KRB5_CONFD_PATH); +@@ -675,6 +684,17 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx) + return ret; + } + ++ /* Make sure disabled domains are not re-enabled accidentially */ ++ if (subdoms_ctx->ad_enabled_domains != NULL) { ++ for (dom = subdoms_ctx->be_ctx->domain->subdomains; dom; ++ dom = get_next_domain(dom, false)) { ++ if (!is_domain_enabled(dom->name, ++ subdoms_ctx->ad_enabled_domains)) { ++ sss_domain_set_state(dom, DOM_DISABLED); ++ } ++ } ++ } ++ + return EOK; + } + +@@ -898,7 +918,7 @@ static errno_t ad_get_slave_domain_recv(struct tevent_req *req) + static struct sss_domain_info * + ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs) + { +- struct sss_domain_info *root; ++ struct sss_domain_info *dom; + const char *name; + errno_t ret; + +@@ -909,9 +929,22 @@ ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs) + } + + /* With a subsequent run, the root should already be known */ +- root = find_domain_by_name(be_ctx->domain, name, false); ++ for (dom = be_ctx->domain; dom != NULL; ++ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + +- return root; ++ if (strcasecmp(dom->name, name) == 0) { ++ /* The forest root is special, although it might be disabled for ++ * general lookups we still want to try to get the domains in the ++ * forest from a DC of the forest root */ ++ if (sss_domain_get_state(dom) == DOM_DISABLED ++ && !sss_domain_is_forest_root(dom)) { ++ return NULL; ++ } ++ return dom; ++ } ++ } ++ ++ return NULL; + } + + static struct ad_id_ctx * +-- +2.9.3 + diff --git a/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch b/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch new file mode 100644 index 0000000..52c114d --- /dev/null +++ b/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch @@ -0,0 +1,168 @@ +From ef6d1aaaa416bca3318e2961269620db7720a55b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 11 Apr 2017 19:56:37 +0200 +Subject: [PATCH 117/118] SDAP: Fix handling of search bases + +We were rewriting the sdap_domain's search bases for only the first +sdap_domain in the list, which does not work for subdomains. + +Also when search bases were already initialized in sdap_domain_subdom_add, +we should only rewrite them when they were explicitly set in sssd.conf. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3351 + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 4c49edbd8df651b1737c59459637962c117212c6) +--- + src/providers/ad/ad_common.c | 39 +++++++++++++++++++++---------- + src/providers/ad/ad_common.h | 3 ++- + src/providers/ipa/ipa_subdomains_server.c | 2 +- + src/providers/ldap/ldap_options.c | 2 -- + 4 files changed, 30 insertions(+), 16 deletions(-) + +diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c +index f893b748a2ddcff1eab6e8d919d2aa950b825446..1a9d8dc0bfdf18e76e3c97a7ac7e297c4d24fd44 100644 +--- a/src/providers/ad/ad_common.c ++++ b/src/providers/ad/ad_common.c +@@ -29,7 +29,8 @@ struct ad_server_data { + bool gc; + }; + +-errno_t ad_set_search_bases(struct sdap_options *id_opts); ++errno_t ad_set_search_bases(struct sdap_options *id_opts, ++ struct sdap_domain *sdap); + static errno_t ad_set_sdap_options(struct ad_options *ad_opts, + struct sdap_options *id_opts); + +@@ -1074,7 +1075,7 @@ ad_get_id_options(struct ad_options *ad_opts, + } + + /* Set up search bases if they were assigned explicitly */ +- ret = ad_set_search_bases(id_opts); ++ ret = ad_set_search_bases(id_opts, NULL); + if (ret != EOK) { + talloc_free(id_opts); + return ret; +@@ -1116,11 +1117,14 @@ ad_get_autofs_options(struct ad_options *ad_opts, + } + + errno_t +-ad_set_search_bases(struct sdap_options *id_opts) ++ad_set_search_bases(struct sdap_options *id_opts, ++ struct sdap_domain *sdom) + { + errno_t ret; +- char *default_search_base; ++ char *default_search_base = NULL; + size_t o; ++ struct sdap_domain *sdap_dom; ++ bool has_default; + const int search_base_options[] = { SDAP_USER_SEARCH_BASE, + SDAP_GROUP_SEARCH_BASE, + SDAP_NETGROUP_SEARCH_BASE, +@@ -1132,10 +1136,21 @@ ad_set_search_bases(struct sdap_options *id_opts) + * been specifically overridden. + */ + +- default_search_base = +- dp_opt_get_string(id_opts->basic, SDAP_SEARCH_BASE); ++ if (sdom != NULL) { ++ sdap_dom = sdom; ++ } else { ++ /* If no specific sdom was given, use the first in the list. */ ++ sdap_dom = id_opts->sdom; ++ } + +- if (default_search_base) { ++ has_default = sdap_dom->search_bases != NULL; ++ ++ if (has_default == false) { ++ default_search_base = ++ dp_opt_get_string(id_opts->basic, SDAP_SEARCH_BASE); ++ } ++ ++ if (default_search_base && has_default == false) { + /* set search bases if they are not */ + for (o = 0; search_base_options[o] != -1; o++) { + if (NULL == dp_opt_get_string(id_opts->basic, +@@ -1162,31 +1177,31 @@ ad_set_search_bases(struct sdap_options *id_opts) + /* Default search */ + ret = sdap_parse_search_base(id_opts, id_opts->basic, + SDAP_SEARCH_BASE, +- &id_opts->sdom->search_bases); ++ &sdap_dom->search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* User search */ + ret = sdap_parse_search_base(id_opts, id_opts->basic, + SDAP_USER_SEARCH_BASE, +- &id_opts->sdom->user_search_bases); ++ &sdap_dom->user_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* Group search base */ + ret = sdap_parse_search_base(id_opts, id_opts->basic, + SDAP_GROUP_SEARCH_BASE, +- &id_opts->sdom->group_search_bases); ++ &sdap_dom->group_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* Netgroup search */ + ret = sdap_parse_search_base(id_opts, id_opts->basic, + SDAP_NETGROUP_SEARCH_BASE, +- &id_opts->sdom->netgroup_search_bases); ++ &sdap_dom->netgroup_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* Service search */ + ret = sdap_parse_search_base(id_opts, id_opts->basic, + SDAP_SERVICE_SEARCH_BASE, +- &id_opts->sdom->service_search_bases); ++ &sdap_dom->service_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + ret = EOK; +diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h +index 2981550f6c390929501ec8942e861b16ea0a5cb0..ce33b37c75f45ae72adb268858cce34759b8b02f 100644 +--- a/src/providers/ad/ad_common.h ++++ b/src/providers/ad/ad_common.h +@@ -130,7 +130,8 @@ struct ad_options *ad_create_1way_trust_options(TALLOC_CTX *mem_ctx, + const char *keytab, + const char *sasl_authid); + +-errno_t ad_set_search_bases(struct sdap_options *id_opts); ++errno_t ad_set_search_bases(struct sdap_options *id_opts, ++ struct sdap_domain *sdap); + + errno_t + ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, +diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c +index e8ee30392d84f84e30bcdaa3d2110ba130b1ad73..b02ea67af964a03e5466067cdb2b3ba4498120eb 100644 +--- a/src/providers/ipa/ipa_subdomains_server.c ++++ b/src/providers/ipa/ipa_subdomains_server.c +@@ -332,7 +332,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, + return EFAULT; + } + +- ret = ad_set_search_bases(ad_options->id); ++ ret = ad_set_search_bases(ad_options->id, sdom); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD search bases\n"); + talloc_free(ad_options); +diff --git a/src/providers/ldap/ldap_options.c b/src/providers/ldap/ldap_options.c +index 15a2609f07506b6dd442b180651a7e25461976c0..eb4e177b456253ebdfa06ee52886a5dffe0d3351 100644 +--- a/src/providers/ldap/ldap_options.c ++++ b/src/providers/ldap/ldap_options.c +@@ -581,8 +581,6 @@ errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx, + char *unparsed_base; + const char *old_filter = NULL; + +- *_search_bases = NULL; +- + switch (class) { + case SDAP_SEARCH_BASE: + class_name = "DEFAULT"; +-- +2.9.3 + diff --git a/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch b/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch new file mode 100644 index 0000000..2bfa2c1 --- /dev/null +++ b/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch @@ -0,0 +1,102 @@ +From 4a3f3c675e360c888da7d23ab6ec4cca10876b08 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 27 Apr 2017 09:28:55 +0200 +Subject: [PATCH 118/118] overrides: add certificates to mapped attribute +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Certificates in overrides are explicitly used to map users to +certificates, so we add them to SYSDB_USER_MAPPED_CERT as well. + +Resolves https://pagure.io/SSSD/sssd/issue/3373 + +Reviewed-by: Pavel Březina +(cherry picked from commit 2e5fc89ef25434fab7febe2c52e97ef989b50d5b) +--- + src/db/sysdb_views.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c +index 20db9b06183d68b33bb19f498513d7f5cf84b1cf..3773dda77e16b35fa217be0aa7974da7e34c09f4 100644 +--- a/src/db/sysdb_views.c ++++ b/src/db/sysdb_views.c +@@ -777,6 +777,7 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain, + int ret; + TALLOC_CTX *tmp_ctx; + struct sysdb_attrs *attrs; ++ struct sysdb_attrs *mapped_attrs = NULL; + size_t c; + size_t d; + size_t num_values; +@@ -791,6 +792,7 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain, + SYSDB_USER_CERT, + NULL }; + bool override_attrs_found = false; ++ bool is_cert = false; + + if (override_attrs == NULL) { + /* nothing to do */ +@@ -846,6 +848,24 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain, + num_values = 1; + } + ++ is_cert = false; ++ if (strcmp(allowed_attrs[c], SYSDB_USER_CERT) == 0) { ++ /* Certificates in overrides are explicitly used to map ++ * users to certificates, so we add them to ++ * SYSDB_USER_MAPPED_CERT as well. */ ++ is_cert = true; ++ ++ if (mapped_attrs == NULL) { ++ mapped_attrs = sysdb_new_attrs(tmp_ctx); ++ if (mapped_attrs == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_new_attrs failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ + for (d = 0; d < num_values; d++) { + ret = sysdb_attrs_add_val(attrs, allowed_attrs[c], + &el->values[d]); +@@ -854,6 +874,18 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain, + "sysdb_attrs_add_val failed.\n"); + goto done; + } ++ ++ if (is_cert) { ++ ret = sysdb_attrs_add_val(mapped_attrs, ++ SYSDB_USER_MAPPED_CERT, ++ &el->values[d]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_attrs_add_val failed.\n"); ++ goto done; ++ } ++ } ++ + DEBUG(SSSDBG_TRACE_ALL, + "Override [%s] with [%.*s] for [%s].\n", + allowed_attrs[c], (int) el->values[d].length, +@@ -878,6 +910,15 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain, + DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n"); + goto done; + } ++ ++ if (mapped_attrs != NULL) { ++ ret = sysdb_set_entry_attr(domain->sysdb, obj_dn, mapped_attrs, ++ SYSDB_MOD_ADD); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_set_entry_attr failed, ignored.\n"); ++ } ++ } + } + + ret = EOK; +-- +2.9.3 + diff --git a/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch b/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch new file mode 100644 index 0000000..c9874b9 --- /dev/null +++ b/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch @@ -0,0 +1,243 @@ +From 54790675d0fd0627f7db8449ef97d59c0632006e Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 24 Apr 2017 10:13:44 +0200 +Subject: [PATCH 119/119] AD: Make ad_account_can_shortcut() reusable by SSSD + on an IPA server +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Resolves: + https://pagure.io/SSSD/sssd/issue/3318 + +The ad_account_can_shortcut() function is helpful to avoid unnecessary +searches when SSSD is configured with an Active Directory domain that +uses ID-mapping in the sense that if we find that an ID is outside our +range, we can just abort the search in this domain and carry on. + +This function was only used in the AD provider functions which are used +when SSSD is enrolled direcly with an AD server. This patch moves the +function to a codepath that is shared between directly enrolled SSSD and +SSSD running on an IPA server. + +Apart from moving the code, there are some minor changes to the function +signature, namely the domain is passed as as struct (previously the +domain name from the DP input was passed). + +Reviewed-by: Michal Židek +(cherry picked from commit dfe05f505dcfea16e7d66ca1a44206aa2570e861) +--- + src/providers/ad/ad_id.c | 162 ++++++++++++++++++++++++----------------------- + 1 file changed, 84 insertions(+), 78 deletions(-) + +diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c +index 8f26cb8744d2372c6180342c0d1bca025b16f52c..d1f6c444f5ddbcbbac6ff7f41fb6c8bf9ca976cb 100644 +--- a/src/providers/ad/ad_id.c ++++ b/src/providers/ad/ad_id.c +@@ -50,6 +50,77 @@ disable_gc(struct ad_options *ad_options) + } + } + ++static bool ad_account_can_shortcut(struct sdap_idmap_ctx *idmap_ctx, ++ struct sss_domain_info *domain, ++ int filter_type, ++ const char *filter_value) ++{ ++ struct sss_domain_info *dom_head = NULL; ++ struct sss_domain_info *sid_dom = NULL; ++ enum idmap_error_code err; ++ char *sid = NULL; ++ const char *csid = NULL; ++ uint32_t id; ++ bool shortcut = false; ++ errno_t ret; ++ ++ if (!sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, domain->name, ++ domain->domain_id)) { ++ goto done; ++ } ++ ++ switch (filter_type) { ++ case BE_FILTER_IDNUM: ++ /* convert value to ID */ ++ errno = 0; ++ id = strtouint32(filter_value, NULL, 10); ++ if (errno != 0) { ++ ret = errno; ++ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to convert filter value to " ++ "number [%d]: %s\n", ret, strerror(ret)); ++ goto done; ++ } ++ ++ /* convert the ID to its SID equivalent */ ++ err = sss_idmap_unix_to_sid(idmap_ctx->map, id, &sid); ++ if (err != IDMAP_SUCCESS) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: " ++ "[%s]\n", filter_value, idmap_error_string(err)); ++ goto done; ++ } ++ /* fall through */ ++ SSS_ATTRIBUTE_FALLTHROUGH; ++ case BE_FILTER_SECID: ++ csid = sid == NULL ? filter_value : sid; ++ ++ dom_head = get_domains_head(domain); ++ if (dom_head == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find domain head\n"); ++ goto done; ++ } ++ ++ sid_dom = find_domain_by_sid(dom_head, csid); ++ if (sid_dom == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "Invalid domain for SID:%s\n", csid); ++ goto done; ++ } ++ ++ if (strcasecmp(sid_dom->name, domain->name) != 0) { ++ shortcut = true; ++ } ++ break; ++ default: ++ break; ++ } ++ ++done: ++ if (sid != NULL) { ++ sss_idmap_free_sid(idmap_ctx->map, sid); ++ } ++ ++ return shortcut; ++} ++ + struct ad_handle_acct_info_state { + struct dp_id_data *ar; + struct sdap_id_ctx *ctx; +@@ -78,6 +149,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, + struct ad_handle_acct_info_state *state; + struct be_ctx *be_ctx = ctx->be; + errno_t ret; ++ bool shortcut; + + req = tevent_req_create(mem_ctx, &state, struct ad_handle_acct_info_state); + if (req == NULL) { +@@ -90,6 +162,18 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, + state->ad_options = ad_options; + state->cindex = 0; + ++ /* Try to shortcut if this is ID or SID search and it belongs to ++ * other domain range than is in ar->domain. */ ++ shortcut = ad_account_can_shortcut(ctx->opts->idmap_ctx, ++ sdom->dom, ++ ar->filter_type, ++ ar->filter_value); ++ if (shortcut) { ++ DEBUG(SSSDBG_TRACE_FUNC, "This ID is from different domain\n"); ++ ret = EOK; ++ goto immediate; ++ } ++ + if (sss_domain_get_state(sdom->dom) == DOM_INACTIVE) { + ret = ERR_SUBDOM_INACTIVE; + goto immediate; +@@ -297,72 +381,6 @@ get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx, + return clist; + } + +-static bool ad_account_can_shortcut(struct be_ctx *be_ctx, +- struct sdap_idmap_ctx *idmap_ctx, +- int filter_type, +- const char *filter_value, +- const char *filter_domain) +-{ +- struct sss_domain_info *domain = be_ctx->domain; +- struct sss_domain_info *req_dom = NULL; +- enum idmap_error_code err; +- char *sid = NULL; +- const char *csid = NULL; +- uint32_t id; +- bool shortcut = false; +- errno_t ret; +- +- if (!sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, domain->name, +- domain->domain_id)) { +- goto done; +- } +- +- switch (filter_type) { +- case BE_FILTER_IDNUM: +- /* convert value to ID */ +- errno = 0; +- id = strtouint32(filter_value, NULL, 10); +- if (errno != 0) { +- ret = errno; +- DEBUG(SSSDBG_MINOR_FAILURE, "Unable to convert filter value to " +- "number [%d]: %s\n", ret, strerror(ret)); +- goto done; +- } +- +- /* convert the ID to its SID equivalent */ +- err = sss_idmap_unix_to_sid(idmap_ctx->map, id, &sid); +- if (err != IDMAP_SUCCESS) { +- DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: " +- "[%s]\n", filter_value, idmap_error_string(err)); +- goto done; +- } +- /* fall through */ +- SSS_ATTRIBUTE_FALLTHROUGH; +- case BE_FILTER_SECID: +- csid = sid == NULL ? filter_value : sid; +- +- req_dom = find_domain_by_sid(domain, csid); +- if (req_dom == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "Invalid domain for SID:%s\n", csid); +- goto done; +- } +- +- if (strcasecmp(req_dom->name, filter_domain) != 0) { +- shortcut = true; +- } +- break; +- default: +- break; +- } +- +-done: +- if (sid != NULL) { +- sss_idmap_free_sid(idmap_ctx->map, sid); +- } +- +- return shortcut; +-} +- + struct ad_account_info_handler_state { + struct sss_domain_info *domain; + struct dp_reply_std reply; +@@ -384,7 +402,6 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct tevent_req *subreq; + struct tevent_req *req; + struct be_ctx *be_ctx; +- bool shortcut; + errno_t ret; + + sdap_id_ctx = id_ctx->sdap_id_ctx; +@@ -403,17 +420,6 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, + goto immediately; + } + +- /* Try to shortcut if this is ID or SID search and it belongs to +- * other domain range than is in ar->domain. */ +- shortcut = ad_account_can_shortcut(be_ctx, sdap_id_ctx->opts->idmap_ctx, +- data->filter_type, data->filter_value, +- data->domain); +- if (shortcut) { +- DEBUG(SSSDBG_TRACE_FUNC, "This ID is from different domain\n"); +- ret = EOK; +- goto immediately; +- } +- + domain = be_ctx->domain; + if (strcasecmp(data->domain, be_ctx->domain->name) != 0) { + /* Subdomain request, verify subdomain. */ +-- +2.9.3 + diff --git a/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch b/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch new file mode 100644 index 0000000..abe44ec --- /dev/null +++ b/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch @@ -0,0 +1,47 @@ +From 428909abd59f1eb8bb02b6627f37f61af3de2691 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 1 May 2017 14:49:50 +0200 +Subject: [PATCH 120/120] LDAP/AD: Do not fail in case + rfc2307bis_nested_groups_recv() returns ENOENT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 25699846 introduced a regression seen when an initgroup lookup is +done and there's no nested groups involved. + +In this scenario the whole lookup fails due to an ENOENT returned by +rfc2307bis_nested_groups_recv(), which leads to the user removal from +sysdb causing some authentication issues. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3331 + +Signed-off-by: Fabiano Fidêncio +Reviewed-by: Sumit Bose +--- + src/providers/ldap/sdap_async_initgroups_ad.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c +index f75b9211e2a06616dbf9b948e60b023a818c7e19..2831be9776293260aeec0e2ff85160f1938bdb32 100644 +--- a/src/providers/ldap/sdap_async_initgroups_ad.c ++++ b/src/providers/ldap/sdap_async_initgroups_ad.c +@@ -1746,7 +1746,13 @@ static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq) + + ret = rfc2307bis_nested_groups_recv(subreq); + talloc_zfree(subreq); +- if (ret != EOK) { ++ if (ret == ENOENT) { ++ /* In case of ENOENT we can just proceed without making ++ * sdap_get_initgr_user() fail because there's no nested ++ * groups for this user/group. */ ++ ret = EOK; ++ goto done; ++ } else if (ret != EOK) { + tevent_req_error(req, ret); + return; + } +-- +2.9.3 + diff --git a/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch b/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch new file mode 100644 index 0000000..55c7c61 --- /dev/null +++ b/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch @@ -0,0 +1,116 @@ +From 52514960f5b0609cd9f31f3c4455b61fbe4c04c5 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 26 Apr 2017 17:16:19 +0200 +Subject: [PATCH 121/121] PAM: check matching certificates from all domains + +Although the cache_req lookup found matching in multiple domains only +the results from the first domain were used. With this patch the results +from all domains are checked. + +Resolves https://pagure.io/SSSD/sssd/issue/3385 + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 92d8b072f8c521e1b4effe109b5caedabd36ed6f) +--- + src/responder/pam/pamsrv_cmd.c | 69 ++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 63 insertions(+), 6 deletions(-) + +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index f2b3c74b483e527932dda42279d14a9ac184b475..10a178f839ec011c09a6da4575efbb026f3f7700 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1352,15 +1352,71 @@ done: + pam_check_user_done(preq, ret); + } + ++static errno_t get_results_from_all_domains(TALLOC_CTX *mem_ctx, ++ struct cache_req_result **results, ++ struct ldb_result **ldb_results) ++{ ++ int ret; ++ size_t count = 0; ++ size_t c; ++ size_t d; ++ size_t r = 0; ++ struct ldb_result *res; ++ ++ for (d = 0; results != NULL && results[d] != NULL; d++) { ++ count += results[d]->count; ++ } ++ ++ res = talloc_zero(mem_ctx, struct ldb_result); ++ if (res == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); ++ return ENOMEM; ++ } ++ ++ if (count == 0) { ++ *ldb_results = res; ++ return EOK; ++ } ++ ++ res->msgs = talloc_zero_array(res, struct ldb_message *, count); ++ if (res->msgs == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); ++ return ENOMEM; ++ } ++ res->count = count; ++ ++ for (d = 0; results != NULL && results[d] != NULL; d++) { ++ for (c = 0; c < results[d]->count; c++) { ++ if (r >= count) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "More results found then counted before.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ res->msgs[r++] = talloc_steal(res->msgs, results[d]->msgs[c]); ++ } ++ } ++ ++ *ldb_results = res; ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(res); ++ } ++ ++ return ret; ++} ++ + static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + { + int ret; +- struct cache_req_result *result; ++ struct cache_req_result **results; + struct pam_auth_req *preq = tevent_req_callback_data(req, + struct pam_auth_req); + const char *cert_user; + +- ret = cache_req_user_by_cert_recv(preq, req, &result); ++ ret = cache_req_recv(preq, req, &results); + talloc_zfree(req); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n"); +@@ -1368,12 +1424,13 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + } + + if (ret == EOK) { +- if (preq->domain == NULL) { +- preq->domain = result->domain; ++ ret = get_results_from_all_domains(preq, results, ++ &preq->cert_user_objs); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "get_results_from_all_domains failed.\n"); ++ goto done; + } + +- preq->cert_user_objs = talloc_steal(preq, result->ldb_result); +- + if (preq->pd->logon_name == NULL) { + if (preq->pd->cmd != SSS_PAM_PREAUTH) { + DEBUG(SSSDBG_CRIT_FAILURE, +-- +2.9.3 + diff --git a/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch b/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch new file mode 100644 index 0000000..b7dfbd3 --- /dev/null +++ b/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch @@ -0,0 +1,80 @@ +From b818bb3f27ce672df0a6cadf2fd90716d2a576dc Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Wed, 26 Apr 2017 15:45:33 -0400 +Subject: [PATCH 122/127] DP: Reduce Data Provider log level noise +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Certain operations are not supported with certain providers +causing informational Data Provider log messages to be logged as +errors or failures. This patch lowers the log level to reduce overall +log noise and ensure only critical log messages are logged when +a low debug_level value is used. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3287 +https://pagure.io/SSSD/sssd/issue/3278 + +Reviewed-by: Fabiano Fidêncio +Reviewed-by: Pavel Březina +(cherry picked from commit e98d085b529e0ae5e07a717ce3b30f3943be0ee0) +--- + src/providers/data_provider/dp_methods.c | 2 +- + src/providers/data_provider/dp_targets.c | 2 +- + src/responder/common/responder_dp.c | 13 +++++++++++-- + 3 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/src/providers/data_provider/dp_methods.c b/src/providers/data_provider/dp_methods.c +index 498676d1bec2da300ca4b33f7110debcbf0aac00..9e49c5f5d65b869b3699fdc682a535e0111b6fd4 100644 +--- a/src/providers/data_provider/dp_methods.c ++++ b/src/providers/data_provider/dp_methods.c +@@ -109,7 +109,7 @@ errno_t dp_find_method(struct data_provider *provider, + } + + if (!dp_target_initialized(provider->targets, target)) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not initialized\n", ++ DEBUG(SSSDBG_CONF_SETTINGS, "Target [%s] is not initialized\n", + dp_target_to_string(target)); + return ERR_MISSING_DP_TARGET; + } +diff --git a/src/providers/data_provider/dp_targets.c b/src/providers/data_provider/dp_targets.c +index 26d20a8ef79b80d56df76d7a73ec8e63d001ecbc..e2a45bbac969ca7b9b13729f26b8cded8ab7eebc 100644 +--- a/src/providers/data_provider/dp_targets.c ++++ b/src/providers/data_provider/dp_targets.c +@@ -284,7 +284,7 @@ static errno_t dp_target_init(struct be_ctx *be_ctx, + if (!target->explicitly_configured && (ret == ELIBBAD || ret == ENOTSUP)) { + /* Target not found but it wasn't explicitly + * configured so we shall just continue. */ +- DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not supported by " ++ DEBUG(SSSDBG_CONF_SETTINGS, "Target [%s] is not supported by " + "module [%s].\n", target->name, target->module_name); + ret = EOK; + goto done; +diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c +index 080f70fd5945ffd234e0ef226d8139df071c4752..a75a611960801f5f5bdc95f00aea9ab921e8e293 100644 +--- a/src/responder/common/responder_dp.c ++++ b/src/responder/common/responder_dp.c +@@ -218,8 +218,17 @@ static int sss_dp_get_reply(DBusPendingCall *pending, + err = ETIME; + goto done; + } +- DEBUG(SSSDBG_FATAL_FAILURE,"The Data Provider returned an error [%s]\n", +- dbus_message_get_error_name(reply)); ++ ++ if (strcmp(dbus_message_get_error_name(reply), ++ SBUS_ERROR_DP_NOTSUP) == 0) { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "Data Provider does not support this operation.\n"); ++ } else { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "The Data Provider returned an error [%s]\n", ++ dbus_message_get_error_name(reply)); ++ } ++ + /* Falling through to default intentionally*/ + SSS_ATTRIBUTE_FALLTHROUGH; + default: +-- +2.9.3 + diff --git a/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch b/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch new file mode 100644 index 0000000..abc8b99 --- /dev/null +++ b/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch @@ -0,0 +1,249 @@ +From 43b07b3fe8794a6e19db5cd2e9036e3d4d6c43ad Mon Sep 17 00:00:00 2001 +From: Nikolai Kondrashov +Date: Wed, 22 Mar 2017 14:32:35 +0200 +Subject: [PATCH 123/127] NSS: Move output name formatting to utils +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Move NSS nss_get_name_from_msg and the core of sized_output_name to the +utils to make them available to provider and other responders. + +Reviewed-by: Pavel Březina +(cherry picked from commit a012a71f21bf1a4687e58085f19c18cc5b2bbadd) +--- + src/responder/common/responder_common.c | 27 ++++--------- + src/responder/nss/nss_protocol_grent.c | 2 +- + src/responder/nss/nss_protocol_pwent.c | 2 +- + src/responder/nss/nss_protocol_sid.c | 2 +- + src/responder/nss/nss_utils.c | 27 ------------- + src/util/usertools.c | 67 +++++++++++++++++++++++++++++++++ + src/util/util.h | 9 +++++ + 7 files changed, 87 insertions(+), 49 deletions(-) + +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 7496d293fddb3e947d59a4f2aaeb2c83234dfcc7..9d4889be652c6d6fb974b59001a9ac77b496e9ab 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -1685,7 +1685,7 @@ int sized_output_name(TALLOC_CTX *mem_ctx, + { + TALLOC_CTX *tmp_ctx = NULL; + errno_t ret; +- char *username; ++ char *name_str; + struct sized_string *name; + + tmp_ctx = talloc_new(NULL); +@@ -1693,30 +1693,19 @@ int sized_output_name(TALLOC_CTX *mem_ctx, + return ENOMEM; + } + +- username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve, +- rctx->override_space); +- if (username == NULL) { +- ret = EIO; +- goto done; +- } +- +- if (name_dom->fqnames) { +- username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username); +- if (username == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n"); +- ret = EIO; +- goto done; +- } +- } +- + name = talloc_zero(tmp_ctx, struct sized_string); + if (name == NULL) { + ret = ENOMEM; + goto done; + } + +- to_sized_string(name, username); +- name->str = talloc_steal(name, username); ++ ret = sss_output_fqname(mem_ctx, name_dom, orig_name, ++ rctx->override_space, &name_str); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ to_sized_string(name, name_str); + *_name = talloc_steal(mem_ctx, name); + ret = EOK; + done: +diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c +index fae1d47d7b217beafba75740e2e6d9cb8cdbc1d0..947463df93e188729959737efa4ac4f44a8459c4 100644 +--- a/src/responder/nss/nss_protocol_grent.c ++++ b/src/responder/nss/nss_protocol_grent.c +@@ -41,7 +41,7 @@ nss_get_grent(TALLOC_CTX *mem_ctx, + } + + /* Get fields. */ +- name = nss_get_name_from_msg(domain, msg); ++ name = sss_get_name_from_msg(domain, msg); + gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0); + + if (name == NULL || gid == 0) { +diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c +index edda9d3c87389898435a34fe7927868bc1cd9ac5..cb643f29e2d5f0a0c55c51afd9def73813061aa7 100644 +--- a/src/responder/nss/nss_protocol_pwent.c ++++ b/src/responder/nss/nss_protocol_pwent.c +@@ -225,7 +225,7 @@ nss_get_pwent(TALLOC_CTX *mem_ctx, + + /* Get fields. */ + upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL); +- name = nss_get_name_from_msg(domain, msg); ++ name = sss_get_name_from_msg(domain, msg); + gid = nss_get_gid(domain, msg); + uid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_UIDNUM, 0); + +diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c +index a6a4e27d039c67ef98f6d5900d5e3fcadb3ee717..d4b7ee22d7c68a9e6f7c668f7268cdc5f36768b3 100644 +--- a/src/responder/nss/nss_protocol_sid.c ++++ b/src/responder/nss/nss_protocol_sid.c +@@ -532,7 +532,7 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx, + return ret; + } + +- tmp_str = nss_get_name_from_msg(result->domain, result->msgs[c]); ++ tmp_str = sss_get_name_from_msg(result->domain, result->msgs[c]); + if (tmp_str == NULL) { + return EINVAL; + } +diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c +index 2cd9c33b42f7e018ea89d2df206637f35646489e..b4950e5a6eaec6a4511f7251dcf2e623c0177230 100644 +--- a/src/responder/nss/nss_utils.c ++++ b/src/responder/nss/nss_utils.c +@@ -27,33 +27,6 @@ + #include "responder/nss/nss_private.h" + + const char * +-nss_get_name_from_msg(struct sss_domain_info *domain, +- struct ldb_message *msg) +-{ +- const char *name; +- +- /* If domain has a view associated we return overridden name +- * if possible. */ +- if (DOM_HAS_VIEWS(domain)) { +- name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME, +- NULL); +- if (name != NULL) { +- return name; +- } +- } +- +- /* Otherwise we try to return name override from +- * Default Truest View for trusted users. */ +- name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL); +- if (name != NULL) { +- return name; +- } +- +- /* If no override is found we return the original name. */ +- return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); +-} +- +-const char * + nss_get_pwfield(struct nss_ctx *nctx, + struct sss_domain_info *dom) + { +diff --git a/src/util/usertools.c b/src/util/usertools.c +index 7b87c567a6c2dc7e9c267407434b2a7a9edeaa00..5dfe6d7765b8032c7447de75e10c6c2a1d4c49ec 100644 +--- a/src/util/usertools.c ++++ b/src/util/usertools.c +@@ -816,3 +816,70 @@ done: + talloc_free(tmp_ctx); + return outname; + } ++ ++const char * ++sss_get_name_from_msg(struct sss_domain_info *domain, ++ struct ldb_message *msg) ++{ ++ const char *name; ++ ++ /* If domain has a view associated we return overridden name ++ * if possible. */ ++ if (DOM_HAS_VIEWS(domain)) { ++ name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME, ++ NULL); ++ if (name != NULL) { ++ return name; ++ } ++ } ++ ++ /* Otherwise we try to return name override from ++ * Default Truest View for trusted users. */ ++ name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL); ++ if (name != NULL) { ++ return name; ++ } ++ ++ /* If no override is found we return the original name. */ ++ return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); ++} ++ ++int sss_output_fqname(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ const char *name, ++ char override_space, ++ char **_output_name) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ errno_t ret; ++ char *output_name; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ output_name = sss_output_name(tmp_ctx, name, domain->case_preserve, ++ override_space); ++ if (output_name == NULL) { ++ ret = EIO; ++ goto done; ++ } ++ ++ if (domain->fqnames) { ++ output_name = sss_tc_fqname(tmp_ctx, domain->names, ++ domain, output_name); ++ if (output_name == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n"); ++ ret = EIO; ++ goto done; ++ } ++ } ++ ++ *_output_name = talloc_steal(mem_ctx, output_name); ++ ret = EOK; ++done: ++ talloc_zfree(tmp_ctx); ++ return ret; ++} +diff --git a/src/util/util.h b/src/util/util.h +index 4ef13ced48addc19403402d7d880176da24ceec6..5ba4c36ca88e325c20a3b1ecc8080a11ca276dcf 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -304,6 +304,15 @@ char *sss_output_name(TALLOC_CTX *mem_ctx, + bool case_sensitive, + const char replace_space); + ++int sss_output_fqname(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ const char *name, ++ char override_space, ++ char **_output_name); ++ ++const char *sss_get_name_from_msg(struct sss_domain_info *domain, ++ struct ldb_message *msg); ++ + /* from backup-file.c */ + int backup_file(const char *src, int dbglvl); + +-- +2.9.3 + diff --git a/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch b/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch new file mode 100644 index 0000000..fd0780e --- /dev/null +++ b/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch @@ -0,0 +1,322 @@ +From da437bb72fc6ab072fc9b3e6d6809bac323de1e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 25 Apr 2017 14:14:05 +0200 +Subject: [PATCH 124/127] CACHE_REQ: Add a new cache_req_ncache_filter_fn() + plugin function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This function will be responsible for filtering out all the results that +we have that are also present in the negative cache. + +This is useful mainly for plugins which don't use name as an input token +but can still be affected by filter_{users,groups} options. + +For now this new function is not being used anywhere. + +Related: +https://pagure.io/SSSD/sssd/issue/3362 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit f24ee5cca4cd43e7edf26fec453fbd99392bbe4b) +--- + src/responder/common/cache_req/cache_req_plugin.h | 13 +++++++++++++ + .../common/cache_req/plugins/cache_req_enum_groups.c | 1 + + src/responder/common/cache_req/plugins/cache_req_enum_svc.c | 1 + + .../common/cache_req/plugins/cache_req_enum_users.c | 1 + + .../common/cache_req/plugins/cache_req_group_by_filter.c | 1 + + .../common/cache_req/plugins/cache_req_group_by_id.c | 1 + + .../common/cache_req/plugins/cache_req_group_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_host_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_initgroups_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_initgroups_by_upn.c | 1 + + .../common/cache_req/plugins/cache_req_netgroup_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_object_by_id.c | 1 + + .../common/cache_req/plugins/cache_req_object_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_object_by_sid.c | 1 + + .../common/cache_req/plugins/cache_req_svc_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_svc_by_port.c | 1 + + .../common/cache_req/plugins/cache_req_user_by_cert.c | 1 + + .../common/cache_req/plugins/cache_req_user_by_filter.c | 1 + + .../common/cache_req/plugins/cache_req_user_by_id.c | 1 + + .../common/cache_req/plugins/cache_req_user_by_name.c | 1 + + .../common/cache_req/plugins/cache_req_user_by_upn.c | 1 + + 21 files changed, 33 insertions(+) + +diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h +index e0b619528f6aa31a10a5b48c3c5acc96de90caa1..8117325506b2951c3966fa50506ed0d55273ee81 100644 +--- a/src/responder/common/cache_req/cache_req_plugin.h ++++ b/src/responder/common/cache_req/cache_req_plugin.h +@@ -93,6 +93,18 @@ typedef errno_t + struct cache_req_data *data); + + /** ++ * Filter the result through the negative cache. ++ * ++ * This is useful for plugins which don't use name as an input ++ * token but can be affected by filter_users and filter_groups ++ * options. ++ */ ++typedef errno_t ++(*cache_req_ncache_filter_fn)(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *name); ++ ++/** + * Add an object into global negative cache. + * + * @return EOK If everything went fine. +@@ -207,6 +219,7 @@ struct cache_req_plugin { + cache_req_global_ncache_add_fn global_ncache_add_fn; + cache_req_ncache_check_fn ncache_check_fn; + cache_req_ncache_add_fn ncache_add_fn; ++ cache_req_ncache_filter_fn ncache_filter_fn; + cache_req_lookup_fn lookup_fn; + cache_req_dp_send_fn dp_send_fn; + cache_req_dp_recv_fn dp_recv_fn; +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c +index 49ce3508e678862e4389657187b9659ce90fbd1c..11ce9e90ff28f77078b025a44593a44be8f1f5c5 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c +@@ -75,6 +75,7 @@ const struct cache_req_plugin cache_req_enum_groups = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_enum_groups_lookup, + .dp_send_fn = cache_req_enum_groups_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c +index 499b994738d62707b4e86d5a8383e3e2b82e8c57..72b2f1a7d2d2e02ce1a995098d1f26003444bddb 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c +@@ -76,6 +76,7 @@ const struct cache_req_plugin cache_req_enum_svc = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_enum_svc_lookup, + .dp_send_fn = cache_req_enum_svc_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c +index b635354be6e9d2e2e2af1a6f867ac68e6cf7f085..e0647a0102d9568abdcebfbf0fb99fc2624d5565 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c +@@ -75,6 +75,7 @@ const struct cache_req_plugin cache_req_enum_users = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_enum_users_lookup, + .dp_send_fn = cache_req_enum_users_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c +index 4377a476c36e5e03c8533bc62335b84fa1cee3ff..aa89953b88313605041cce599999fc5bbc741525 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c +@@ -131,6 +131,7 @@ const struct cache_req_plugin cache_req_group_by_filter = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_group_by_filter_lookup, + .dp_send_fn = cache_req_group_by_filter_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c +index ad5b7d890a42f29b586ab8e0943fef3dfab1162d..5613bf67c6acd1b2ace00cf75221462f45ef6743 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c +@@ -144,6 +144,7 @@ const struct cache_req_plugin cache_req_group_by_id = { + .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add, + .ncache_check_fn = cache_req_group_by_id_ncache_check, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_group_by_id_lookup, + .dp_send_fn = cache_req_group_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c +index de1e8f9442273acf386a2278b06f28ee63a7e3c6..7706051818590af77da75d3e4c7f671c89170f82 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c +@@ -194,6 +194,7 @@ const struct cache_req_plugin cache_req_group_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_group_by_name_ncache_check, + .ncache_add_fn = cache_req_group_by_name_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_group_by_name_lookup, + .dp_send_fn = cache_req_group_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c +index 1171cd63fac5cc1d36b31bf8a069f059705cae90..9cb32f6b18327873ba4b96fa177e8295be461db0 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c +@@ -92,6 +92,7 @@ const struct cache_req_plugin cache_req_host_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_host_by_name_lookup, + .dp_send_fn = cache_req_host_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c +index f100aefe5c92279cde7e3209c7f48f5e2b35f135..75ac44e1ad36238f01342eced9188d07daa50720 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c +@@ -209,6 +209,7 @@ const struct cache_req_plugin cache_req_initgroups_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_initgroups_by_name_ncache_check, + .ncache_add_fn = cache_req_initgroups_by_name_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_initgroups_by_name_lookup, + .dp_send_fn = cache_req_initgroups_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c +index 266ec7b8a28d496d9603bd9b6cdfef268ffa8559..b6fb43ee02d2f041fb3d992b375ae65a02db8b03 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c ++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c +@@ -120,6 +120,7 @@ const struct cache_req_plugin cache_req_initgroups_by_upn = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_initgroups_by_upn_ncache_check, + .ncache_add_fn = cache_req_initgroups_by_upn_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_initgroups_by_upn_lookup, + .dp_send_fn = cache_req_initgroups_by_upn_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +index ab3e553d3ecb8ae09094dcfc938ed0ac01925327..4d8bb18579a286042b00528190dadd52fdd7c75c 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +@@ -128,6 +128,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_netgroup_by_name_ncache_check, + .ncache_add_fn = cache_req_netgroup_by_name_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_netgroup_by_name_lookup, + .dp_send_fn = cache_req_netgroup_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c +index 9557bd15270b2eb1a0671f9ef91033efac29c3ac..ff3d0e67862be365c56ab24396b4982e8addded0 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c +@@ -111,6 +111,7 @@ const struct cache_req_plugin cache_req_object_by_id = { + .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add, + .ncache_check_fn = cache_req_object_by_id_ncache_check, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_object_by_id_lookup, + .dp_send_fn = cache_req_object_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c +index e236d1fa4aadcd87b192d34ebaf5f9ad8908b6c2..854d0b83c420ebebcb5e0e079c707081fa313632 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c +@@ -204,6 +204,7 @@ const struct cache_req_plugin cache_req_object_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_object_by_name_ncache_check, + .ncache_add_fn = cache_req_object_by_name_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_object_by_name_lookup, + .dp_send_fn = cache_req_object_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c +index dfec79da07d669165205a767cab22c2254686134..039a79df7bb1ab213ce4334835e9fc18e6d0faac 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c +@@ -120,6 +120,7 @@ const struct cache_req_plugin cache_req_object_by_sid = { + .global_ncache_add_fn = cache_req_object_by_sid_global_ncache_add, + .ncache_check_fn = cache_req_object_by_sid_ncache_check, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_object_by_sid_lookup, + .dp_send_fn = cache_req_object_by_sid_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c +index b2bfb26ffed1a60ed8389fa89b0e728c8c6cf76c..4c32d9977cc06e43eed3a90e7dcf107e91efefb5 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c +@@ -152,6 +152,7 @@ const struct cache_req_plugin cache_req_svc_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_svc_by_name_ncache_check, + .ncache_add_fn = cache_req_svc_by_name_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_svc_by_name_lookup, + .dp_send_fn = cache_req_svc_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c +index 0e48437f4b64d26112be88af1eebc20f012b70fd..1e998f642c766d15d3f6fe777aa5c789629508e2 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c ++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c +@@ -125,6 +125,7 @@ const struct cache_req_plugin cache_req_svc_by_port = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_svc_by_port_ncache_check, + .ncache_add_fn = cache_req_svc_by_port_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_svc_by_port_lookup, + .dp_send_fn = cache_req_svc_by_port_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c +index 286a34db276e0098060982c572e2a68ceceebf60..7a0c7d8ce1644f1c41b64c6903e4e20eb3c2c081 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c +@@ -94,6 +94,7 @@ const struct cache_req_plugin cache_req_user_by_cert = { + .global_ncache_add_fn = cache_req_user_by_cert_global_ncache_add, + .ncache_check_fn = cache_req_user_by_cert_ncache_check, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_cert_lookup, + .dp_send_fn = cache_req_user_by_cert_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c +index c476814373cd784bf8dbbea1da7b010afe5bb4e4..dd3f42e855389ecc73690e4d18c4977253b108a6 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c +@@ -131,6 +131,7 @@ const struct cache_req_plugin cache_req_user_by_filter = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_filter_lookup, + .dp_send_fn = cache_req_user_by_filter_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c +index 9ba73292e5dc518e86c6e00e7e493d6871f28e70..b14b3738aa7721723f524ebd46301a3a9a1c712f 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c +@@ -144,6 +144,7 @@ const struct cache_req_plugin cache_req_user_by_id = { + .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add, + .ncache_check_fn = cache_req_user_by_id_ncache_check, + .ncache_add_fn = NULL, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_id_lookup, + .dp_send_fn = cache_req_user_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c +index 15da7d0d20b1ac97511a226daecc8ef7e7d2e7e4..2e49de938d0af50089d0cf49860441c2b6ea679c 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c +@@ -199,6 +199,7 @@ const struct cache_req_plugin cache_req_user_by_name = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_user_by_name_ncache_check, + .ncache_add_fn = cache_req_user_by_name_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_name_lookup, + .dp_send_fn = cache_req_user_by_name_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c +index 40a097b1634d2b2d089b7feb377ea2389a58672c..b8bcd241ed79c510aca214ad3788215ae2997d20 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c +@@ -125,6 +125,7 @@ const struct cache_req_plugin cache_req_user_by_upn = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = cache_req_user_by_upn_ncache_check, + .ncache_add_fn = cache_req_user_by_upn_ncache_add, ++ .ncache_filter_fn = NULL, + .lookup_fn = cache_req_user_by_upn_lookup, + .dp_send_fn = cache_req_user_by_upn_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +-- +2.9.3 + diff --git a/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch b/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch new file mode 100644 index 0000000..2508964 --- /dev/null +++ b/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch @@ -0,0 +1,92 @@ +From a2bfa4d2074cacc5d30f17a3b3af260ec9eaaa59 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Thu, 27 Apr 2017 11:24:45 +0200 +Subject: [PATCH 125/127] CACHE_REQ_RESULT: Introduce + cache_req_create_ldb_result_from_msg_list() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Similarly to what cache_req_create_ldb_result_from_msg() does this new +function creates a new ldb_result from a list of ldb_message. + +It's going to be used in the follow-up patch where some messages from +ldb_result may be filtered and then a new ldb_result has to be created. + +Related: +https://pagure.io/SSSD/sssd/issue/3362 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit 180e0b282be6aeb047c4b24b46e0b56afba1fdc8) +--- + src/responder/common/cache_req/cache_req_private.h | 5 ++++ + src/responder/common/cache_req/cache_req_result.c | 35 ++++++++++++++++++++++ + 2 files changed, 40 insertions(+) + +diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h +index 851005c389f994b1bd2d04cda9b68df8b18492cc..c0ee5f969f2a171b8a6eb396b3d14b593d157b76 100644 +--- a/src/responder/common/cache_req/cache_req_private.h ++++ b/src/responder/common/cache_req/cache_req_private.h +@@ -137,6 +137,11 @@ cache_req_create_and_add_result(TALLOC_CTX *mem_ctx, + size_t *_num_results); + + struct ldb_result * ++cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx, ++ struct ldb_message **ldb_msgs, ++ size_t ldb_msg_count); ++ ++struct ldb_result * + cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx, + struct ldb_message *ldb_msg); + +diff --git a/src/responder/common/cache_req/cache_req_result.c b/src/responder/common/cache_req/cache_req_result.c +index e20ae5653acf22a2e0190ef6a88836c7fab9868e..366ba748082336c7c752b576cfd7b8fb8cd82fcf 100644 +--- a/src/responder/common/cache_req/cache_req_result.c ++++ b/src/responder/common/cache_req/cache_req_result.c +@@ -122,6 +122,41 @@ cache_req_create_and_add_result(TALLOC_CTX *mem_ctx, + } + + struct ldb_result * ++cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx, ++ struct ldb_message **ldb_msgs, ++ size_t ldb_msg_count) ++{ ++ struct ldb_result *ldb_result; ++ ++ if (ldb_msgs == NULL || ldb_msgs[0] == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n"); ++ return NULL; ++ } ++ ++ ldb_result = talloc_zero(NULL, struct ldb_result); ++ if (ldb_result == NULL) { ++ return NULL; ++ } ++ ++ ldb_result->extended = NULL; ++ ldb_result->controls = NULL; ++ ldb_result->refs = NULL; ++ ldb_result->count = ldb_msg_count; ++ ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *, ++ ldb_msg_count + 1); ++ if (ldb_result->msgs == NULL) { ++ talloc_free(ldb_result); ++ return NULL; ++ } ++ ++ for (size_t i = 0; i < ldb_msg_count; i++) { ++ ldb_result->msgs[i] = talloc_steal(ldb_result->msgs, ldb_msgs[i]); ++ } ++ ++ return ldb_result; ++} ++ ++struct ldb_result * + cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx, + struct ldb_message *ldb_msg) + { +-- +2.9.3 + diff --git a/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch b/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch new file mode 100644 index 0000000..d36136a --- /dev/null +++ b/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch @@ -0,0 +1,392 @@ +From 4c3780ced1b1507ebd8c3d0b91a3ef50b74e0b52 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 25 Apr 2017 16:33:58 +0200 +Subject: [PATCH 126/127] CACHE_REQ: Make use of cache_req_ncache_filter_fn() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch makes use of cache_req_ncache_filter_fn() in order to process +the result of a cache_req search and then filter out all the results +that are present in the negative cache. + +The "post cache_req search" result processing is done basically in two +different cases: +- plugins which don't use name as an input token (group_by_id, user_by_id + and object_by_id), but still can be affected by filter_{users,groups} + options; +- plugins responsible for groups and users enumeration (enum_groups and + enum_users); + +Resolves: +https://pagure.io/SSSD/sssd/issue/3362 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit 4ef0b19a5e8a327443d027e57487c8a1e4f654ce) +--- + src/responder/common/cache_req/cache_req_search.c | 124 +++++++++++++++++++-- + .../cache_req/plugins/cache_req_enum_groups.c | 10 +- + .../cache_req/plugins/cache_req_enum_users.c | 10 +- + .../cache_req/plugins/cache_req_group_by_id.c | 10 +- + .../cache_req/plugins/cache_req_object_by_id.c | 17 ++- + .../cache_req/plugins/cache_req_user_by_id.c | 10 +- + src/responder/nss/nss_protocol_grent.c | 12 -- + src/responder/nss/nss_protocol_pwent.c | 11 -- + 8 files changed, 165 insertions(+), 39 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c +index 8bc1530b341f587cb502fdf0ca3ed8d37cfb7d13..793dbc5042ae329b2cade5d1eb5a6d41102e264f 100644 +--- a/src/responder/common/cache_req/cache_req_search.c ++++ b/src/responder/common/cache_req/cache_req_search.c +@@ -84,6 +84,87 @@ static void cache_req_search_ncache_add(struct cache_req *cr) + return; + } + ++static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct ldb_result *result, ++ struct ldb_result **_result) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_result *filtered_result; ++ struct ldb_message **msgs; ++ size_t msg_count; ++ const char *name; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ if (cr->plugin->ncache_filter_fn == NULL) { ++ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, ++ "This request type does not support filtering " ++ "result by negative cache\n"); ++ ++ *_result = talloc_steal(mem_ctx, result); ++ ++ ret = EOK; ++ goto done; ++ } ++ ++ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, ++ "Filtering out results by negative cache\n"); ++ ++ msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, result->count); ++ msg_count = 0; ++ ++ for (size_t i = 0; i < result->count; i++) { ++ name = sss_get_name_from_msg(cr->domain, result->msgs[i]); ++ if (name == NULL) { ++ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, ++ "sss_get_name_from_msg() returned NULL, which should never " ++ "happen in this scenario!\n"); ++ ret = ERR_INTERNAL; ++ goto done; ++ } ++ ++ ret = cr->plugin->ncache_filter_fn(cr->ncache, cr->domain, name); ++ if (ret == EEXIST) { ++ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, ++ "[%s] filtered out! (negative cache)\n", ++ name); ++ continue; ++ } else if (ret != EOK && ret != ENOENT) { ++ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, ++ "Unable to check negative cache [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ msgs[msg_count] = talloc_steal(msgs, result->msgs[i]); ++ msg_count++; ++ } ++ ++ if (msg_count == 0) { ++ ret = ENOENT; ++ goto done; ++ } ++ ++ filtered_result = cache_req_create_ldb_result_from_msg_list(tmp_ctx, msgs, ++ msg_count); ++ if (filtered_result == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ *_result = talloc_steal(mem_ctx, filtered_result); ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ + static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + struct ldb_result **_result) +@@ -338,10 +419,18 @@ static void cache_req_search_oob_done(struct tevent_req *subreq) + + static void cache_req_search_done(struct tevent_req *subreq) + { ++ TALLOC_CTX *tmp_ctx; + struct cache_req_search_state *state; + struct tevent_req *req; ++ struct ldb_result *result = NULL; + errno_t ret; + ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_search_state); + +@@ -349,23 +438,36 @@ static void cache_req_search_done(struct tevent_req *subreq) + talloc_zfree(subreq); + + /* Get result from cache again. */ +- ret = cache_req_search_cache(state, state->cr, &state->result); +- if (ret == ENOENT) { +- /* Only store entry in negative cache if DP request succeeded +- * because only then we know that the entry does not exist. */ +- if (state->dp_success) { +- cache_req_search_ncache_add(state->cr); ++ ret = cache_req_search_cache(tmp_ctx, state->cr, &result); ++ if (ret != EOK) { ++ if (ret == ENOENT) { ++ /* Only store entry in negative cache if DP request succeeded ++ * because only then we know that the entry does not exist. */ ++ if (state->dp_success) { ++ cache_req_search_ncache_add(state->cr); ++ } + } +- tevent_req_error(req, ENOENT); +- return; +- } else if (ret != EOK) { +- tevent_req_error(req, ret); +- return; ++ goto done; ++ } ++ ++ /* ret == EOK */ ++ ret = cache_req_search_ncache_filter(state, state->cr, result, ++ &state->result); ++ if (ret != EOK) { ++ goto done; + } + + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Returning updated object [%s]\n", state->cr->debugobj); + ++done: ++ talloc_free(tmp_ctx); ++ ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ + tevent_req_done(req); + return; + } +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c +index 11ce9e90ff28f77078b025a44593a44be8f1f5c5..15350ca8279bc77c73bcc4abe51c97a8a37cb8c8 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c +@@ -55,6 +55,14 @@ cache_req_enum_groups_dp_send(TALLOC_CTX *mem_ctx, + SSS_DP_GROUP, NULL, 0, NULL); + } + ++static errno_t ++cache_req_enum_groups_ncache_filter(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *name) ++{ ++ return sss_ncache_check_group(ncache, domain, name); ++} ++ + const struct cache_req_plugin cache_req_enum_groups = { + .name = "Enumerate groups", + .attr_expiration = SYSDB_CACHE_EXPIRE, +@@ -75,7 +83,7 @@ const struct cache_req_plugin cache_req_enum_groups = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, +- .ncache_filter_fn = NULL, ++ .ncache_filter_fn = cache_req_enum_groups_ncache_filter, + .lookup_fn = cache_req_enum_groups_lookup, + .dp_send_fn = cache_req_enum_groups_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c +index e0647a0102d9568abdcebfbf0fb99fc2624d5565..a3ddcdd45548a2fa7c367f3fb3be103c115dedb4 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c ++++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c +@@ -55,6 +55,14 @@ cache_req_enum_users_dp_send(TALLOC_CTX *mem_ctx, + SSS_DP_USER, NULL, 0, NULL); + } + ++static errno_t ++cache_req_enum_users_ncache_filter(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *name) ++{ ++ return sss_ncache_check_user(ncache, domain, name); ++} ++ + const struct cache_req_plugin cache_req_enum_users = { + .name = "Enumerate users", + .attr_expiration = SYSDB_CACHE_EXPIRE, +@@ -75,7 +83,7 @@ const struct cache_req_plugin cache_req_enum_users = { + .global_ncache_add_fn = NULL, + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, +- .ncache_filter_fn = NULL, ++ .ncache_filter_fn = cache_req_enum_users_ncache_filter, + .lookup_fn = cache_req_enum_users_lookup, + .dp_send_fn = cache_req_enum_users_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c +index 5613bf67c6acd1b2ace00cf75221462f45ef6743..5ca64283a781318bc4e4d6920fff989c3f3919b4 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c +@@ -43,6 +43,14 @@ cache_req_group_by_id_ncache_check(struct sss_nc_ctx *ncache, + } + + static errno_t ++cache_req_group_by_id_ncache_filter(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *name) ++{ ++ return sss_ncache_check_group(ncache, domain, name); ++} ++ ++static errno_t + cache_req_group_by_id_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) + { +@@ -144,7 +152,7 @@ const struct cache_req_plugin cache_req_group_by_id = { + .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add, + .ncache_check_fn = cache_req_group_by_id_ncache_check, + .ncache_add_fn = NULL, +- .ncache_filter_fn = NULL, ++ .ncache_filter_fn = cache_req_group_by_id_ncache_filter, + .lookup_fn = cache_req_group_by_id_lookup, + .dp_send_fn = cache_req_group_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c +index ff3d0e67862be365c56ab24396b4982e8addded0..339bd4f5fef827acc1aa3c123d041e426d9e4782 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c +@@ -50,6 +50,21 @@ cache_req_object_by_id_ncache_check(struct sss_nc_ctx *ncache, + } + + static errno_t ++cache_req_object_by_id_ncache_filter(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *name) ++{ ++ errno_t ret; ++ ++ ret = sss_ncache_check_user(ncache, domain, name); ++ if (ret == EEXIST) { ++ ret = sss_ncache_check_group(ncache, domain, name); ++ } ++ ++ return ret; ++} ++ ++static errno_t + cache_req_object_by_id_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) + { +@@ -111,7 +126,7 @@ const struct cache_req_plugin cache_req_object_by_id = { + .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add, + .ncache_check_fn = cache_req_object_by_id_ncache_check, + .ncache_add_fn = NULL, +- .ncache_filter_fn = NULL, ++ .ncache_filter_fn = cache_req_object_by_id_ncache_filter, + .lookup_fn = cache_req_object_by_id_lookup, + .dp_send_fn = cache_req_object_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c +index b14b3738aa7721723f524ebd46301a3a9a1c712f..913f9be5bcc2dfd074b52cb3b15fb6948826e831 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c ++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c +@@ -43,6 +43,14 @@ cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache, + } + + static errno_t ++cache_req_user_by_id_ncache_filter(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *name) ++{ ++ return sss_ncache_check_user(ncache, domain, name); ++} ++ ++static errno_t + cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache, + struct cache_req_data *data) + { +@@ -144,7 +152,7 @@ const struct cache_req_plugin cache_req_user_by_id = { + .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add, + .ncache_check_fn = cache_req_user_by_id_ncache_check, + .ncache_add_fn = NULL, +- .ncache_filter_fn = NULL, ++ .ncache_filter_fn = cache_req_user_by_id_ncache_filter, + .lookup_fn = cache_req_user_by_id_lookup, + .dp_send_fn = cache_req_user_by_id_dp_send, + .dp_recv_fn = cache_req_common_dp_recv +diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c +index 947463df93e188729959737efa4ac4f44a8459c4..ee228c722a153a1ba7aa8a1b30a1e551108424bb 100644 +--- a/src/responder/nss/nss_protocol_grent.c ++++ b/src/responder/nss/nss_protocol_grent.c +@@ -241,18 +241,6 @@ nss_protocol_fill_grent(struct nss_ctx *nss_ctx, + continue; + } + +- /* Check negative cache during enumeration. */ +- if (cmd_ctx->enumeration) { +- ret = sss_ncache_check_group(nss_ctx->rctx->ncache, +- result->domain, name->str); +- if (ret == EEXIST) { +- DEBUG(SSSDBG_TRACE_FUNC, +- "User [%s] filtered out! (negative cache)\n", +- name->str); +- continue; +- } +- } +- + /* Adjust packet size: gid, num_members + string fields. */ + + ret = sss_packet_grow(packet, 2 * sizeof(uint32_t) +diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c +index cb643f29e2d5f0a0c55c51afd9def73813061aa7..b355d4fc90397f51e82545e56940be850f144d49 100644 +--- a/src/responder/nss/nss_protocol_pwent.c ++++ b/src/responder/nss/nss_protocol_pwent.c +@@ -309,17 +309,6 @@ nss_protocol_fill_pwent(struct nss_ctx *nss_ctx, + continue; + } + +- /* Check negative cache during enumeration. */ +- if (cmd_ctx->enumeration) { +- ret = sss_ncache_check_user(nss_ctx->rctx->ncache, +- result->domain, name->str); +- if (ret == EEXIST) { +- DEBUG(SSSDBG_TRACE_FUNC, +- "User [%s] filtered out! (negative cache)\n", name->str); +- continue; +- } +- } +- + /* Adjust packet size: uid, gid + string fields. */ + + ret = sss_packet_grow(packet, 2 * sizeof(uint32_t) +-- +2.9.3 + diff --git a/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch b/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch new file mode 100644 index 0000000..772f302 --- /dev/null +++ b/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch @@ -0,0 +1,81 @@ +From 9b9d3e2817fdcf16f2949641d4130b39856a4bf6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Fri, 28 Apr 2017 20:49:56 +0200 +Subject: [PATCH 127/127] SERVER_MODE: Update sdap lists for each ad_ctx + +We use separate AD context for each subdomain in the server mode. +Every such context has it's own sdap_domain list witch represents +sdap options such as filter and search bases for every domain. + +However AD context can only fully initialize sdap_domain structure +for the same domain for which the whole context was created, which +resulted in the other sdap_domain structures to be have automaticily +detected settings. This can cause problems if user is member of +groups from multiple domains. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3381 + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 21f3d6124ea28218d02e1e345d38e2b948e4ec23) +--- + src/providers/ipa/ipa_subdomains_server.c | 36 +++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c +index b02ea67af964a03e5466067cdb2b3ba4498120eb..443d83824f329b9d8c3d8e820113e1029f832240 100644 +--- a/src/providers/ipa/ipa_subdomains_server.c ++++ b/src/providers/ipa/ipa_subdomains_server.c +@@ -870,6 +870,7 @@ static errno_t ipa_server_create_trusts_step(struct tevent_req *req) + { + struct tevent_req *subreq = NULL; + struct ipa_ad_server_ctx *trust_iter; ++ struct ipa_ad_server_ctx *trust_i; + struct ipa_server_create_trusts_state *state = NULL; + + state = tevent_req_data(req, struct ipa_server_create_trusts_state); +@@ -900,6 +901,41 @@ static errno_t ipa_server_create_trusts_step(struct tevent_req *req) + } + } + ++ /* Refresh all sdap_dom lists in all ipa_ad_server_ctx contexts */ ++ DLIST_FOR_EACH(trust_iter, state->id_ctx->server_mode->trusts) { ++ struct sdap_domain *sdom_a; ++ ++ sdom_a = sdap_domain_get(trust_iter->ad_id_ctx->sdap_id_ctx->opts, ++ trust_iter->dom); ++ if (sdom_a == NULL) { ++ continue; ++ } ++ ++ DLIST_FOR_EACH(trust_i, state->id_ctx->server_mode->trusts) { ++ struct sdap_domain *sdom_b; ++ ++ if (strcmp(trust_iter->dom->name, trust_i->dom->name) == 0) { ++ continue; ++ } ++ ++ sdom_b = sdap_domain_get(trust_i->ad_id_ctx->sdap_id_ctx->opts, ++ sdom_a->dom); ++ if (sdom_b == NULL) { ++ continue; ++ } ++ ++ /* Replace basedn and search bases from sdom_b with values ++ * from sdom_a */ ++ sdom_b->search_bases = sdom_a->search_bases; ++ sdom_b->user_search_bases = sdom_a->user_search_bases; ++ sdom_b->group_search_bases = sdom_a->group_search_bases; ++ sdom_b->netgroup_search_bases = sdom_a->netgroup_search_bases; ++ sdom_b->sudo_search_bases = sdom_a->sudo_search_bases; ++ sdom_b->service_search_bases = sdom_a->service_search_bases; ++ sdom_b->autofs_search_bases = sdom_a->autofs_search_bases; ++ } ++ } ++ + return EOK; + } + +-- +2.9.3 + diff --git a/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch b/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch new file mode 100644 index 0000000..26092f6 --- /dev/null +++ b/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch @@ -0,0 +1,348 @@ +From 71731d26dc4f2c36989779f327b0e9a399486e14 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 9 May 2017 16:57:43 +0200 +Subject: [PATCH] sss_nss_getlistbycert: return results from multiple domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently only the results from one domain were returned although all +domains were searched and the results were available. Unit tests are +updated to cover this case as well. + +Resolves https://pagure.io/SSSD/sssd/issue/3393 + +Reviewed-by: Pavel Březina +--- + src/responder/nss/nss_cmd.c | 87 +++++++++++++++++++++++++++++++++++- + src/responder/nss/nss_protocol.h | 6 +++ + src/responder/nss/nss_protocol_sid.c | 78 ++++++++++++++++++++++++++++++++ + src/tests/cmocka/test_nss_srv.c | 33 +++++++++----- + 4 files changed, 192 insertions(+), 12 deletions(-) + +diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c +index 1931bf62a686c7f30852dac547866609cf54a81b..a4727c18786a86c28b5415ba82295967a47a8ec0 100644 +--- a/src/responder/nss/nss_cmd.c ++++ b/src/responder/nss/nss_cmd.c +@@ -51,6 +51,7 @@ nss_cmd_ctx_create(TALLOC_CTX *mem_ctx, + } + + static void nss_getby_done(struct tevent_req *subreq); ++static void nss_getlistby_done(struct tevent_req *subreq); + + static errno_t nss_getby_name(struct cli_ctx *cli_ctx, + enum cache_req_type type, +@@ -212,6 +213,89 @@ done: + return EOK; + } + ++static errno_t nss_getlistby_cert(struct cli_ctx *cli_ctx, ++ enum cache_req_type type) ++{ ++ struct nss_cmd_ctx *cmd_ctx; ++ struct tevent_req *subreq; ++ const char *cert; ++ errno_t ret; ++ ++ cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, NULL); ++ if (cmd_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cmd_ctx->sid_id_type = SSS_ID_TYPE_UID; ++ ++ ret = nss_protocol_parse_cert(cli_ctx, &cert); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n"); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Input cert: %s\n", get_last_x_chars(cert, 10)); ++ ++ subreq = cache_req_user_by_cert_send(cmd_ctx, cli_ctx->ev, cli_ctx->rctx, ++ cli_ctx->rctx->ncache, 0, ++ CACHE_REQ_ANY_DOM, NULL, ++ cert); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ tevent_req_set_callback(subreq, nss_getlistby_done, cmd_ctx); ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(cmd_ctx); ++ return nss_protocol_done(cli_ctx, ret); ++ } ++ ++ return EOK; ++} ++ ++static void nss_getlistby_done(struct tevent_req *subreq) ++{ ++ struct cache_req_result **results; ++ struct nss_cmd_ctx *cmd_ctx; ++ errno_t ret; ++ struct cli_protocol *pctx; ++ ++ cmd_ctx = tevent_req_callback_data(subreq, struct nss_cmd_ctx); ++ ++ ret = cache_req_recv(cmd_ctx, subreq, &results); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n"); ++ goto done; ++ } ++ ++ pctx = talloc_get_type(cmd_ctx->cli_ctx->protocol_ctx, struct cli_protocol); ++ ++ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), ++ &pctx->creq->out); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = nss_protocol_fill_name_list_all_domains(cmd_ctx->nss_ctx, cmd_ctx, ++ pctx->creq->out, results); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ sss_packet_set_error(pctx->creq->out, EOK); ++ ++done: ++ nss_protocol_done(cmd_ctx->cli_ctx, ret); ++ talloc_free(cmd_ctx); ++} ++ + static errno_t nss_getby_cert(struct cli_ctx *cli_ctx, + enum cache_req_type type, + nss_protocol_fill_packet_fn fill_fn) +@@ -934,8 +1018,7 @@ static errno_t nss_cmd_getnamebycert(struct cli_ctx *cli_ctx) + + static errno_t nss_cmd_getlistbycert(struct cli_ctx *cli_ctx) + { +- return nss_getby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT, +- nss_protocol_fill_name_list); ++ return nss_getlistby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT); + } + + struct sss_cmd_table *get_nss_cmds(void) +diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h +index e4c0e52c0e642e885ef2c8423ea564beff7242cf..417b0891615dcb8771d49f7b2f4d276342ca3150 100644 +--- a/src/responder/nss/nss_protocol.h ++++ b/src/responder/nss/nss_protocol.h +@@ -181,6 +181,12 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx, + struct cache_req_result *result); + + errno_t ++nss_protocol_fill_name_list_all_domains(struct nss_ctx *nss_ctx, ++ struct nss_cmd_ctx *cmd_ctx, ++ struct sss_packet *packet, ++ struct cache_req_result **results); ++ ++errno_t + nss_protocol_fill_id(struct nss_ctx *nss_ctx, + struct nss_cmd_ctx *cmd_ctx, + struct sss_packet *packet, +diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c +index d4b7ee22d7c68a9e6f7c668f7268cdc5f36768b3..61357c2bf92e2f15d978b64a15ad5bd5aa354445 100644 +--- a/src/responder/nss/nss_protocol_sid.c ++++ b/src/responder/nss/nss_protocol_sid.c +@@ -561,3 +561,81 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx, + + return EOK; + } ++ ++errno_t ++nss_protocol_fill_name_list_all_domains(struct nss_ctx *nss_ctx, ++ struct nss_cmd_ctx *cmd_ctx, ++ struct sss_packet *packet, ++ struct cache_req_result **results) ++{ ++ enum sss_id_type *id_types; ++ size_t rp = 0; ++ size_t body_len; ++ uint8_t *body; ++ errno_t ret; ++ struct sized_string *sz_names; ++ size_t len; ++ size_t c; ++ const char *tmp_str; ++ size_t d; ++ size_t total = 0; ++ size_t iter = 0; ++ ++ if (results == NULL) { ++ return EINVAL; ++ } ++ ++ for (d = 0; results[d] != NULL; d++) { ++ total += results[d]->count; ++ } ++ ++ sz_names = talloc_array(cmd_ctx, struct sized_string, total); ++ if (sz_names == NULL) { ++ return ENOMEM; ++ } ++ ++ id_types = talloc_array(cmd_ctx, enum sss_id_type, total); ++ if (id_types == NULL) { ++ return ENOMEM; ++ } ++ ++ len = 0; ++ for (d = 0; results[d] != NULL; d++) { ++ for (c = 0; c < results[d]->count; c++) { ++ ret = nss_get_id_type(cmd_ctx, results[d], &(id_types[iter])); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ tmp_str = sss_get_name_from_msg(results[d]->domain, ++ results[d]->msgs[c]); ++ if (tmp_str == NULL) { ++ return EINVAL; ++ } ++ to_sized_string(&(sz_names[iter]), tmp_str); ++ ++ len += sz_names[iter].len; ++ iter++; ++ } ++ } ++ ++ len += (2 + total) * sizeof(uint32_t); ++ ++ ret = sss_packet_grow(packet, len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); ++ return ret; ++ } ++ ++ sss_packet_get_body(packet, &body, &body_len); ++ ++ SAFEALIGN_SET_UINT32(&body[rp], total, &rp); /* Num results. */ ++ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */ ++ for (c = 0; c < total; c++) { ++ SAFEALIGN_SET_UINT32(&body[rp], id_types[c], &rp); ++ SAFEALIGN_SET_STRING(&body[rp], sz_names[c].str, sz_names[c].len, ++ &rp); ++ } ++ ++ return EOK; ++} +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 8c72f44f1869558893627e1f2f91b5f3b96c6317..03b5bcc302322551a32f5b8cfe4b7698947abbe7 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -3808,7 +3808,8 @@ static int test_nss_getnamebycert_check(uint32_t status, uint8_t *body, size_t b + return EOK; + } + +-static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t blen) ++static int test_nss_getlistbycert_check_exp(uint32_t status, uint8_t *body, ++ size_t blen, size_t exp) + { + size_t rp = 0; + uint32_t id_type; +@@ -3817,13 +3818,13 @@ static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t b + const char *name; + int found = 0; + const char *fq_name1 = "testcertuser@"TEST_DOM_NAME ; +- const char *fq_name2 = "testcertuser2@"TEST_DOM_NAME; ++ const char *fq_name2 = "testcertuser2@"TEST_SUBDOM_NAME; + + assert_int_equal(status, EOK); + + /* num_results and reserved */ + SAFEALIGN_COPY_UINT32(&num, body + rp, &rp); +- assert_in_range(num, 1, 2); ++ assert_int_equal(num, exp); + SAFEALIGN_COPY_UINT32(&reserved, body + rp, &rp); + assert_int_equal(reserved, 0); + +@@ -3858,6 +3859,17 @@ static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t b + return EOK; + } + ++static int test_nss_getlistbycert_check_one(uint32_t status, uint8_t *body, ++ size_t blen) ++{ ++ return test_nss_getlistbycert_check_exp(status, body, blen, 1); ++} ++ ++static int test_nss_getlistbycert_check_two(uint32_t status, uint8_t *body, ++ size_t blen) ++{ ++ return test_nss_getlistbycert_check_exp(status, body, blen, 2); ++} + + static void test_nss_getnamebycert(void **state) + { +@@ -3949,7 +3961,7 @@ static void test_nss_getlistbycert(void **state) + der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size); + assert_non_null(der); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + +@@ -3967,7 +3979,7 @@ static void test_nss_getlistbycert(void **state) + /* Should go straight to back end, without contacting DP. */ + /* If there is only a single user mapped the result will look like the */ + /* result of getnamebycert. */ +- set_cmd_cb(test_nss_getlistbycert_check); ++ set_cmd_cb(test_nss_getlistbycert_check_one); + ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT, + nss_test_ctx->nss_cmds); + assert_int_equal(ret, EOK); +@@ -3990,7 +4002,7 @@ static void test_nss_getlistbycert_multi(void **state) + attrs = sysdb_new_attrs(nss_test_ctx); + assert_non_null(attrs); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + assert_int_equal(ret, EOK); + + /* Prime the cache with two valid user */ +@@ -4004,11 +4016,11 @@ static void test_nss_getlistbycert_multi(void **state) + attrs = sysdb_new_attrs(nss_test_ctx); + assert_non_null(attrs); + +- ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size); ++ ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + +- ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom, ++ ret = store_user(nss_test_ctx, nss_test_ctx->subdom, + &testbycert2, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); +@@ -4019,7 +4031,7 @@ static void test_nss_getlistbycert_multi(void **state) + + /* Query for that user, call a callback when command finishes */ + /* Should go straight to back end, without contacting DP */ +- set_cmd_cb(test_nss_getlistbycert_check); ++ set_cmd_cb(test_nss_getlistbycert_check_two); + ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT, + nss_test_ctx->nss_cmds); + assert_int_equal(ret, EOK); +@@ -4290,7 +4302,8 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_nss_getlistbycert, + nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getlistbycert_multi, +- nss_test_setup, nss_test_teardown), ++ nss_subdom_test_setup, ++ nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getsidbyname, + nss_test_setup, nss_test_teardown), + cmocka_unit_test_setup_teardown(test_nss_getsidbyupn, +-- +2.9.3 + diff --git a/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch b/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch new file mode 100644 index 0000000..d11d711 --- /dev/null +++ b/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch @@ -0,0 +1,68 @@ +From 6a1da829eaa1eee3e854f0cadc0b6effff776ab4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 15 May 2017 11:54:00 +0200 +Subject: [PATCH 1/2] CACHE_REQ: Avoid using of uninitialized value +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 4ef0b19a introduced the following warning, as "req" may be used +without being initialized: +src/responder/common/cache_req/cache_req_search.c: + In function 'cache_req_search_done': +src/responder/common/cache_req/cache_req_search.c:467:9: + error: 'req' may be used uninitialized in this function + [-Werror=maybe-uninitialized] + tevent_req_error(req, ret); + ^ +src/responder/common/cache_req/cache_req_search.c:424:24: + note: 'req' was declared here + struct tevent_req *req; + ^ +cc1: all warnings being treated as errors + +In order to fix the issue above, let's just allocate tmp_ctx after "req" +is already set. + +Related: +https://pagure.io/SSSD/sssd/issue/3362 + +Signed-off-by: Fabiano Fidêncio +Co-Author: Lukáš Slebodník +Reviewed-by: Sumit Bose +--- + src/responder/common/cache_req/cache_req_search.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c +index 793dbc5042ae329b2cade5d1eb5a6d41102e264f..70448a7639bc9f98d380b8edce9d130adfa0ceb2 100644 +--- a/src/responder/common/cache_req/cache_req_search.c ++++ b/src/responder/common/cache_req/cache_req_search.c +@@ -425,18 +425,18 @@ static void cache_req_search_done(struct tevent_req *subreq) + struct ldb_result *result = NULL; + errno_t ret; + +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- ret = ENOMEM; +- goto done; +- } +- + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_search_state); + + state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr); + talloc_zfree(subreq); + ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ + /* Get result from cache again. */ + ret = cache_req_search_cache(tmp_ctx, state->cr, &result); + if (ret != EOK) { +-- +2.9.3 + diff --git a/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch b/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch new file mode 100644 index 0000000..1063c09 --- /dev/null +++ b/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch @@ -0,0 +1,163 @@ +From 1a89fc33d1b9b1070c7ab83fb0314e538ac46736 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 3 May 2017 13:24:40 +0200 +Subject: [PATCH 2/2] CACHE_REQ: Ensure the domains are updated for "filter" + related calls +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As contacting the infopipe responder on a "filter" related call may lead +to the situation where the cr_domains' list is not populated yet (as the +domains and subdomains lists from the data provider are not processed +yet), let's explicitly call sss_dp_get_domains() for those cases and +avoid returning a wrong result to the caller. + +This situation may happen only because the schedule_get_domains_task(), +that's called when the infopipe responder is initialized, may take some +time to run/finish. + +While I'm not exactly sure whether it's the best solution to avoid the +"race", it seems to be sane enough to avoid the issues. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3387 + +Signed-off-by: Fabiano Fidêncio +Reviewed-by: Sumit Bose +Reviewed-by: Pavel Březina +--- + src/responder/common/cache_req/cache_req.c | 86 ++++++++++++++++++++++++++++-- + 1 file changed, 81 insertions(+), 5 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index 797325a30e6c1ed5f1d4b4c147c65391d5204b52..7d77eb7dd72a7ccf3d687eee8f746ab84176b487 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -698,6 +698,13 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + struct cache_req *cr, + const char *domain); + ++static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct cache_req *cr, ++ const char *domain); ++ ++static void cache_req_domains_updated(struct tevent_req *subreq); ++ + static void cache_req_input_parsed(struct tevent_req *subreq); + + static errno_t cache_req_select_domains(struct tevent_req *req, +@@ -753,13 +760,13 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx, + goto done; + } + ++ state->domain_name = domain; + ret = cache_req_process_input(state, req, cr, domain); + if (ret != EOK) { + goto done; + } + +- state->domain_name = domain; +- ret = cache_req_select_domains(req, domain); ++ ret = cache_req_select_domains(req, state->domain_name); + + done: + if (ret == EOK) { +@@ -780,14 +787,25 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + { + struct tevent_req *subreq; + const char *default_domain; ++ errno_t ret; + + if (cr->data->name.input == NULL) { +- /* Input was not name, there is no need to process it further. */ +- return EOK; ++ /* Call cache_req_update_domains() in order to get a up to date list ++ * of domains and subdomains, if needed. Otherwise just return EOK as ++ * the input was not a name, thus there's no need to process it ++ * further. */ ++ return cache_req_update_domains(mem_ctx, req, cr, domain); + } + + if (cr->plugin->parse_name == false || domain != NULL) { +- /* We do not want to parse the name. */ ++ /* Call cache_req_update_domains() in order to get a up to date list ++ * of domains and subdomains, if needed. Otherwise, just use the input ++ * name as it is. */ ++ ret = cache_req_update_domains(mem_ctx, req, cr, domain); ++ if (ret != EOK) { ++ return ret; ++ } ++ + return cache_req_set_name(cr, cr->data->name.input); + } + +@@ -812,6 +830,64 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + return EAGAIN; + } + ++static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct cache_req *cr, ++ const char *domain) ++{ ++ struct tevent_req *subreq; ++ ++ if (cr->rctx->get_domains_last_call.tv_sec != 0) { ++ return EOK; ++ } ++ ++ subreq = sss_dp_get_domains_send(mem_ctx, cr->rctx, false, domain); ++ if (subreq == NULL) { ++ return ENOMEM; ++ } ++ ++ tevent_req_set_callback(subreq, cache_req_domains_updated, req); ++ return EAGAIN; ++} ++ ++static void cache_req_domains_updated(struct tevent_req *subreq) ++{ ++ struct tevent_req *req; ++ struct cache_req_state *state; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct cache_req_state); ++ ++ ret = sss_dp_get_domains_recv(subreq); ++ talloc_free(subreq); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ if (state->cr->data->name.input == NULL) { ++ /* Input was not name, there is no need to process it further. */ ++ goto immediately; ++ } ++ ++ if (state->cr->plugin->parse_name == false || state->domain_name != NULL) { ++ /* We do not want to parse the name. */ ++ ret = cache_req_set_name(state->cr, state->cr->data->name.input); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ ++immediately: ++ ret = cache_req_select_domains(req, state->domain_name); ++ ++done: ++ if (ret != EOK && ret != EAGAIN) { ++ tevent_req_error(req, ret); ++ return; ++ } ++} ++ + static void cache_req_input_parsed(struct tevent_req *subreq) + { + struct tevent_req *req; +-- +2.9.3 + diff --git a/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch b/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch new file mode 100644 index 0000000..50c99ed --- /dev/null +++ b/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch @@ -0,0 +1,102 @@ +From f994343e9ffc8f8d2917678ae61bcdf68c316a20 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 9 May 2017 11:21:02 +0200 +Subject: [PATCH 131/131] AD SUBDOMAINS: Fix search bases for child domains + +When using direct AD integration, child domains did not respect +the sssd.conf configuration of search bases. + +There were few issues all of which are fixed in this small +patch. + +First problem was that the sdap domain list was not properly +inherited from the parent in the child domains and the children +always created their own sdap domains lists that were disconnected +from the parent context and never used. + +Second issue was that the child domain did not call the function +to reinit the search bases after the sdap_domain was added to the +list of sdap domains. This caused that child domains always used +automatically detected search bases and never used the configured +ones even though they were properly read into the ID options +context attached to the subdomain. + +Also there has been an issue that the sdap search bases +were rewritten by the new child domain initialization +(this only happened with more than one child domain) +because the sdap domain list was 'updated' every time +a new child domain was initialized, which caused that +only the main domain and the last child domain had proper +search bases, the others only the auto-discovered ones +(because they were overwritten with the 'update'). + +Resolves: +https://pagure.io/SSSD/sssd/issue/3397 + +Reviewed-by: Sumit Bose +--- + src/providers/ad/ad_subdomains.c | 17 +++++++++++++++++ + src/providers/ldap/sdap_domain.c | 5 +++++ + 2 files changed, 22 insertions(+) + +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index ef166446e837c3f7cd824c1abf4b5cc587aec9da..c9b79dd9d6840802cddc067eef9d5110cf8d0778 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -221,6 +221,9 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + ad_id_ctx->sdap_id_ctx->opts = ad_options->id; + ad_options->id_ctx = ad_id_ctx; + ++ /* We need to pass the sdap list from parent */ ++ ad_id_ctx->sdap_id_ctx->opts->sdom = id_ctx->sdap_id_ctx->opts->sdom; ++ + /* use AD plugin */ + srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res, + default_host_dbs, +@@ -257,6 +260,13 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + ad_id_ctx->sdap_id_ctx->opts->idmap_ctx = + id_ctx->sdap_id_ctx->opts->idmap_ctx; + ++ ret = ad_set_search_bases(ad_options->id, sdom); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to set LDAP search bases for " ++ "domain '%s'. Will try to use automatically detected search " ++ "bases.", subdom->name); ++ } ++ + *_subdom_id_ctx = ad_id_ctx; + return EOK; + } +@@ -621,6 +631,13 @@ ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx, + return ret; + } + ++ ret = ad_set_search_bases(ctx->ad_id_ctx->ad_options->id, ctx->sdom); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "failed to set ldap search bases for " ++ "domain '%s'. will try to use automatically detected search " ++ "bases.", ctx->sdom->dom->name); ++ } ++ + DLIST_FOR_EACH(sditer, ctx->sdom) { + if (IS_SUBDOMAIN(sditer->dom) && sditer->pvt == NULL) { + ret = ad_subdom_ad_ctx_new(ctx->be_ctx, ctx->ad_id_ctx, +diff --git a/src/providers/ldap/sdap_domain.c b/src/providers/ldap/sdap_domain.c +index 5cba9df0fd5fb320a57adc39093283aed865f57f..d384b2e4a0ec3a7c8d0b05e0ce735feb2189085f 100644 +--- a/src/providers/ldap/sdap_domain.c ++++ b/src/providers/ldap/sdap_domain.c +@@ -154,6 +154,11 @@ sdap_domain_subdom_add(struct sdap_id_ctx *sdap_id_ctx, + parent->name, ret, strerror(ret)); + return ret; + } ++ } else if (sditer->search_bases != NULL) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "subdomain %s has already initialized search bases\n", ++ dom->name); ++ continue; + } else { + sdom = sditer; + } +-- +2.9.3 + diff --git a/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch b/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch new file mode 100644 index 0000000..7eecef9 --- /dev/null +++ b/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch @@ -0,0 +1,39 @@ +From cf1bb5464609f5873685406f9e09e43de8738e42 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 22 May 2017 09:55:12 +0200 +Subject: [PATCH 132/135] KRB5: Advise the user to inspect the krb5_child.log + if the child doesn't return a valid response +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the child returns a runtime error, it is often not clear from the +domain debug logs what to do next. This patch adds a DEBUG message that +tells the admin to look into the krb5_child.log + +Resolves: +https://pagure.io/SSSD/sssd/issue/2955 + +Reviewed-by: Pavel Březina +(cherry picked from commit 7410f735b64937e0c2401c09b5cffc9c78b11849) +--- + src/providers/krb5/krb5_auth.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index 2faf18d17a735476c20f9cc27b15be4a39cadc5c..894bd41bde031ac33187bfa3b14202e9429a9198 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -890,6 +890,9 @@ static void krb5_auth_done(struct tevent_req *subreq) + state->be_ctx->domain->pwd_expiration_warning, + &res); + if (ret) { ++ DEBUG(SSSDBG_IMPORTANT_INFO, ++ "The krb5_child process returned an error. Please inspect the " ++ "krb5_child.log file or the journal for more information\n"); + DEBUG(SSSDBG_OP_FAILURE, "Could not parse child response [%d]: %s\n", + ret, strerror(ret)); + goto done; +-- +2.9.3 + diff --git a/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch b/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch new file mode 100644 index 0000000..52aa5dc --- /dev/null +++ b/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch @@ -0,0 +1,40 @@ +From 04ef28b7cc49a71209551646b3a82518506f40a6 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 22 May 2017 14:58:01 +0200 +Subject: [PATCH 133/135] cache_req: use the right negative cache for + initgroups by upn +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 870b58a6cc6b5cf92a6503c1578e5c21617c8d40) +--- + src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c +index b6fb43ee02d2f041fb3d992b375ae65a02db8b03..dfb21ac1a0090a3ef9029b38f5b1e8bdda3440c6 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c ++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c +@@ -66,7 +66,7 @@ cache_req_initgroups_by_upn_ncache_check(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) + { +- return sss_ncache_check_user(ncache, domain, data->name.lookup); ++ return sss_ncache_check_upn(ncache, domain, data->name.lookup); + } + + static errno_t +@@ -74,7 +74,7 @@ cache_req_initgroups_by_upn_ncache_add(struct sss_nc_ctx *ncache, + struct sss_domain_info *domain, + struct cache_req_data *data) + { +- return sss_ncache_set_user(ncache, false, domain, data->name.lookup); ++ return sss_ncache_set_upn(ncache, false, domain, data->name.lookup); + } + + static errno_t +-- +2.9.3 + diff --git a/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch b/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch new file mode 100644 index 0000000..5fa1c16 --- /dev/null +++ b/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch @@ -0,0 +1,31 @@ +From 9e7bb71e02af7cf8fe8b593ddc762a09183ff32c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 22 May 2017 15:04:17 +0200 +Subject: [PATCH 134/135] test: make sure p11_child is build for pam-srv-tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit ec9ac22d699a17d590b1d4ba9ba3750eb719f340) +--- + Makefile.am | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/Makefile.am b/Makefile.am +index 370d6442ec58a14946ad288a23c696f25ca98f47..a6279133b56dcd5bcbd1306ae8f2ce18d90c2c12 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -2356,6 +2356,9 @@ nss_srv_tests_LDADD = \ + EXTRA_pam_srv_tests_DEPENDENCIES = \ + $(ldblib_LTLIBRARIES) \ + $(NULL) ++if HAVE_NSS ++EXTRA_pam_srv_tests_DEPENDENCIES += p11_child ++endif + pam_srv_tests_SOURCES = \ + $(TEST_MOCK_RESP_OBJ) \ + src/tests/cmocka/test_pam_srv.c \ +-- +2.9.3 + diff --git a/SOURCES/0135-pam-properly-support-UPN-logon-names.patch b/SOURCES/0135-pam-properly-support-UPN-logon-names.patch new file mode 100644 index 0000000..54d6529 --- /dev/null +++ b/SOURCES/0135-pam-properly-support-UPN-logon-names.patch @@ -0,0 +1,163 @@ +From b6ba8bc43a0b5a448e4cd30bc406bd08c389e365 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 12 May 2017 10:40:21 +0200 +Subject: [PATCH 135/135] pam: properly support UPN logon names +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Many logon applications like /bin/login or sshd canonicalize the user +name before they call pam_start() and hence the UPN is not seen by +SSSD's pam responder. But some like e.g. gdm don't and authentication +might fail if a UPN is used. + +The reason is that currently the already parsed short name of the user +was used in the cache_req and hence the cache_req was not able to fall +back to the UPN lookup code. This patch uses the name originally +provided by the user as input to allow the fallback to the UPN lookup. + +Resolves https://pagure.io/SSSD/sssd/issue/3240 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 29d063505c07127f7747405b1a61d8f782673645) +--- + src/responder/pam/pamsrv_cmd.c | 4 +-- + src/tests/cmocka/test_pam_srv.c | 79 ++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 80 insertions(+), 3 deletions(-) + +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 10a178f839ec011c09a6da4575efbb026f3f7700..36dba37964b71153435b4df5d5328de4361926e6 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1560,7 +1560,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + + data = cache_req_data_name(preq, + CACHE_REQ_INITGROUPS, +- preq->pd->user); ++ preq->pd->logon_name); + if (data == NULL) { + return ENOMEM; + } +@@ -1589,7 +1589,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + preq->cctx->rctx->ncache, + 0, + preq->req_dom_type, +- preq->pd->domain, ++ NULL, + data); + if (!dpreq) { + DEBUG(SSSDBG_CRIT_FAILURE, +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index d249b8f1ea48f1c17b461c3add9e8c63774e5f88..4d351a3707d2a49604595b728fff7705560c871a 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -518,6 +518,8 @@ static void mock_input_pam_ex(TALLOC_CTX *mem_ctx, + int ret; + size_t needed_size; + uint8_t *authtok; ++ char *s_name; ++ char *dom; + + if (name != NULL) { + pi.pam_user = name; +@@ -574,7 +576,13 @@ static void mock_input_pam_ex(TALLOC_CTX *mem_ctx, + will_return(__wrap_sss_packet_get_body, buf); + will_return(__wrap_sss_packet_get_body, buf_size); + +- mock_parse_inp(name, NULL, EOK); ++ if (strrchr(name, '@') == NULL) { ++ mock_parse_inp(name, NULL, EOK); ++ } else { ++ ret = sss_parse_internal_fqname(mem_ctx, name, &s_name, &dom); ++ mock_parse_inp(s_name, dom, EOK); ++ } ++ + if (contact_dp) { + mock_account_recv_simple(); + } +@@ -1582,6 +1590,71 @@ void test_pam_preauth_no_logon_name(void **state) + assert_int_equal(ret, EOK); + } + ++void test_pam_auth_no_upn_logon_name(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password(pam_test_ctx->tctx->dom, ++ pam_test_ctx->pam_user_fqdn, ++ "12345"); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam_ex(pam_test_ctx, "upn@"TEST_DOM_NAME, "12345", NULL, NULL, ++ true); ++ mock_account_recv_simple(); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN; ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_auth_upn_logon_name(void **state) ++{ ++ int ret; ++ struct sysdb_attrs *attrs; ++ ++ ret = sysdb_cache_password(pam_test_ctx->tctx->dom, ++ pam_test_ctx->pam_user_fqdn, ++ "12345"); ++ assert_int_equal(ret, EOK); ++ attrs = sysdb_new_attrs(pam_test_ctx); ++ assert_non_null(attrs); ++ ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upn@"TEST_DOM_NAME); ++ assert_int_equal(ret, EOK); ++ ++ ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, ++ pam_test_ctx->pam_user_fqdn, ++ attrs, ++ LDB_FLAG_MOD_ADD); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam_ex(pam_test_ctx, "upn@"TEST_DOM_NAME, "12345", NULL, NULL, ++ true); ++ mock_account_recv_simple(); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_successful_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++ + static void set_cert_auth_param(struct pam_ctx *pctx, const char *dbpath) + { + pam_test_ctx->pctx->cert_auth = true; +@@ -2312,6 +2385,10 @@ int main(int argc, const char *argv[]) + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_no_logon_name, + pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_auth_no_upn_logon_name, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_auth_upn_logon_name, ++ pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_success, + pam_cached_test_setup, + pam_test_teardown), +-- +2.9.3 + diff --git a/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch b/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch new file mode 100644 index 0000000..3cf4420 --- /dev/null +++ b/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch @@ -0,0 +1,334 @@ +From b31f75f44a9e1dc0521ec73176f89e05db4973ba Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 11 May 2017 16:24:24 +0200 +Subject: [PATCH 136/138] KCM: Fix the per-client serialization queue +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Resolves: + https://pagure.io/SSSD/sssd/issue/3372 + +Fixes a race condition between one client request adding an operation to +the hash table value, which was previously a linked list of operations, +while another concurrent operation would remove the last remaining +linked list element through its callback. + +Instead, the hash table value is now a separate 'queue head' structure +which is only changed in a tevent request to make sure is is not +processes concurrently with adding to the queue (which is also a tevent +request). + +Reviewed-by: Pavel Březina +(cherry picked from commit fb51bb68e62de7bb8542f5d224994eb7143040a6) +--- + src/responder/kcm/kcmsrv_op_queue.c | 182 ++++++++++++++++++++++++------------ + 1 file changed, 122 insertions(+), 60 deletions(-) + +diff --git a/src/responder/kcm/kcmsrv_op_queue.c b/src/responder/kcm/kcmsrv_op_queue.c +index f6c425dd5b64877c8b7401e488dd6565157fc9b5..55c8b65d94f70979fe56fcc4d8747547a9cc9d33 100644 +--- a/src/responder/kcm/kcmsrv_op_queue.c ++++ b/src/responder/kcm/kcmsrv_op_queue.c +@@ -27,17 +27,23 @@ + + struct kcm_ops_queue_entry { + struct tevent_req *req; +- uid_t uid; + +- hash_table_t *wait_queue_hash; ++ struct kcm_ops_queue *queue; + +- struct kcm_ops_queue_entry *head; + struct kcm_ops_queue_entry *next; + struct kcm_ops_queue_entry *prev; + }; + ++struct kcm_ops_queue { ++ uid_t uid; ++ struct tevent_context *ev; ++ struct kcm_ops_queue_ctx *qctx; ++ ++ struct kcm_ops_queue_entry *head; ++}; ++ + struct kcm_ops_queue_ctx { +- /* UID: dlist of kcm_ops_queue_entry */ ++ /* UID:kcm_ops_queue */ + hash_table_t *wait_queue_hash; + }; + +@@ -45,8 +51,9 @@ struct kcm_ops_queue_ctx { + * Per-UID wait queue + * + * They key in the hash table is the UID of the peer. The value of each +- * hash table entry is a linked list of kcm_ops_queue_entry structures +- * which primarily hold the tevent request being queued. ++ * hash table entry is kcm_ops_queue structure which in turn contains a ++ * linked list of kcm_ops_queue_entry structures * which primarily hold the ++ * tevent request being queued. + */ + struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx) + { +@@ -71,11 +78,45 @@ struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx) + return queue_ctx; + } + +-static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry) ++void queue_removal_cb(struct tevent_context *ctx, ++ struct tevent_immediate *imm, ++ void *private_data) + { ++ struct kcm_ops_queue *kq = talloc_get_type(private_data, ++ struct kcm_ops_queue); + int ret; ++ hash_key_t key; ++ ++ talloc_free(imm); ++ ++ if (kq->head != NULL) { ++ DEBUG(SSSDBG_TRACE_LIBS, "The queue is no longer empty\n"); ++ return; ++ } ++ ++ key.type = HASH_KEY_ULONG; ++ key.ul = kq->uid; ++ ++ /* If this was the last entry, remove the key (the UID) from the ++ * hash table to signal the queue is empty ++ */ ++ ret = hash_delete(kq->qctx->wait_queue_hash, &key); ++ if (ret != HASH_SUCCESS) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to remove wait queue for user %"SPRIuid"\n", ++ kq->uid); ++ return; ++ } ++ ++ DEBUG(SSSDBG_FUNC_DATA, ++ "Removed queue for %"SPRIuid" \n", kq->uid); ++ talloc_free(kq); ++} ++ ++static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry) ++{ + struct kcm_ops_queue_entry *next_entry; +- hash_key_t key; ++ struct tevent_immediate *imm; + + if (entry == NULL) { + return 1; +@@ -85,22 +126,19 @@ static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry) + next_entry = entry->next; + + /* Remove the current entry from the queue */ +- DLIST_REMOVE(entry->head, entry); ++ DLIST_REMOVE(entry->queue->head, entry); + + if (next_entry == NULL) { +- key.type = HASH_KEY_ULONG; +- key.ul = entry->uid; +- +- /* If this was the last entry, remove the key (the UID) from the +- * hash table to signal the queue is empty ++ /* If there was no other entry, schedule removal of the queue. Do it ++ * in another tevent tick to avoid issues with callbacks invoking ++ * the descructor while another request is touching the queue + */ +- ret = hash_delete(entry->wait_queue_hash, &key); +- if (ret != HASH_SUCCESS) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to remove wait queue for user %"SPRIuid"\n", +- entry->uid); ++ imm = tevent_create_immediate(entry->queue); ++ if (imm == NULL) { + return 1; + } ++ ++ tevent_schedule_immediate(imm, entry->queue->ev, queue_removal_cb, entry->queue); + return 0; + } + +@@ -109,41 +147,33 @@ static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry) + return 0; + } + +-static errno_t kcm_op_queue_add(hash_table_t *wait_queue_hash, +- struct kcm_ops_queue_entry *entry, +- uid_t uid) ++static struct kcm_ops_queue *kcm_op_queue_get(struct kcm_ops_queue_ctx *qctx, ++ struct tevent_context *ev, ++ uid_t uid) + { + errno_t ret; + hash_key_t key; + hash_value_t value; +- struct kcm_ops_queue_entry *head = NULL; ++ struct kcm_ops_queue *kq; + + key.type = HASH_KEY_ULONG; + key.ul = uid; + +- ret = hash_lookup(wait_queue_hash, &key, &value); ++ ret = hash_lookup(qctx->wait_queue_hash, &key, &value); + switch (ret) { + case HASH_SUCCESS: +- /* The key with this UID already exists. Its value is request queue +- * for the UID, so let's just add the current request to the end +- * of the queue and wait for the previous requests to finish +- */ + if (value.type != HASH_VALUE_PTR) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n"); +- return EINVAL; ++ return NULL; + } + +- head = talloc_get_type(value.ptr, struct kcm_ops_queue_entry); +- if (head == NULL) { ++ kq = talloc_get_type(value.ptr, struct kcm_ops_queue); ++ if (kq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid queue pointer\n"); +- return EINVAL; ++ return NULL; + } + +- entry->head = head; +- DLIST_ADD_END(head, entry, struct kcm_ops_queue_entry *); +- +- DEBUG(SSSDBG_TRACE_LIBS, "Waiting in queue\n"); +- ret = EAGAIN; ++ DEBUG(SSSDBG_TRACE_LIBS, "Found existing queue for this ID\n"); + break; + + case HASH_ERROR_KEY_NOT_FOUND: +@@ -151,36 +181,41 @@ static errno_t kcm_op_queue_add(hash_table_t *wait_queue_hash, + * another one comes in and return EOK to run the current request + * immediatelly + */ +- entry->head = entry; ++ DEBUG(SSSDBG_TRACE_LIBS, "No existing queue for this ID\n"); ++ ++ kq = talloc_zero(qctx->wait_queue_hash, struct kcm_ops_queue); ++ if (kq == NULL) { ++ return NULL; ++ } ++ kq->uid = uid; ++ kq->qctx = qctx; ++ kq->ev = ev; + + value.type = HASH_VALUE_PTR; +- value.ptr = entry; ++ value.ptr = kq; + +- ret = hash_enter(wait_queue_hash, &key, &value); ++ ret = hash_enter(qctx->wait_queue_hash, &key, &value); + if (ret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n"); +- return EIO; ++ return NULL; + } +- +- DEBUG(SSSDBG_TRACE_LIBS, +- "Added a first request to the queue, running immediately\n"); +- ret = EOK; + break; + + default: + DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n"); +- return EIO; ++ return NULL; + } + +- talloc_steal(wait_queue_hash, entry); +- talloc_set_destructor(entry, kcm_op_queue_entry_destructor); +- return ret; ++ return kq; + } + + struct kcm_op_queue_state { + struct kcm_ops_queue_entry *entry; + }; + ++static errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq, ++ struct tevent_req *req); ++ + /* + * Enqueue a request. + * +@@ -198,6 +233,7 @@ struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx, + { + errno_t ret; + struct tevent_req *req; ++ struct kcm_ops_queue *kq; + struct kcm_op_queue_state *state; + uid_t uid; + +@@ -208,22 +244,21 @@ struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx, + return NULL; + } + +- state->entry = talloc_zero(state, struct kcm_ops_queue_entry); +- if (state->entry == NULL) { +- ret = ENOMEM; +- goto immediate; +- } +- state->entry->req = req; +- state->entry->uid = uid; +- state->entry->wait_queue_hash = qctx->wait_queue_hash; +- + DEBUG(SSSDBG_FUNC_DATA, + "Adding request by %"SPRIuid" to the wait queue\n", uid); + +- ret = kcm_op_queue_add(qctx->wait_queue_hash, state->entry, uid); ++ kq = kcm_op_queue_get(qctx, ev, uid); ++ if (kq == NULL) { ++ ret = EIO; ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot get queue [%d]: %s\n", ret, sss_strerror(ret)); ++ goto immediate; ++ } ++ ++ ret = kcm_op_queue_add_req(kq, req); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_LIBS, +- "Wait queue was empty, running immediately\n"); ++ "Queue was empty, running the request immediately\n"); + goto immediate; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_OP_FAILURE, +@@ -244,6 +279,33 @@ immediate: + return req; + } + ++static errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq, ++ struct tevent_req *req) ++{ ++ errno_t ret; ++ struct kcm_op_queue_state *state = tevent_req_data(req, ++ struct kcm_op_queue_state); ++ ++ state->entry = talloc_zero(kq->qctx->wait_queue_hash, struct kcm_ops_queue_entry); ++ if (state->entry == NULL) { ++ return ENOMEM; ++ } ++ state->entry->req = req; ++ state->entry->queue = kq; ++ talloc_set_destructor(state->entry, kcm_op_queue_entry_destructor); ++ ++ if (kq->head == NULL) { ++ /* First entry, will run callback at once */ ++ ret = EOK; ++ } else { ++ /* Will wait for the previous callbacks to finish */ ++ ret = EAGAIN; ++ } ++ ++ DLIST_ADD_END(kq->head, state->entry, struct kcm_ops_queue_entry *); ++ return ret; ++} ++ + /* + * The queue recv function is called when this request is 'activated'. The queue + * entry should be allocated on the same memory context as the enqueued request +-- +2.9.4 + diff --git a/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch b/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch new file mode 100644 index 0000000..25a3528 --- /dev/null +++ b/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch @@ -0,0 +1,79 @@ +From 7930ee12093eae1e1ab9422c4f4f9f8c5661fcb9 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 23 May 2017 13:55:01 +0200 +Subject: [PATCH 137/138] TESTS: Add a test for parallel execution of klist +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Integration test for: + https://pagure.io/SSSD/sssd/issue/3372 + +With https://pagure.io/SSSD/sssd/issue/3372 still broken, the unit test +wold fail because one of the concurrent klist commands would trigger a +race condition in the KCM queue code, crashing the KCM responder. + +Reviewed-by: Pavel Březina +(cherry picked from commit 274489b092bba5fc81cb0f803843d56b267c5aaf) +--- + src/tests/intg/krb5utils.py | 6 +++++- + src/tests/intg/test_kcm.py | 22 ++++++++++++++++++++++ + 2 files changed, 27 insertions(+), 1 deletion(-) + +diff --git a/src/tests/intg/krb5utils.py b/src/tests/intg/krb5utils.py +index 775cffd0bbfa011f2d8ffc1169dccfef96d78fab..0349ff3829533088fb2263f84b19574127d6e809 100644 +--- a/src/tests/intg/krb5utils.py ++++ b/src/tests/intg/krb5utils.py +@@ -36,7 +36,7 @@ class Krb5Utils(object): + def __init__(self, krb5_conf_path): + self.krb5_conf_path = krb5_conf_path + +- def _run_in_env(self, args, stdin=None, extra_env=None): ++ def spawn_in_env(self, args, stdin=None, extra_env=None): + my_env = os.environ + my_env['KRB5_CONFIG'] = self.krb5_conf_path + +@@ -50,6 +50,10 @@ class Krb5Utils(object): + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) ++ return cmd ++ ++ def _run_in_env(self, args, stdin=None, extra_env=None): ++ cmd = self.spawn_in_env(args, stdin, extra_env) + out, err = cmd.communicate(stdin) + return cmd.returncode, out.decode('utf-8'), err.decode('utf-8') + +diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py +index 11f80a1803b4ad9b8e8857bf9a8a244d4816f0a2..1ab2a1837687a6c2cf8676124b42538833550c91 100644 +--- a/src/tests/intg/test_kcm.py ++++ b/src/tests/intg/test_kcm.py +@@ -445,3 +445,25 @@ def test_kcm_sec_kdestroy_nocache(setup_for_kcm_sec, + setup_secrets): + testenv = setup_for_kcm_sec + exercise_subsidiaries(testenv) ++ ++def test_kcm_sec_parallel_klist(setup_for_kcm_sec, ++ setup_secrets): ++ """ ++ Test that parallel operations from a single UID are handled well. ++ Regression test for https://pagure.io/SSSD/sssd/issue/3372 ++ """ ++ testenv = setup_for_kcm_sec ++ ++ testenv.k5kdc.add_principal("alice", "alicepw") ++ out, _, _ = testenv.k5util.kinit("alice", "alicepw") ++ assert out == 0 ++ ++ ++ processes = [] ++ for i in range(0,10): ++ p = testenv.k5util.spawn_in_env(['klist', '-A']) ++ processes.append(p) ++ ++ for p in processes: ++ rc = p.wait() ++ assert rc == 0 +-- +2.9.4 + diff --git a/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch b/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch new file mode 100644 index 0000000..f9689ea --- /dev/null +++ b/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch @@ -0,0 +1,115 @@ +From e3b29c9f95d5a5ff007000b254143c337ef0b0dc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 19 May 2017 12:52:47 +0200 +Subject: [PATCH 138/138] ipa: filter IPA users from extdom lookups by + certificate +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The extdom lookup by certificate will return the names of all matching +users, both from the IPA and trusted domains. The IPA users from the +list should not be looked up via the extdom plugin because they are +already lookup up directly. Additionally the lookup might fail and cause +an error which might prevent that the remaining users from the list are +looked up. + +Resolves https://pagure.io/SSSD/sssd/issue/3407 + +Reviewed-by: Pavel Březina +(cherry picked from commit eb7095099b2dd0afb1d028dbc15d8c5a897d90f8) +--- + src/providers/ipa/ipa_s2n_exop.c | 35 ++++++++++++++++++++++++++++++----- + 1 file changed, 30 insertions(+), 5 deletions(-) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index f5f4401f86615dc7f81f844e1096ad43e965c384..15904e0197919c34b1bce58b4bd2c070f99b67a7 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -792,6 +792,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + char **name_list = NULL; + ber_len_t ber_len; + char *fq_name = NULL; ++ struct sss_domain_info *root_domain = NULL; + + if (retoid == NULL || retdata == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Missing OID or data.\n"); +@@ -965,6 +966,8 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + goto done; + } + ++ root_domain = get_domains_head(dom); ++ + while (ber_peek_tag(ber, &ber_len) == LBER_SEQUENCE) { + tag = ber_scanf(ber, "{aa}", &domain_name, &name); + if (tag == LBER_ERROR) { +@@ -983,7 +986,12 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + DEBUG(SSSDBG_TRACE_ALL, "[%s][%s][%s].\n", domain_name, name, + fq_name); + +- ret = add_string_to_list(attrs, fq_name, &name_list); ++ if (strcasecmp(root_domain->name, domain_name) != 0) { ++ ret = add_string_to_list(attrs, fq_name, &name_list); ++ } else { ++ DEBUG(SSSDBG_TRACE_ALL, ++ "[%s] from root domain, skipping.\n", fq_name); ++ } + ber_memfree(domain_name); + ber_memfree(name); + talloc_free(fq_name); +@@ -1228,7 +1236,7 @@ static errno_t ipa_s2n_get_list_step(struct tevent_req *req) + + break; + default: +- DEBUG(SSSDBG_OP_FAILURE, "Unexpected inoput type [%d].\n", ++ DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n", + state->req_input.type); + return EINVAL; + } +@@ -1247,9 +1255,10 @@ static errno_t ipa_s2n_get_list_step(struct tevent_req *req) + + if (state->req_input.type == REQ_INP_NAME + && state->req_input.inp.name != NULL) { +- DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for group [%s].\n", +- ipa_s2n_reqtype2str(state->request_type), +- state->list[state->list_idx]); ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Sending request_type: [%s] for object [%s].\n", ++ ipa_s2n_reqtype2str(state->request_type), ++ state->list[state->list_idx]); + } + + subreq = ipa_s2n_exop_send(state, state->ev, state->sh, need_v1, +@@ -1886,6 +1895,13 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq) + + if (state->simple_attrs->response_type == RESP_NAME_LIST + && state->req_input->type == REQ_INP_CERT) { ++ ++ if (state->simple_attrs->name_list == NULL) { ++ /* No results from sub-domains, nothing to do */ ++ ret = EOK; ++ goto done; ++ } ++ + state->mapped_attrs = sysdb_new_attrs(state); + if (state->mapped_attrs == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n"); +@@ -2640,6 +2656,15 @@ static void ipa_s2n_get_list_done(struct tevent_req *subreq) + return; + } + ++ if (state->attrs == NULL) { ++ /* If this is a request by certificate we are done */ ++ if (state->req_input->type == REQ_INP_CERT) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, EINVAL); ++ } ++ } ++ + ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR, + &sid_str); + if (ret == ENOENT) { +-- +2.9.4 + diff --git a/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch b/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch new file mode 100644 index 0000000..259a343 --- /dev/null +++ b/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch @@ -0,0 +1,50 @@ +From 04a1802749b6ebf72730357b06bf8cabe09ebb01 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 24 May 2017 16:10:26 +0200 +Subject: [PATCH 139/141] krb5: accept changed principal if + krb5_canonicalize=True + +Currently SSSD accepts significant changes in the principal only if +krb5_use_enterprise_principal=True. But canonicalization can lead to +similar changes so they should be accepted in this case as well. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3408 + +Reviewed-by: Robbie Harwood +(cherry picked from commit ca95807a9060e454ee68f6f30558d6f7ee968c39) +--- + src/providers/krb5/krb5_auth.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index 894bd41bde031ac33187bfa3b14202e9429a9198..03ea9d88cac67919d4b9ba3a1cf2efa208662195 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -829,6 +829,7 @@ static void krb5_auth_done(struct tevent_req *subreq) + char *renew_interval_str; + time_t renew_interval_time = 0; + bool use_enterprise_principal; ++ bool canonicalize; + + ret = handle_child_recv(subreq, pd, &buf, &len); + talloc_zfree(subreq); +@@ -908,6 +909,7 @@ static void krb5_auth_done(struct tevent_req *subreq) + + use_enterprise_principal = dp_opt_get_bool(kr->krb5_ctx->opts, + KRB5_USE_ENTERPRISE_PRINCIPAL); ++ canonicalize = dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_CANONICALIZE); + + /* Check if the cases of our upn are correct and update it if needed. + * Fail if the upn differs by more than just the case for non-enterprise +@@ -915,6 +917,7 @@ static void krb5_auth_done(struct tevent_req *subreq) + if (res->correct_upn != NULL && + strcmp(kr->upn, res->correct_upn) != 0) { + if (strcasecmp(kr->upn, res->correct_upn) == 0 || ++ canonicalize == true || + use_enterprise_principal == true) { + talloc_free(kr->upn); + kr->upn = talloc_strdup(kr, res->correct_upn); +-- +2.9.4 + diff --git a/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch b/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch new file mode 100644 index 0000000..e6a08b1 --- /dev/null +++ b/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch @@ -0,0 +1,30 @@ +From 4986fe8b68a3e14a30e8091353bf0679eb3c5e55 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 24 May 2017 21:24:20 +0200 +Subject: [PATCH 140/141] IPA: Avoid using uninitialized ret value when + skipping entries from the joined domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 3e3034199b44e01899ec7ba8152fef3738a0e093) +--- + src/providers/ipa/ipa_s2n_exop.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 15904e0197919c34b1bce58b4bd2c070f99b67a7..3f5f9859554f0b98ecd3fdad31fd66274c5707b0 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -991,6 +991,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx, + } else { + DEBUG(SSSDBG_TRACE_ALL, + "[%s] from root domain, skipping.\n", fq_name); ++ ret = EOK; /* Free resources and continue in the loop */ + } + ber_memfree(domain_name); + ber_memfree(name); +-- +2.9.4 + diff --git a/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch b/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch new file mode 100644 index 0000000..1da145d --- /dev/null +++ b/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch @@ -0,0 +1,30 @@ +From 2ae1485566cbd2b095935aaf7e851d12d2de4513 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 24 May 2017 21:26:22 +0200 +Subject: [PATCH 141/141] IPA: Return from function after marking a request as + finished +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit eb404bcdbbff7e080a93d816e17b8cec04f79fc4) +--- + src/providers/ipa/ipa_s2n_exop.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c +index 3f5f9859554f0b98ecd3fdad31fd66274c5707b0..39ed17cbf0e8c523212084197e9f2963fed88dc8 100644 +--- a/src/providers/ipa/ipa_s2n_exop.c ++++ b/src/providers/ipa/ipa_s2n_exop.c +@@ -2664,6 +2664,7 @@ static void ipa_s2n_get_list_done(struct tevent_req *subreq) + } else { + tevent_req_error(req, EINVAL); + } ++ return; + } + + ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR, +-- +2.9.4 + diff --git a/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch b/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch new file mode 100644 index 0000000..4669a77 --- /dev/null +++ b/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch @@ -0,0 +1,175 @@ +From c7c087b5485d50e8689d31fd9d52af935ae398be Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Sun, 9 Apr 2017 20:50:47 +0200 +Subject: [PATCH 142/142] HBAC: Do not rely on originalMemberOf, use the sysdb + memberof links instead + +The IPA HBAC code used to read the group members from the +originalMemberOf attribute value for performance reasons. However, +especially on IPA clients trusting an AD domain, the originalMemberOf +attribute value is often not synchronized correctly. + +Instead of going through the work of maintaining both member/memberOf +and originalMemberOf, let's just do an ASQ search for the group names of +the groups the user is a member of in the cache and read their +SYSBD_NAME attribute. + +To avoid clashing between similarly-named groups in IPA and in AD, we +look at the container of the group. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3382 + +Reviewed-by: Sumit Bose +(cherry picked from commit c92e49144978ad3b6c9fffa8803ebdad8f6f5b18) +--- + src/providers/ipa/ipa_hbac_common.c | 97 +++++++++++++++++++++++++------------ + 1 file changed, 67 insertions(+), 30 deletions(-) + +diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c +index b99b75d322930f16412f6abd4cdf0d7e0b59c32c..ba677965a3eb68a54baf99b1875bca2acbb76c99 100644 +--- a/src/providers/ipa/ipa_hbac_common.c ++++ b/src/providers/ipa/ipa_hbac_common.c +@@ -507,15 +507,15 @@ hbac_eval_user_element(TALLOC_CTX *mem_ctx, + struct hbac_request_element **user_element) + { + errno_t ret; +- unsigned int i; + unsigned int num_groups = 0; + TALLOC_CTX *tmp_ctx; +- const char *member_dn; + struct hbac_request_element *users; +- struct ldb_message *msg; +- struct ldb_message_element *el; +- const char *attrs[] = { SYSDB_ORIG_MEMBEROF, NULL }; + char *shortname; ++ const char *fqgroupname = NULL; ++ struct sss_domain_info *ipa_domain; ++ struct ldb_dn *ipa_groups_basedn; ++ struct ldb_result *res; ++ int exp_comp; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; +@@ -533,56 +533,93 @@ hbac_eval_user_element(TALLOC_CTX *mem_ctx, + } + users->name = talloc_steal(users, shortname); + +- /* Read the originalMemberOf attribute +- * This will give us the list of both POSIX and +- * non-POSIX groups that this user belongs to. ++ ipa_domain = get_domains_head(domain); ++ if (ipa_domain == NULL) { ++ ret = EINVAL; ++ goto done; ++ } ++ ++ ipa_groups_basedn = ldb_dn_new_fmt(tmp_ctx, sysdb_ctx_get_ldb(domain->sysdb), ++ SYSDB_TMPL_GROUP_BASE, ipa_domain->name); ++ if (ipa_groups_basedn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ /* +1 because there will be a RDN preceding the base DN */ ++ exp_comp = ldb_dn_get_comp_num(ipa_groups_basedn) + 1; ++ ++ /* ++ * Get all the groups the user is a member of. ++ * This includes both POSIX and non-POSIX groups. + */ +- ret = sysdb_search_user_by_name(tmp_ctx, domain, username, +- attrs, &msg); ++ ret = sysdb_initgroups(tmp_ctx, domain, username, &res); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "Could not determine user memberships for [%s]\n", +- users->name); ++ "sysdb_asq_search failed [%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + +- el = ldb_msg_find_element(msg, SYSDB_ORIG_MEMBEROF); +- if (el == NULL || el->num_values == 0) { ++ if (res->count == 0) { ++ /* This should not happen at this point */ ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "User [%s] not found in cache.\n", username); ++ ret = ENOENT; ++ goto done; ++ } else if (res->count == 1) { ++ /* The first item is the user entry */ + DEBUG(SSSDBG_TRACE_LIBS, "No groups for [%s]\n", users->name); + ret = create_empty_grouplist(users); + goto done; + } + DEBUG(SSSDBG_TRACE_LIBS, +- "[%d] groups for [%s]\n", el->num_values, users->name); ++ "[%u] groups for [%s]\n", res->count - 1, username); + +- users->groups = talloc_array(users, const char *, el->num_values + 1); ++ /* This also includes the sentinel, b/c we'll skip the user entry below */ ++ users->groups = talloc_array(users, const char *, res->count); + if (users->groups == NULL) { + ret = ENOMEM; + goto done; + } + +- for (i = 0; i < el->num_values; i++) { +- member_dn = (const char *)el->values[i].data; ++ /* Start counting from 1 to exclude the user entry */ ++ for (size_t i = 1; i < res->count; i++) { ++ /* Only groups from the IPA domain can be referenced from HBAC rules. To ++ * avoid evaluating groups which might even have the same name, but come ++ * from a trusted domain, we first copy the DN to a temporary one.. ++ */ ++ if (ldb_dn_get_comp_num(res->msgs[i]->dn) != exp_comp ++ || ldb_dn_compare_base(ipa_groups_basedn, ++ res->msgs[i]->dn) != 0) { ++ DEBUG(SSSDBG_FUNC_DATA, ++ "Skipping non-IPA group %s\n", ++ ldb_dn_get_linearized(res->msgs[i]->dn)); ++ continue; ++ } + +- ret = get_ipa_groupname(users->groups, domain->sysdb, member_dn, +- &users->groups[num_groups]); +- if (ret != EOK && ret != ERR_UNEXPECTED_ENTRY_TYPE) { ++ fqgroupname = ldb_msg_find_attr_as_string(res->msgs[i], SYSDB_NAME, NULL); ++ if (fqgroupname == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, +- "Skipping malformed entry [%s]\n", member_dn); ++ "Skipping malformed entry [%s]\n", ++ ldb_dn_get_linearized(res->msgs[i]->dn)); + continue; +- } else if (ret == EOK) { +- DEBUG(SSSDBG_TRACE_LIBS, "Added group [%s] for user [%s]\n", +- users->groups[num_groups], users->name); +- num_groups++; ++ } ++ ++ ret = sss_parse_internal_fqname(tmp_ctx, fqgroupname, ++ &shortname, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Malformed name %s, skipping!\n", fqgroupname); + continue; + } +- /* Skip entries that are not groups */ +- DEBUG(SSSDBG_TRACE_INTERNAL, +- "Skipping non-group memberOf [%s]\n", member_dn); ++ ++ users->groups[num_groups] = talloc_steal(users->groups, shortname); ++ DEBUG(SSSDBG_TRACE_LIBS, "Added group [%s] for user [%s]\n", ++ users->groups[num_groups], users->name); ++ num_groups++; + } + users->groups[num_groups] = NULL; + +- if (num_groups < el->num_values) { ++ if (num_groups < (res->count - 1)) { + /* Shrink the array memory */ + users->groups = talloc_realloc(users, users->groups, const char *, + num_groups+1); +-- +2.9.4 + diff --git a/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch b/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch new file mode 100644 index 0000000..8b1c3c5 --- /dev/null +++ b/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch @@ -0,0 +1,53 @@ +From 270121098caff2496da73795fe586ff734ae1e56 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 4 Apr 2017 18:01:02 +0200 +Subject: [PATCH 143/152] VALIDATORS: Add subdomain section +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add separate rule for subdomain sections. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3356 + +Reviewed-by: Lukáš Slebodník +--- + src/config/cfg_rules.ini | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index e47ff33242d6a9e5979fe0eb8eea14c2af28685a..4b30e8fc43b50844023e7fffa607a59530a302f0 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -11,7 +11,8 @@ section = ifp + section = secrets + section = kcm + section_re = ^secrets/users/[0-9]\+$ +-section_re = ^domain/.*$ ++section_re = ^domain/[^/\@]\+$ ++section_re = ^domain/[^/\@]\+/[^/\@]\+$ + section_re = ^application/.*$ + + [rule/allowed_sssd_options] +@@ -698,3 +699,17 @@ validator = ini_allowed_options + section_re = ^application/.*$ + + option = inherit_from ++ ++[rule/allowed_subdomain_options] ++validator = ini_allowed_options ++section_re = ^domain/[^/\@]\+/[^/\@]\+$ ++ ++option = ldap_search_base ++option = ldap_user_search_base ++option = ldap_group_search_base ++option = ldap_netgroup_search_base ++option = ldap_service_search_base ++option = ad_server ++option = ad_backup_server ++option = ad_site ++option = use_fully_qualified_names +-- +2.9.4 + diff --git a/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch b/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch new file mode 100644 index 0000000..d644b35 --- /dev/null +++ b/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch @@ -0,0 +1,48 @@ +From 39864f9ce41ce892fe3cafb769584173299f49f7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 4 Apr 2017 19:07:12 +0200 +Subject: [PATCH 144/152] VALIDATORS: Remove application section domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Application domains can use the same options as normal domains section +with one more additional option. + +We could either duplicate all options from the domain section also in +the application domain section + add the one additional option or +add this one option to the domain section even though it is not meant +to be used there to avoid duplication of all domain options in the +rule for application section. + +It would be could to enhance the validators in libini to allow +something like 'include' section in order to avoid this issue +in the future. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3356 + +Reviewed-by: Lukáš Slebodník +--- + src/config/cfg_rules.ini | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 4b30e8fc43b50844023e7fffa607a59530a302f0..a30fe57e262716abeb2d2af9c3add326122ee4ca 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -694,10 +694,7 @@ option = ldap_user_uid_number + option = ldap_user_uuid + option = ldap_use_tokengroups + +-[rule/allowed_application_options] +-validator = ini_allowed_options +-section_re = ^application/.*$ +- ++# For application domains + option = inherit_from + + [rule/allowed_subdomain_options] +-- +2.9.4 + diff --git a/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch b/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch new file mode 100644 index 0000000..d80f3cf --- /dev/null +++ b/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch @@ -0,0 +1,35 @@ +From fc6bffc8523e6decf4656182f8caf72236e45c3d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 4 Apr 2017 20:06:40 +0200 +Subject: [PATCH 145/152] VALIDATORS: Escape special regex chars +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The rule allowed_domain_options did not work because +of bad regex. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3356 + +Reviewed-by: Lukáš Slebodník +--- + src/config/cfg_rules.ini | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index a30fe57e262716abeb2d2af9c3add326122ee4ca..628f2e0e0a040bad5128d00d9348aa91170ed704 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -295,7 +295,7 @@ option = responder_idle_timeout + + [rule/allowed_domain_options] + validator = ini_allowed_options +-section_re = ^(domain|application)/.*$ ++section_re = ^\(domain\|application\)/.*$ + + option = debug + option = debug_level +-- +2.9.4 + diff --git a/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch b/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch new file mode 100644 index 0000000..dae5f6c --- /dev/null +++ b/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch @@ -0,0 +1,328 @@ +From 897216b87352e9f80181be6f1a036163c599ba46 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Fri, 26 May 2017 19:58:48 +0200 +Subject: [PATCH 146/152] TESTS: Add unit tests for cfg validation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add infrastructure for unit tests for validators. + +Reviewed-by: Lukáš Slebodník +--- + Makefile.am | 16 +++ + src/tests/cmocka/test_config_check.c | 268 +++++++++++++++++++++++++++++++++++ + 2 files changed, 284 insertions(+) + create mode 100644 src/tests/cmocka/test_config_check.c + +diff --git a/Makefile.am b/Makefile.am +index a6279133b56dcd5bcbd1306ae8f2ce18d90c2c12..503c8cfd795b503f566431c08a56a56147180322 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -252,6 +252,7 @@ if HAVE_CMOCKA + dp_opt_tests \ + responder-get-domains-tests \ + sbus-internal-tests \ ++ config_check-tests \ + sss_sifp-tests \ + test_search_bases \ + test_ldap_auth \ +@@ -2429,6 +2430,21 @@ sbus_internal_tests_LDADD = \ + libsss_debug.la \ + libsss_test_common.la + ++config_check_tests_SOURCES = \ ++ src/tests/cmocka/test_config_check.c \ ++ $(NULL) ++config_check_tests_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++config_check_tests_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(POPT_LIBS) \ ++ $(INI_CONFIG_LIBS) \ ++ $(TALLOC_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ $(NULL) ++ + test_find_uid_SOURCES = \ + src/tests/cmocka/test_find_uid.c \ + src/util/find_uid.c \ +diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c +new file mode 100644 +index 0000000000000000000000000000000000000000..8fc0b01f3ef3fe03152efd979a3e96c21ba567cc +--- /dev/null ++++ b/src/tests/cmocka/test_config_check.c +@@ -0,0 +1,268 @@ ++/* ++ Authors: ++ Michal Zidek ++ ++ Copyright (C) 2017 Red Hat ++ ++ Config file validators test ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++#include ++ ++#include "util/sss_ini.h" ++#include "tests/cmocka/common_mock.h" ++ ++#ifdef HAVE_LIBINI_CONFIG_V1_3 ++ ++#define RULES_PATH ABS_SRC_DIR"/src/config/cfg_rules.ini" ++ ++struct sss_ini_initdata { ++ char **error_list; ++ struct ref_array *ra_success_list; ++ struct ref_array *ra_error_list; ++ struct ini_cfgobj *sssd_config; ++ struct value_obj *obj; ++ const struct stat *cstat; ++ struct ini_cfgfile *file; ++}; ++ ++void config_check_test_common(const char *cfg_string, ++ size_t num_errors_expected, ++ const char **errors_expected) ++{ ++ struct sss_ini_initdata *init_data; ++ size_t num_errors; ++ char **strs; ++ int ret; ++ TALLOC_CTX *tmp_ctx; ++ ++ tmp_ctx = talloc_new(NULL); ++ assert_non_null(tmp_ctx); ++ ++ init_data = sss_ini_initdata_init(tmp_ctx); ++ ++ ret = ini_config_file_from_mem(discard_const(cfg_string), ++ strlen(cfg_string), ++ &init_data->file); ++ assert_int_equal(ret, EOK); ++ ++ ret = ini_config_create(&(init_data->sssd_config)); ++ assert_int_equal(ret, EOK); ++ ++ ret = ini_config_parse(init_data->file, ++ INI_STOP_ON_ANY, ++ INI_MV1S_OVERWRITE, ++ INI_PARSE_NOWRAP, ++ init_data->sssd_config); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_ini_call_validators_strs(tmp_ctx, init_data, ++ RULES_PATH, ++ &strs, &num_errors); ++ assert_int_equal(ret, EOK); ++ ++ /* Output from validators */ ++ for (int i = 0; i < num_errors; i++) { ++ /* Keep this printf loop for faster debugging */ ++ printf("%s\n", strs[i]); ++ } ++ ++ for (int i = 0; i < num_errors && i <= num_errors_expected; i++) { ++ assert_string_equal(strs[i], errors_expected[i]); ++ } ++ ++ /* Check if the number of errors is the same */ ++ assert_int_equal(num_errors_expected, num_errors); ++ ++ sss_ini_close_file(init_data); ++ sss_ini_config_destroy(init_data); ++ talloc_free(tmp_ctx); ++} ++ ++void config_check_test_bad_section_name(void **state) ++{ ++ char cfg_str[] = "[sssssssssssssd]"; ++ const char *expected_errors[] = { ++ "[rule/allowed_sections]: Section [sssssssssssssd] is not allowed. " ++ "Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_sssd_option_name(void **state) ++{ ++ char cfg_str[] = "[sssd]\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'sssd'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_pam_option_name(void **state) ++{ ++ char cfg_str[] = "[pam]\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_pam_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'pam'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_nss_option_name(void **state) ++{ ++ char cfg_str[] = "[nss]\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_nss_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'nss'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_pac_option_name(void **state) ++{ ++ char cfg_str[] = "[pac]\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_pac_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'pac'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_ifp_option_name(void **state) ++{ ++ char cfg_str[] = "[ifp]\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_ifp_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'ifp'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_domain_option_name(void **state) ++{ ++ char cfg_str[] = "[domain/A.test\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'domain/A.test'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_appdomain_option_name(void **state) ++{ ++ char cfg_str[] = "[application/myapp\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'application/myapp'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_bad_subdom_option_name(void **state) ++{ ++ char cfg_str[] = "[domain/A.test/B.A.test]\n" ++ "debug_leTYPOvel = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not " ++ "allowed in section 'domain/A.test/B.A.test'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_good_sections(void **state) ++{ ++ char cfg_str[] = "[sssd]\n" ++ "[pam]\n" ++ "[nss]\n" ++ "[domain/testdom.test]\n" ++ "[domain/testdom.test/testsubdom.testdom.test]\n" ++ "[application/myapp]\n" ++ "[secrets]\n" ++ "[ifp]\n" ++ "[pac]\n"; ++ const char *expected_errors[] = { NULL }; ++ ++ config_check_test_common(cfg_str, 0, expected_errors); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(config_check_test_bad_section_name), ++ cmocka_unit_test(config_check_test_bad_sssd_option_name), ++ cmocka_unit_test(config_check_test_bad_pam_option_name), ++ cmocka_unit_test(config_check_test_bad_nss_option_name), ++ cmocka_unit_test(config_check_test_bad_pac_option_name), ++ cmocka_unit_test(config_check_test_bad_ifp_option_name), ++ cmocka_unit_test(config_check_test_good_sections), ++ }; ++ ++ /* Set debug level to invalid value so we can decide if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while ((opt = poptGetNextOpt(pc)) != -1) { ++ switch (opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ tests_set_cwd(); ++ return cmocka_run_group_tests(tests, NULL, NULL); ++} ++ ++#else /* !HAVE_LIBINI_CONFIG_V1_3 */ ++ ++int main(int argc, const char *argv[]) ++{ ++ fprintf(stderr, "%s requires newer version of libini\n", argv[0]); ++ return 0; ++} ++ ++#endif /* HAVE_LIBINI_CONFIG_V1_3 */ +-- +2.9.4 + diff --git a/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch b/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch new file mode 100644 index 0000000..3d015fc --- /dev/null +++ b/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch @@ -0,0 +1,29 @@ +From 8a6087c3b53bbe26cb212e60af74da981529f57d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 30 May 2017 12:05:39 +0200 +Subject: [PATCH 147/152] MAN: Fix typo in trusted domain section +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +--- + src/man/sssd.conf.5.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index c4e30396f16c40db37af2f56ac218b6e37201ef7..a35f2807eac8bb89d6cb1dd0a48f738d71a7578f 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -2912,7 +2912,7 @@ ldap_user_extra_attrs = phone:telephoneNumber + + Some options used in the domain section can also be used in the + trusted domain section, that is, in a section called +- [domain/DOMAIN_NAME]/TRUSTED_DOMAIN_NAME]. ++ [domain/DOMAIN_NAME/TRUSTED_DOMAIN_NAME]. + Currently supported options in the trusted domain section are: + + ldap_search_base, +-- +2.9.4 + diff --git a/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch b/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch new file mode 100644 index 0000000..3ca41ad --- /dev/null +++ b/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch @@ -0,0 +1,36 @@ +From b32bb7226b89777063e4cd49373ce86353abd74c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Tue, 30 May 2017 13:17:45 +0200 +Subject: [PATCH 148/152] VALIDATORS: Change regex for app domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use the same restrictions for application domains that we use for +normal domain. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3356 + +Reviewed-by: Lukáš Slebodník +--- + src/config/cfg_rules.ini | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 628f2e0e0a040bad5128d00d9348aa91170ed704..2c8c0cb98ed039c374c827775798f61369c1521e 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -13,7 +13,8 @@ section = kcm + section_re = ^secrets/users/[0-9]\+$ + section_re = ^domain/[^/\@]\+$ + section_re = ^domain/[^/\@]\+/[^/\@]\+$ +-section_re = ^application/.*$ ++section_re = ^application/[^/\@]\+$ ++ + + [rule/allowed_sssd_options] + validator = ini_allowed_options +-- +2.9.4 + diff --git a/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch b/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch new file mode 100644 index 0000000..b99885a --- /dev/null +++ b/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch @@ -0,0 +1,154 @@ +From b94b578fac8f94d42fd6fb691438d2dbe5248309 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Wed, 31 May 2017 14:21:02 +0200 +Subject: [PATCH 149/152] VALIDATORS: Detect inherit_from in normal domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds new sssd specific validator. In the future we +can add more checks in it, but currently it only checks if +the option inherit_from is used on normal domain and reports +error if it is. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3356 + +Reviewed-by: Lukáš Slebodník +--- + src/config/cfg_rules.ini | 3 ++ + src/tests/cmocka/test_config_check.c | 22 +++++++++++++++ + src/util/sss_ini.c | 53 +++++++++++++++++++++++++++++++++++- + 3 files changed, 77 insertions(+), 1 deletion(-) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 2c8c0cb98ed039c374c827775798f61369c1521e..744446478e5d5489cd86d8e15ce8e178cf5e3a91 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -711,3 +711,6 @@ option = ad_server + option = ad_backup_server + option = ad_site + option = use_fully_qualified_names ++ ++[rule/sssd_checks] ++validator = sssd_checks +diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c +index 8fc0b01f3ef3fe03152efd979a3e96c21ba567cc..bab3226c004fb9495471af7c7d3f6861552d8a86 100644 +--- a/src/tests/cmocka/test_config_check.c ++++ b/src/tests/cmocka/test_config_check.c +@@ -217,6 +217,27 @@ void config_check_test_good_sections(void **state) + config_check_test_common(cfg_str, 0, expected_errors); + } + ++void config_check_test_inherit_from_in_normal_dom(void **state) ++{ ++ char cfg_str[] = "[domain/A.test]\n" ++ "inherit_from = domain\n"; ++ const char *expected_errors[] = { ++ "[rule/sssd_checks]: Attribute 'inherit_from' is not allowed in " ++ "section 'domain/A.test'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ ++void config_check_test_inherit_from_in_app_dom(void **state) ++{ ++ char cfg_str[] = "[application/A.test]\n" ++ "inherit_from = domain\n"; ++ const char *expected_errors[] = { NULL }; ++ ++ config_check_test_common(cfg_str, 0, expected_errors); ++} ++ + int main(int argc, const char *argv[]) + { + poptContext pc; +@@ -235,6 +256,7 @@ int main(int argc, const char *argv[]) + cmocka_unit_test(config_check_test_bad_pac_option_name), + cmocka_unit_test(config_check_test_bad_ifp_option_name), + cmocka_unit_test(config_check_test_good_sections), ++ cmocka_unit_test(config_check_test_inherit_from_in_normal_dom), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ +diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c +index e56006c05555d6e0c5e726e83771abce5a72b139..175a4cfaba7ea964aee174e928d5e3c1e81de638 100644 +--- a/src/util/sss_ini.c ++++ b/src/util/sss_ini.c +@@ -561,12 +561,63 @@ error: + } + + #ifdef HAVE_LIBINI_CONFIG_V1_3 ++/* Here we can put custom SSSD specific checks that can not be implemented ++ * using libini validators */ ++static int custom_sssd_checks(const char *rule_name, ++ struct ini_cfgobj *rules_obj, ++ struct ini_cfgobj *config_obj, ++ struct ini_errobj *errobj, ++ void **data) ++{ ++ char **cfg_sections = NULL; ++ int num_cfg_sections; ++ struct value_obj *vo = NULL; ++ char dom_prefix[] = "domain/"; ++ int ret; ++ ++ /* Get all sections in configuration */ ++ cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ /* Check if a normal domain section (not application domains) has option ++ * inherit_from and report error if it does */ ++ for (int i = 0; i < num_cfg_sections; i++) { ++ if (strncmp(dom_prefix, cfg_sections[i], strlen(dom_prefix)) == 0) { ++ ret = ini_get_config_valueobj(cfg_sections[i], ++ "inherit_from", ++ config_obj, ++ INI_GET_NEXT_VALUE, ++ &vo); ++ if (vo != NULL) { ++ ret = ini_errobj_add_msg(errobj, ++ "Attribute 'inherit_from' is not " ++ "allowed in section '%s'. Check for " ++ "typos.", ++ cfg_sections[i]); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ } ++ } ++ ++ ret = EOK; ++done: ++ ini_free_section_list(cfg_sections); ++ return EOK; ++} ++ + static int sss_ini_call_validators_errobj(struct sss_ini_initdata *data, + const char *rules_path, + struct ini_errobj *errobj) + { + int ret; + struct ini_cfgobj *rules_cfgobj = NULL; ++ struct ini_validator custom_sssd = { "sssd_checks", custom_sssd_checks, ++ NULL }; ++ struct ini_validator *sss_validators[] = { &custom_sssd, NULL }; + + ret = ini_rules_read_from_file(rules_path, &rules_cfgobj); + if (ret != EOK) { +@@ -575,7 +626,7 @@ static int sss_ini_call_validators_errobj(struct sss_ini_initdata *data, + goto done; + } + +- ret = ini_rules_check(rules_cfgobj, data->sssd_config, NULL, errobj); ++ ret = ini_rules_check(rules_cfgobj, data->sssd_config, sss_validators, errobj); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "ini_rules_check failed %d [%s]\n", ret, strerror(ret)); +-- +2.9.4 + diff --git a/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch b/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch new file mode 100644 index 0000000..8ce1bb0 --- /dev/null +++ b/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch @@ -0,0 +1,36 @@ +From 03bfcf1746b163fa3fbce9f2741db77064ac84e7 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Wed, 31 May 2017 17:35:27 +0200 +Subject: [PATCH 150/152] VALIDATOR: prevent duplicite report from subdomain + sections +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Issues is subdomain sections e.g. "[domain/A.test/B.A.test]" were +reported twice. + +[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos. +[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos. + +Reviewed-by: Michal Židek +--- + src/config/cfg_rules.ini | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 744446478e5d5489cd86d8e15ce8e178cf5e3a91..d6506b7c3cee13f7c5400a546deb787e755abc8b 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -296,7 +296,7 @@ option = responder_idle_timeout + + [rule/allowed_domain_options] + validator = ini_allowed_options +-section_re = ^\(domain\|application\)/.*$ ++section_re = ^\(domain\|application\)/[^/]\+$ + + option = debug + option = debug_level +-- +2.9.4 + diff --git a/SOURCES/0151-test_config_check-Fix-few-issues.patch b/SOURCES/0151-test_config_check-Fix-few-issues.patch new file mode 100644 index 0000000..9e48ed1 --- /dev/null +++ b/SOURCES/0151-test_config_check-Fix-few-issues.patch @@ -0,0 +1,92 @@ +From 15f997c22228f4b87a841148bf05c6911107879c Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Wed, 31 May 2017 17:16:47 +0200 +Subject: [PATCH 151/152] test_config_check: Fix few issues +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* enable few tests +* malformed configuration file due to missing closing ']' +* fix few expected failures +* add few sections into whitelist test +* crash in test if count of expected failures is different then real + value + +[ RUN ] config_check_test_bad_subdom_option_name +[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos. +[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos. +[ ERROR ] --- Test failed with exception: Segmentation fault(11) + +Reviewed-by: Michal Židek +--- + src/tests/cmocka/test_config_check.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c +index bab3226c004fb9495471af7c7d3f6861552d8a86..0066ebe77e9f174449461caebdb3359380bc19b5 100644 +--- a/src/tests/cmocka/test_config_check.c ++++ b/src/tests/cmocka/test_config_check.c +@@ -81,6 +81,7 @@ void config_check_test_common(const char *cfg_string, + /* Keep this printf loop for faster debugging */ + printf("%s\n", strs[i]); + } ++ assert_int_equal(num_errors, num_errors_expected); + + for (int i = 0; i < num_errors && i <= num_errors_expected; i++) { + assert_string_equal(strs[i], errors_expected[i]); +@@ -167,7 +168,7 @@ void config_check_test_bad_ifp_option_name(void **state) + + void config_check_test_bad_domain_option_name(void **state) + { +- char cfg_str[] = "[domain/A.test\n" ++ char cfg_str[] = "[domain/A.test]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " +@@ -179,10 +180,10 @@ void config_check_test_bad_domain_option_name(void **state) + + void config_check_test_bad_appdomain_option_name(void **state) + { +- char cfg_str[] = "[application/myapp\n" ++ char cfg_str[] = "[application/myapp]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { +- "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " ++ "[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'application/myapp'. Check for typos.", + }; + +@@ -194,7 +195,7 @@ void config_check_test_bad_subdom_option_name(void **state) + char cfg_str[] = "[domain/A.test/B.A.test]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { +- "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not " ++ "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'domain/A.test/B.A.test'. Check for typos.", + }; + +@@ -210,6 +211,8 @@ void config_check_test_good_sections(void **state) + "[domain/testdom.test/testsubdom.testdom.test]\n" + "[application/myapp]\n" + "[secrets]\n" ++ "[secrets/users/1000]\n" ++ "[ssh]\n" + "[ifp]\n" + "[pac]\n"; + const char *expected_errors[] = { NULL }; +@@ -255,8 +258,11 @@ int main(int argc, const char *argv[]) + cmocka_unit_test(config_check_test_bad_nss_option_name), + cmocka_unit_test(config_check_test_bad_pac_option_name), + cmocka_unit_test(config_check_test_bad_ifp_option_name), ++ cmocka_unit_test(config_check_test_bad_appdomain_option_name), ++ cmocka_unit_test(config_check_test_bad_subdom_option_name), + cmocka_unit_test(config_check_test_good_sections), + cmocka_unit_test(config_check_test_inherit_from_in_normal_dom), ++ cmocka_unit_test(config_check_test_inherit_from_in_app_dom), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ +-- +2.9.4 + diff --git a/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch b/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch new file mode 100644 index 0000000..89845c7 --- /dev/null +++ b/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch @@ -0,0 +1,81 @@ +From 3ee575c2852adb9d5a5c0a4616c082afc6779a8e Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 1 Jun 2017 09:51:31 +0200 +Subject: [PATCH 152/152] KRB5: Fix access_provider=krb5 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The domain type (posix or not) was being sent to the krb5_child always, +but the buffer only had enough space in case of authentication, not +authorization. Bug was introduced in the commit + 861ab44e8148208425b67c4711bc8fade10fd3ed + +This patch makes the buffer one uint32_t unit larger. + +To reproduce, just set up sssd.conf with: + access_provider = krb5 + +Without the patch, you would see messages like: + ==14111== Invalid write of size 2 + ==14111== at 0x4C3041B: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1018) + ==14111== by 0xE0EE275: safealign_memcpy (util_safealign.h:51) + ==14111== by 0xE0EECB3: create_send_buffer (krb5_child_handler.c:239) + ==14111== by 0xE0EFDDE: handle_child_send (krb5_child_handler.c:529) + ==14111== by 0xE0EDEDD: krb5_access_send (krb5_access.c:149) + ==14111== by 0xE0ED32F: krb5_pam_handler_send (krb5_auth.c:1250) + ==14111== by 0x418868: file_dp_request (dp_request.c:254) + ==14111== by 0x418976: dp_req_send (dp_request.c:300) + ==14111== by 0x41C25F: dp_pam_handler (dp_target_auth.c:219) + ==14111== by 0x52B3456: sbus_request_invoke_or_finish (sssd_dbus_request.c:71) + ==14111== by 0x52B0F37: sbus_message_handler_got_caller_id (sssd_dbus_interface.c:1048) + ==14111== by 0x923C923: tevent_common_loop_immediate (tevent_immediate.c:135) + ==14111== Address 0x126ab506 is 150 bytes inside a block of size 151 alloc'd + ==14111== at 0x4C2BBAD: malloc (vg_replace_malloc.c:299) + ==14111== by 0x944D7F4: __talloc_with_prefix (talloc.c:698) + ==14111== by 0x944D7F4: __talloc (talloc.c:739) + ==14111== by 0x944D7F4: _talloc_named_const (talloc.c:896) + ==14111== by 0x944D7F4: talloc_named_const (talloc.c:1675) + ==14111== by 0xE0EE7B6: create_send_buffer (krb5_child_handler.c:185) + ==14111== by 0xE0EFDDE: handle_child_send (krb5_child_handler.c:529) + ==14111== by 0xE0EDEDD: krb5_access_send (krb5_access.c:149) + ==14111== by 0xE0ED32F: krb5_pam_handler_send (krb5_auth.c:1250) + ==14111== by 0x418868: file_dp_request (dp_request.c:254) + ==14111== by 0x418976: dp_req_send (dp_request.c:300) + ==14111== by 0x41C25F: dp_pam_handler (dp_target_auth.c:219) + ==14111== by 0x52B3456: sbus_request_invoke_or_finish (sssd_dbus_request.c:71) + ==14111== by 0x52B0F37: sbus_message_handler_got_caller_id (sssd_dbus_interface.c:1048) + ==14111== by 0x923C923: tevent_common_loop_immediate (tevent_immediate.c:135) + +Resolves: +https://pagure.io/SSSD/sssd/issue/3418 + +Reviewed-by: Lukáš Slebodník +--- + src/providers/krb5/krb5_child_handler.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c +index 87e79a06e917aadb622455bccfc2e9c6769f70c2..11ac867e62d2ff96b827cf6d4ff341fc8ff0a286 100644 +--- a/src/providers/krb5/krb5_child_handler.c ++++ b/src/providers/krb5/krb5_child_handler.c +@@ -156,14 +156,14 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + return ENOMEM; + } + +- buf->size = 8*sizeof(uint32_t) + strlen(kr->upn); ++ buf->size = 9*sizeof(uint32_t) + strlen(kr->upn); + + if (kr->pd->cmd == SSS_PAM_AUTHENTICATE || + kr->pd->cmd == SSS_PAM_PREAUTH || + kr->pd->cmd == SSS_CMD_RENEW || + kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || + kr->pd->cmd == SSS_PAM_CHAUTHTOK) { +- buf->size += 5*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) + ++ buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) + + sss_authtok_get_size(kr->pd->authtok); + + buf->size += sizeof(uint32_t); +-- +2.9.4 + diff --git a/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch b/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch new file mode 100644 index 0000000..e06105d --- /dev/null +++ b/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch @@ -0,0 +1,86 @@ +From 9581287c1b5e13a38182af12328ace781957a118 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 30 May 2017 14:40:07 +0200 +Subject: [PATCH 153/160] BUILD: Improve error messages for optional + dependencies + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 8ccc9b7c317cf5ee8f295b38bfc4c2b7d551f8f1) +--- + configure.ac | 2 +- + contrib/sssd.spec.in | 6 +++++- + src/external/libcurl.m4 | 6 +++++- + src/external/libjansson.m4 | 5 +++-- + 4 files changed, 14 insertions(+), 5 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 80d8ea9ff5785b0d76edbb04f454d0dd8c8a1e6d..e8fe1d47e1803cc570295cf6512a3363e63c51c5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -197,7 +197,6 @@ m4_include([src/external/service.m4]) + + if test x$with_secrets = xyes; then + m4_include([src/external/libhttp_parser.m4]) +- m4_include([src/external/libjansson.m4]) + fi + + if test x$with_kcm = xyes; then +@@ -206,6 +205,7 @@ fi + + if test x$with_kcm = xyes -o x$with_secrets = xyes; then + m4_include([src/external/libcurl.m4]) ++ m4_include([src/external/libjansson.m4]) + fi + + # This variable is defined by external/libcurl.m4, but conditionals +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 39a974edebba3dbcd7625d1729b4a7330eaa8a27..b19702d091862e25bea352901b85406ccda1db65 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -228,10 +228,14 @@ BuildRequires: systemtap-sdt-devel + %endif + %if (0%{?with_secrets} == 1) + BuildRequires: http-parser-devel +-BuildRequires: jansson-devel + %endif ++%if (0%{?with_kcm} == 1) + BuildRequires: libuuid-devel ++%endif ++%if (0%{?with_secrets} == 1 || 0%{?with_kcm} == 1) ++BuildRequires: jansson-devel + BuildRequires: libcurl-devel ++%endif + + %description + Provides a set of daemons to manage access to remote directories and +diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4 +index 42be308cd1e4b04e736daf887be9b75ea92db80e..94cea9ebe40f07c18452b8c2faf82e81e1dc766b 100644 +--- a/src/external/libcurl.m4 ++++ b/src/external/libcurl.m4 +@@ -1,5 +1,9 @@ + PKG_CHECK_MODULES([CURL], [libcurl], [found_libcurl=yes], +- [AC_MSG_ERROR([The libcurl development library was not found.])]) ++ [AC_MSG_ERROR([The libcurl development library was not found. ++You must have the header file curl/curl.h installed to build sssd ++with secrets and KCM responder. If you want to build sssd without these ++responders then specify --without-secrets --without-kcm when running configure. ++])]) + + AS_IF([test x"$found_libcurl" = xyes], + CFLAGS="$CFLAGS $CURL_CFLAGS" +diff --git a/src/external/libjansson.m4 b/src/external/libjansson.m4 +index 48a4a5fd8df4ac41312a596b5ebd5de7474e75f1..d87769848558efdd32325e01d8d222bb517b4c45 100644 +--- a/src/external/libjansson.m4 ++++ b/src/external/libjansson.m4 +@@ -13,5 +13,6 @@ AS_IF([test x"$found_jansson" != xyes], + [-L$sss_extra_libdir -ljanson])], + [AC_MSG_ERROR([ + You must have the header file jansson.h installed to build sssd +-with secrets responder. If you want to build sssd without secret responder +-then specify --without-secrets when running configure.])])]) ++with secrets and KCM responder. If you want to build sssd without these ++responders then specify --without-secrets --without-kcm when running configure. ++])])]) +-- +2.9.4 + diff --git a/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch b/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch new file mode 100644 index 0000000..0b1ddaa --- /dev/null +++ b/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch @@ -0,0 +1,77 @@ +From d363bd0f829fa7af5f96c2b07b975b7b2c5fdcfa Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 2 May 2017 15:25:10 +0200 +Subject: [PATCH 154/160] RESPONDER_COMMON: update certmaps in responders +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Make certificate mapping data available to the responders. + +Related to https://pagure.io/SSSD/sssd/issue/3395 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 749963195393efa3a4f9b168dd02fbcc68976ba3) +--- + src/confdb/confdb.h | 3 +++ + src/responder/common/responder_get_domains.c | 23 +++++++++++++++++++++++ + 2 files changed, 26 insertions(+) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 8719c239362b371fcdb1b78956bcddde871f141b..797353141edcccbf3341d161ca598c99492e54fe 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -351,6 +351,9 @@ struct sss_domain_info { + char *forest; + struct sss_domain_info *forest_root; + const char **upn_suffixes; ++ ++ struct certmap_info **certmaps; ++ bool user_name_hint; + }; + + /** +diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c +index 8c90b7773e248e1dd6d846c5050e1931fc50c786..155631676d9449f69865919e1b74ee9399607c27 100644 +--- a/src/responder/common/responder_get_domains.c ++++ b/src/responder/common/responder_get_domains.c +@@ -224,6 +224,26 @@ immediately: + return req; + } + ++static void sss_resp_update_certmaps(struct resp_ctx *rctx) ++{ ++ int ret; ++ struct certmap_info **certmaps; ++ bool user_name_hint; ++ struct sss_domain_info *dom; ++ ++ for (dom = rctx->domains; dom != NULL; dom = dom->next) { ++ ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint); ++ if (ret == EOK) { ++ dom->user_name_hint = user_name_hint; ++ talloc_free(dom->certmaps); ++ dom->certmaps = certmaps; ++ } else { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_get_certmap failed for domain [%s].\n", dom->name); ++ } ++ } ++} ++ + static void + sss_dp_get_domains_process(struct tevent_req *subreq) + { +@@ -267,6 +287,9 @@ sss_dp_get_domains_process(struct tevent_req *subreq) + ret, sss_strerror(ret)); + goto fail; + } ++ ++ sss_resp_update_certmaps(state->rctx); ++ + tevent_req_done(req); + return; + } +-- +2.9.4 + diff --git a/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch b/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch new file mode 100644 index 0000000..dc6a41c --- /dev/null +++ b/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch @@ -0,0 +1,43 @@ +From 7487682e505735f2143ccecfc5e7e0fc2dac37f2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 May 2017 15:28:20 +0200 +Subject: [PATCH 155/160] tests: fix test_pam_preauth_cert_no_logon_name() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently a name is provided for test_pam_preauth_cert_no_logon_name() +so it is not a no-logon-name test. This patch removes the name and adds +the now missing mocked reply manually. + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 89ff140d7ab92fce52d6730a7d27c8d73c7d9e4a) +--- + src/tests/cmocka/test_pam_srv.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index 4d351a3707d2a49604595b728fff7705560c871a..35afbdd81d004236885ee80914771ccb4b8acff4 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -1873,10 +1873,14 @@ void test_pam_preauth_cert_no_logon_name(void **state) + * Since there is a matching user the upcoming lookup by name will find + * the user entry. But since we force the lookup by name to go to the + * backend to make sure the group-membership data is up to date the +- * backend response has to be mocked twice and the second argument of +- * mock_input_pam_cert cannot be NULL but must match the user name. */ +- mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, ++ * backend response has to be mocked twice. ++ * Additionally sss_parse_inp_recv() must be mocked because the cache ++ * request will be done with the username found by the certificate ++ * lookup. */ ++ mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, + test_lookup_by_cert_cb, TEST_TOKEN_CERT, false); ++ mock_account_recv_simple(); ++ mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); +-- +2.9.4 + diff --git a/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch b/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch new file mode 100644 index 0000000..94c6f8e --- /dev/null +++ b/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch @@ -0,0 +1,258 @@ +From 19cb2e2d826dc4e3c938c5a6b51a03338e80fa9e Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 May 2017 16:01:26 +0200 +Subject: [PATCH 156/160] pam_sss: add support for SSS_PAM_CERT_INFO_WITH_HINT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The new response type SSS_PAM_CERT_INFO_WITH_HINT is equivalent to +SSS_PAM_CERT_INFO but tells pam_sss to prompt for an option user name as +well. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3395 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit a192a1d72e92dae3e71e062b333e51a5095a0395) +--- + src/sss_client/pam_message.h | 1 + + src/sss_client/pam_sss.c | 129 ++++++++++++++++++++++++++++++++++++++----- + src/sss_client/sss_cli.h | 11 +++- + 3 files changed, 127 insertions(+), 14 deletions(-) + +diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h +index 3f4a770ac08ee416ead2f215ab873e8eb277c9eb..f215392f6879f01a0ca12abc8807bac5fc1f1cbb 100644 +--- a/src/sss_client/pam_message.h ++++ b/src/sss_client/pam_message.h +@@ -63,6 +63,7 @@ struct pam_items { + char *token_name; + char *module_name; + char *key_id; ++ bool user_name_hint; + }; + + int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer); +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index db0dcb9de7b893850bcea96a9cdf76dc0b36dcee..1c06079967e3d9076d537c3de8aba93e13f76d09 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -982,6 +982,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + + break; + case SSS_PAM_CERT_INFO: ++ case SSS_PAM_CERT_INFO_WITH_HINT: + if (buf[p + (len - 1)] != '\0') { + D(("cert info does not end with \\0.")); + break; +@@ -994,7 +995,19 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + break; + } + +- if (pi->pam_user == NULL || *(pi->pam_user) == '\0') { ++ if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') { ++ D(("Invalid CERT message")); ++ break; ++ } ++ ++ if (type == SSS_PAM_CERT_INFO_WITH_HINT) { ++ pi->user_name_hint = true; ++ } else { ++ pi->user_name_hint = false; ++ } ++ ++ if ((pi->pam_user == NULL || *(pi->pam_user) == '\0') ++ && pi->cert_user != '\0') { + ret = pam_set_item(pamh, PAM_USER, pi->cert_user); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_USER during " +@@ -1469,7 +1482,7 @@ done: + return ret; + } + +-#define SC_PROMPT_FMT "PIN for %s for user %s" ++#define SC_PROMPT_FMT "PIN for %s" + + static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi) + { +@@ -1478,32 +1491,108 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi) + char *prompt; + size_t size; + size_t needed_size; ++ const struct pam_conv *conv; ++ const struct pam_message *mesg[2] = { NULL, NULL }; ++ struct pam_message m[2] = { { 0 }, { 0 } }; ++ struct pam_response *resp = NULL; + +- if (pi->token_name == NULL || *pi->token_name == '\0' +- || pi->cert_user == NULL || *pi->cert_user == '\0') { ++ if (pi->token_name == NULL || *pi->token_name == '\0') { + return EINVAL; + } + +- size = sizeof(SC_PROMPT_FMT) + strlen(pi->token_name) + +- strlen(pi->cert_user); ++ size = sizeof(SC_PROMPT_FMT) + strlen(pi->token_name); + prompt = malloc(size); + if (prompt == NULL) { + D(("malloc failed.")); + return ENOMEM; + } + +- ret = snprintf(prompt, size, SC_PROMPT_FMT, pi->token_name, pi->cert_user); ++ ret = snprintf(prompt, size, SC_PROMPT_FMT, pi->token_name); + if (ret < 0 || ret >= size) { + D(("snprintf failed.")); + free(prompt); + return EFAULT; + } + +- ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, &answer); +- free(prompt); +- if (ret != PAM_SUCCESS) { +- D(("do_pam_conversation failed.")); +- return ret; ++ if (pi->user_name_hint) { ++ ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv); ++ if (ret != PAM_SUCCESS) { ++ return ret; ++ } ++ if (conv == NULL || conv->conv == NULL) { ++ logger(pamh, LOG_ERR, "No conversation function"); ++ return PAM_SYSTEM_ERR; ++ } ++ ++ m[0].msg_style = PAM_PROMPT_ECHO_OFF; ++ m[0].msg = prompt; ++ m[1].msg_style = PAM_PROMPT_ECHO_ON; ++ m[1].msg = "User name hint: "; ++ ++ mesg[0] = (const struct pam_message *)m; ++ /* The following assignment might look a bit odd but is recommended in the ++ * pam_conv man page to make sure that the second argument of the PAM ++ * conversation function can be interpreted in two different ways. ++ * Basically it is important that both the actual struct pam_message and ++ * the pointers to the struct pam_message are arrays. Since the assignment ++ * makes clear that mesg[] and (*mesg)[] are arrays it should be kept this ++ * way and not be replaced by other equivalent assignments. */ ++ mesg[1] = &((*mesg)[1]); ++ ++ ret = conv->conv(2, mesg, &resp, conv->appdata_ptr); ++ if (ret != PAM_SUCCESS) { ++ D(("Conversation failure: %s.", pam_strerror(pamh, ret))); ++ return ret; ++ } ++ ++ if (resp == NULL) { ++ D(("response expected, but resp==NULL")); ++ return PAM_SYSTEM_ERR; ++ } ++ ++ if (resp[0].resp == NULL || *(resp[0].resp) == '\0') { ++ D(("Missing PIN.")); ++ ret = PAM_CRED_INSUFFICIENT; ++ goto done; ++ } ++ ++ answer = strndup(resp[0].resp, MAX_AUTHTOK_SIZE); ++ _pam_overwrite((void *)resp[0].resp); ++ free(resp[0].resp); ++ resp[0].resp = NULL; ++ if (answer == NULL) { ++ D(("strndup failed")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ ++ if (resp[1].resp != NULL && *(resp[1].resp) != '\0') { ++ ret = pam_set_item(pamh, PAM_USER, resp[1].resp); ++ free(resp[1].resp); ++ resp[1].resp = NULL; ++ if (ret != PAM_SUCCESS) { ++ D(("Failed to set PAM_USER with user name hint [%s]", ++ pam_strerror(pamh, ret))); ++ goto done; ++ } ++ ++ ret = pam_get_item(pamh, PAM_USER, (const void **)&(pi->pam_user)); ++ if (ret != PAM_SUCCESS) { ++ D(("Failed to get PAM_USER with user name hint [%s]", ++ pam_strerror(pamh, ret))); ++ goto done; ++ } ++ ++ pi->pam_user_size = strlen(pi->pam_user) + 1; ++ } ++ } else { ++ ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, ++ &answer); ++ free(prompt); ++ if (ret != PAM_SUCCESS) { ++ D(("do_pam_conversation failed.")); ++ return ret; ++ } + } + + if (answer == NULL) { +@@ -1552,6 +1641,20 @@ done: + free(answer); + answer=NULL; + ++ if (resp != NULL) { ++ if (resp[0].resp != NULL) { ++ _pam_overwrite((void *)resp[0].resp); ++ free(resp[0].resp); ++ } ++ if (resp[1].resp != NULL) { ++ _pam_overwrite((void *)resp[1].resp); ++ free(resp[1].resp); ++ } ++ ++ free(resp); ++ resp = NULL; ++ } ++ + return ret; + } + +@@ -1680,7 +1783,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, + ret = prompt_2fa(pamh, pi, _("First Factor: "), + _("Second Factor: ")); + } +- } else if (pi->cert_user != NULL) { ++ } else if (pi->token_name != NULL && *(pi->token_name) != '\0') { + ret = prompt_sc_pin(pamh, pi); + } else { + ret = prompt_password(pamh, pi, _("Password: ")); +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index 59fee7a4eceb2c185e156e812af7f2f4c6b2a0dd..d4198407f2f86c6594aee6a2a43775e429692df0 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -427,7 +427,13 @@ enum response_type { + * @param Three zero terminated strings, if one of the + * strings is missing the message will contain only + * an empty string (\0) for that component. */ +- SSS_PAM_CERT_INFO, ++ SSS_PAM_CERT_INFO, /**< A message indicating that Smartcard/certificate ++ * based authentication is available and contains ++ * details about the found Smartcard. ++ * @param user name, zero terminated ++ * @param token name, zero terminated ++ * @param PKCS#11 module name, zero terminated ++ * @param key id, zero terminated */ + SSS_OTP, /**< Indicates that the autotok was a OTP, so don't + * cache it. There is no message. + * @param None. */ +@@ -442,6 +448,9 @@ enum response_type { + * be used together with other prompting options + * to determine the type of prompting. + * @param None. */ ++ SSS_PAM_CERT_INFO_WITH_HINT, /**< Same as SSS_PAM_CERT_INFO but user name ++ * might be missing and should be prompted ++ * for. */ + }; + + /** +-- +2.9.4 + diff --git a/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch b/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch new file mode 100644 index 0000000..a9a73fd --- /dev/null +++ b/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch @@ -0,0 +1,104 @@ +From c5c6ba2546d350a7a01a9f44bb5df9c6652a1e06 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 May 2017 16:02:36 +0200 +Subject: [PATCH 157/160] add_pam_cert_response: add support for + SSS_PAM_CERT_INFO_WITH_HINT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Related to https://pagure.io/SSSD/sssd/issue/3395 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 6073cfc40747cd6d3142f0f98b880fc390dd7aad) +--- + src/responder/pam/pamsrv.h | 2 +- + src/responder/pam/pamsrv_cmd.c | 3 ++- + src/responder/pam/pamsrv_p11.c | 21 +++++++++++++++------ + 3 files changed, 18 insertions(+), 8 deletions(-) + +diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h +index b569748fe2a2005cee5df34bef55e803175492a9..57a37b72594f030995f5e22255eb7a8fcd63d10e 100644 +--- a/src/responder/pam/pamsrv.h ++++ b/src/responder/pam/pamsrv.h +@@ -101,7 +101,7 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + + errno_t add_pam_cert_response(struct pam_data *pd, const char *user, + const char *token_name, const char *module_name, +- const char *key_id); ++ const char *key_id, enum response_type type); + + bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd); + +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 36dba37964b71153435b4df5d5328de4361926e6..080cfafa709d63542fbf57d26fab11f0a367dea7 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1846,7 +1846,8 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) + ret = add_pam_cert_response(preq->pd, cert_user, + preq->token_name, + preq->module_name, +- preq->key_id); ++ preq->key_id, ++ SSS_PAM_CERT_INFO); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n"); + preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; +diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c +index 365300b9075983b603a6f9e91ba6f8f21706388f..4dce43800c3c6b026c545df35c846269cbb49610 100644 +--- a/src/responder/pam/pamsrv_p11.c ++++ b/src/responder/pam/pamsrv_p11.c +@@ -580,7 +580,7 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + + errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, + const char *token_name, const char *module_name, +- const char *key_id) ++ const char *key_id, enum response_type type) + { + uint8_t *msg = NULL; + char *env = NULL; +@@ -590,14 +590,23 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, + size_t module_len; + size_t key_id_len; + int ret; ++ const char *username = ""; + +- if (sysdb_username == NULL || token_name == NULL || module_name == NULL +- || key_id == NULL) { ++ if (type != SSS_PAM_CERT_INFO && type != SSS_PAM_CERT_INFO_WITH_HINT) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid response type [%d].\n", type); ++ return EINVAL; ++ } ++ ++ if ((type == SSS_PAM_CERT_INFO && sysdb_username == NULL) ++ || token_name == NULL || module_name == NULL || key_id == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n"); + return EINVAL; + } + +- user_len = strlen(sysdb_username) + 1; ++ if (sysdb_username != NULL) { ++ username = sysdb_username; ++ } ++ user_len = strlen(username) + 1; + slot_len = strlen(token_name) + 1; + module_len = strlen(module_name) + 1; + key_id_len = strlen(key_id) + 1; +@@ -616,12 +625,12 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, + * re_expression config option was set in a way that user@domain cannot be + * handled anymore some more logic has to be added here. But for the time + * being I think using sysdb_username is fine. */ +- memcpy(msg, sysdb_username, user_len); ++ memcpy(msg, username, user_len); + memcpy(msg + user_len, token_name, slot_len); + memcpy(msg + user_len + slot_len, module_name, module_len); + memcpy(msg + user_len + slot_len + module_len, key_id, key_id_len); + +- ret = pam_add_response(pd, SSS_PAM_CERT_INFO, msg_len, msg); ++ ret = pam_add_response(pd, type, msg_len, msg); + talloc_free(msg); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, +-- +2.9.4 + diff --git a/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch b/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch new file mode 100644 index 0000000..a444dcf --- /dev/null +++ b/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch @@ -0,0 +1,335 @@ +From a531a785f57be7ae228ca04a7af606debd66eeb1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 3 May 2017 16:30:12 +0200 +Subject: [PATCH 158/160] PAM: send user name hint response when needed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the PAM client didn't send a user name and promtusername is enable +the PAM responder will tell pam_sss to ask for an optional user name as +well. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3395 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 32474fa2f0a6dc09386bab405fc3461cb3dd12ac) +--- + src/responder/pam/pamsrv_cmd.c | 72 ++++++++++------ + src/tests/cmocka/test_pam_srv.c | 180 +++++++++++++++++++++++++++++----------- + 2 files changed, 177 insertions(+), 75 deletions(-) + +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 080cfafa709d63542fbf57d26fab11f0a367dea7..49a05657e03feef564d6196029da4cacc2ab8eaf 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1414,7 +1414,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + struct cache_req_result **results; + struct pam_auth_req *preq = tevent_req_callback_data(req, + struct pam_auth_req); +- const char *cert_user; ++ const char *cert_user = NULL; + + ret = cache_req_recv(preq, req, &results); + talloc_zfree(req); +@@ -1439,35 +1439,55 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + goto done; + } + +- if (preq->cert_user_objs->count != 1) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "More than one user mapped to certificate.\n"); +- /* TODO: send pam response to ask for a user name */ +- ret = ERR_NO_CREDS; +- goto done; +- } +- cert_user = ldb_msg_find_attr_as_string( ++ if (preq->cert_user_objs->count == 1) { ++ cert_user = ldb_msg_find_attr_as_string( + preq->cert_user_objs->msgs[0], + SYSDB_NAME, NULL); ++ if (cert_user == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Certificate user object has not name.\n"); ++ ret = ENOENT; ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_FUNC_DATA, ++ "Found certificate user [%s].\n", cert_user); ++ ++ ret = sss_parse_name_for_domains(preq->pd, ++ preq->cctx->rctx->domains, ++ preq->cctx->rctx->default_domain, ++ cert_user, ++ &preq->pd->domain, ++ &preq->pd->user); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_parse_name_for_domains failed.\n"); ++ goto done; ++ } ++ } ++ ++ if (preq->cctx->rctx->domains->user_name_hint) { ++ ret = add_pam_cert_response(preq->pd, cert_user, ++ preq->token_name, ++ preq->module_name, ++ preq->key_id, ++ SSS_PAM_CERT_INFO_WITH_HINT); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n"); ++ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; ++ } ++ ret = EOK; ++ preq->pd->pam_status = PAM_SUCCESS; ++ pam_reply(preq); ++ goto done; ++ } ++ ++ /* Without user name hints the certificate must map to single user ++ * if no login name was given */ + if (cert_user == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "Certificate user object has not name.\n"); +- ret = ENOENT; +- goto done; +- } +- +- DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n", +- cert_user); +- +- ret = sss_parse_name_for_domains(preq->pd, +- preq->cctx->rctx->domains, +- preq->cctx->rctx->default_domain, +- cert_user, +- &preq->pd->domain, +- &preq->pd->user); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "sss_parse_name_for_domains failed.\n"); ++ "More than one user mapped to certificate.\n"); ++ ret = ERR_NO_CREDS; + goto done; + } + +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index 35afbdd81d004236885ee80914771ccb4b8acff4..0f92f05417025e41a702127099d1d01e269412dc 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -747,57 +747,83 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body, + return EOK; + } + ++static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, ++ enum response_type type, const char *name) ++{ ++ size_t rp = 0; ++ uint32_t val; ++ ++ assert_int_equal(status, 0); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, pam_test_ctx->exp_pam_status); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ if (name == NULL || *name == '\0') { ++ assert_int_equal(val, 1); ++ } else { ++ assert_int_equal(val, 2); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, SSS_PAM_DOMAIN_NAME); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, 9); ++ ++ assert_int_equal(*(body + rp + val - 1), 0); ++ assert_string_equal(body + rp, TEST_DOM_NAME); ++ rp += val; ++ } ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, type); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, (strlen(name) + 1 ++ + sizeof(TEST_TOKEN_NAME) ++ + sizeof(TEST_MODULE_NAME) ++ + sizeof(TEST_KEY_ID))); ++ ++ assert_int_equal(*(body + rp + strlen(name)), 0); ++ assert_string_equal(body + rp, name); ++ rp += strlen(name) + 1; ++ ++ assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0); ++ assert_string_equal(body + rp, TEST_TOKEN_NAME); ++ rp += sizeof(TEST_TOKEN_NAME); ++ ++ assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0); ++ assert_string_equal(body + rp, TEST_MODULE_NAME); ++ rp += sizeof(TEST_MODULE_NAME); ++ ++ assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0); ++ assert_string_equal(body + rp, TEST_KEY_ID); ++ rp += sizeof(TEST_KEY_ID); ++ ++ assert_int_equal(rp, blen); ++ ++ return EOK; ++} ++ + static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) + { +- size_t rp = 0; +- uint32_t val; +- +- assert_int_equal(status, 0); +- +- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); +- assert_int_equal(val, pam_test_ctx->exp_pam_status); +- +- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); +- assert_int_equal(val, 2); +- +- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); +- assert_int_equal(val, SSS_PAM_DOMAIN_NAME); +- +- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); +- assert_int_equal(val, 9); +- +- assert_int_equal(*(body + rp + val - 1), 0); +- assert_string_equal(body + rp, TEST_DOM_NAME); +- rp += val; +- +- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); +- assert_int_equal(val, SSS_PAM_CERT_INFO); +- +- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); +- assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME) +- + sizeof(TEST_TOKEN_NAME) +- + sizeof(TEST_MODULE_NAME) +- + sizeof(TEST_KEY_ID))); +- +- assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0); +- assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME); +- rp += sizeof("pamuser@"TEST_DOM_NAME); +- +- assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0); +- assert_string_equal(body + rp, TEST_TOKEN_NAME); +- rp += sizeof(TEST_TOKEN_NAME); +- +- assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0); +- assert_string_equal(body + rp, TEST_MODULE_NAME); +- rp += sizeof(TEST_MODULE_NAME); +- +- assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0); +- assert_string_equal(body + rp, TEST_KEY_ID); +- rp += sizeof(TEST_KEY_ID); +- +- assert_int_equal(rp, blen); +- +- return EOK; ++ return test_pam_cert_check_ex(status, body, blen, ++ SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME); ++} ++ ++static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body, ++ size_t blen) ++{ ++ return test_pam_cert_check_ex(status, body, blen, ++ SSS_PAM_CERT_INFO_WITH_HINT, ++ "pamuser@"TEST_DOM_NAME); ++} ++ ++static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body, ++ size_t blen) ++{ ++ return test_pam_cert_check_ex(status, body, blen, ++ SSS_PAM_CERT_INFO_WITH_HINT, ""); + } + + static int test_pam_offline_chauthtok_check(uint32_t status, +@@ -1895,6 +1921,33 @@ void test_pam_preauth_cert_no_logon_name(void **state) + assert_int_equal(ret, EOK); + } + ++void test_pam_preauth_cert_no_logon_name_with_hint(void **state) ++{ ++ int ret; ++ ++ set_cert_auth_param(pam_test_ctx->pctx, NSS_DB); ++ pam_test_ctx->rctx->domains->user_name_hint = true; ++ ++ /* If no logon name is given the user is looked by certificate first. ++ * Since user name hint is enabled we do not have to search the user ++ * during pre-auth and there is no need for an extra mocked response as in ++ * test_pam_preauth_cert_no_logon_name. */ ++ mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, ++ test_lookup_by_cert_cb, TEST_TOKEN_CERT, false); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_cert_check_with_hint); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ + void test_pam_preauth_cert_no_logon_name_double_cert(void **state) + { + int ret; +@@ -1917,6 +1970,29 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state) + assert_int_equal(ret, EOK); + } + ++void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state) ++{ ++ int ret; ++ ++ set_cert_auth_param(pam_test_ctx->pctx, NSS_DB); ++ pam_test_ctx->rctx->domains->user_name_hint = true; ++ ++ mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, ++ test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_cert_check_with_hint_no_user); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ + void test_pam_preauth_no_cert_no_logon_name(void **state) + { + int ret; +@@ -2426,8 +2502,14 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_no_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( ++ test_pam_preauth_cert_no_logon_name_with_hint, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_double_cert, + pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_preauth_cert_no_logon_name_double_cert_with_hint, ++ pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_no_cert_no_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( +-- +2.9.4 + diff --git a/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch b/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch new file mode 100644 index 0000000..290bab5 --- /dev/null +++ b/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch @@ -0,0 +1,105 @@ +From b435e510fb06af4e8f3cffd3730f43a6aff165fa Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 8 May 2017 17:30:06 +0200 +Subject: [PATCH 159/160] sysdb: sysdb_get_certmap() allow empty certmap +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Since sysdb_get_certmap() returns the user name hint information as well +it should return a result even if there are no certmaps. + +Related to https://pagure.io/SSSD/sssd/issue/3395 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit ee7e72a65d323636600ffda271d5b5c4ddbc78b1) +--- + src/db/sysdb_certmap.c | 13 ++++++++----- + src/tests/cmocka/test_sysdb_certmap.c | 9 +++++---- + 2 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c +index 4917796b11c3967b4d147ebee7c7e83f09b872ce..2d89c08a07be6e8eaf853d6c50b206c5c53d5a37 100644 +--- a/src/db/sysdb_certmap.c ++++ b/src/db/sysdb_certmap.c +@@ -269,7 +269,7 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + size_t d; + struct ldb_dn *container_dn = NULL; + int ret; +- struct certmap_info **maps; ++ struct certmap_info **maps = NULL; + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_result *res; + const char *tmp_str; +@@ -320,7 +320,7 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + + if (res->count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "No certificate maps found.\n"); +- ret = ENOENT; ++ ret = EOK; + goto done; + } + +@@ -377,7 +377,7 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + SYSDB_CERTMAP_PRIORITY, + (uint64_t) -1); + if (tmp_uint != (uint64_t) -1) { +- if (tmp_uint >= UINT32_MAX) { ++ if (tmp_uint > UINT32_MAX) { + DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n", + (unsigned long) tmp_uint); + ret = EINVAL; +@@ -414,11 +414,14 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + } + } + +- *certmaps = talloc_steal(mem_ctx, maps); +- *user_name_hint = hint; + ret = EOK; + + done: ++ if (ret == EOK) { ++ *certmaps = talloc_steal(mem_ctx, maps); ++ *user_name_hint = hint; ++ } ++ + talloc_free(tmp_ctx); + + return ret; +diff --git a/src/tests/cmocka/test_sysdb_certmap.c b/src/tests/cmocka/test_sysdb_certmap.c +index fb07165561779226935f436c308c85abfc305635..72edf5f53fd6d23d7279eaa496b3e798c06cb903 100644 +--- a/src/tests/cmocka/test_sysdb_certmap.c ++++ b/src/tests/cmocka/test_sysdb_certmap.c +@@ -88,8 +88,8 @@ static void test_sysdb_get_certmap_not_exists(void **state) + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); +- assert_int_equal(ret, ENOENT); +- ++ assert_int_equal(ret, EOK); ++ assert_null(certmap); + } + + static void check_certmap(struct certmap_info *m, struct certmap_info *r, +@@ -134,7 +134,7 @@ static void test_sysdb_update_certmap(void **state) + int ret; + const char *domains[] = { "dom1.test", "dom2.test", "dom3.test", NULL }; + struct certmap_info map_a = { discard_const("map_a"), 11, discard_const("abc"), discard_const("def"), NULL }; +- struct certmap_info map_b = { discard_const("map_b"), 22, discard_const("abc"), NULL, domains }; ++ struct certmap_info map_b = { discard_const("map_b"), UINT_MAX, discard_const("abc"), NULL, domains }; + struct certmap_info *certmap_empty[] = { NULL }; + struct certmap_info *certmap_a[] = { &map_a, NULL }; + struct certmap_info *certmap_b[] = { &map_b, NULL }; +@@ -152,7 +152,8 @@ static void test_sysdb_update_certmap(void **state) + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); +- assert_int_equal(ret, ENOENT); ++ assert_int_equal(ret, EOK); ++ assert_null(certmap); + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_a, false); + assert_int_equal(ret, EOK); +-- +2.9.4 + diff --git a/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch b/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch new file mode 100644 index 0000000..6680a3b --- /dev/null +++ b/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch @@ -0,0 +1,53 @@ +From 6edf41eba3cec8aa40dffaf639cd5c7756db310e Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 10 May 2017 17:13:48 +0200 +Subject: [PATCH 160/160] sssctl: show user name used for authentication in + user-checks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Since there are cases where the user name is not entered directly but +determined by other means the user-checks should show the name of the +user used for authentication. + +Related to https://pagure.io/SSSD/sssd/issue/3395 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit b130adaa3934d0531aca0f32961ab8b4cc720820) +--- + src/tools/sssctl/sssctl_user_checks.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/tools/sssctl/sssctl_user_checks.c b/src/tools/sssctl/sssctl_user_checks.c +index 7c7b564bd29100382c9bbef7a3131c379e9aa97e..d5cd8a1b42e84aa96df95ed39905c864a38212b7 100644 +--- a/src/tools/sssctl/sssctl_user_checks.c ++++ b/src/tools/sssctl/sssctl_user_checks.c +@@ -200,6 +200,8 @@ errno_t sssctl_user_checks(struct sss_cmdline *cmdline, + const char *action = DEFAULT_ACTION; + const char *service = DEFAULT_SERVICE; + int ret; ++ int pret; ++ const char *pam_user = NULL; + size_t c; + char **pam_env; + +@@ -246,7 +248,14 @@ errno_t sssctl_user_checks(struct sss_cmdline *cmdline, + if ( strncmp(action, "auth", 4)== 0 ) { + fprintf(stdout, _("testing pam_authenticate\n\n")); + ret = pam_authenticate(pamh, 0); +- fprintf(stderr, _("pam_authenticate: %s\n\n"), pam_strerror(pamh, ret)); ++ pret = pam_get_item(pamh, PAM_USER, (const void **) &pam_user); ++ if (pret != PAM_SUCCESS) { ++ fprintf(stderr, _("pam_get_item failed: %s\n"), pam_strerror(pamh, ++ pret)); ++ pam_user = "- not available -"; ++ } ++ fprintf(stderr, _("pam_authenticate for user [%s]: %s\n\n"), pam_user, ++ pam_strerror(pamh, ret)); + } else if ( strncmp(action, "chau", 4)== 0 ) { + fprintf(stdout, _("testing pam_chauthtok\n\n")); + ret = pam_chauthtok(pamh, 0); +-- +2.9.4 + diff --git a/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch b/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch new file mode 100644 index 0000000..cc83129 --- /dev/null +++ b/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch @@ -0,0 +1,267 @@ +From be1f9a082eb28b3346135cbe399f7f909c8a50ce Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 24 May 2017 17:34:55 +0200 +Subject: [PATCH 161/166] RESP: Provide a reusable request to fully resolve + incomplete groups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +After initgroups, the group objects might not be complete, but just +stubs that contain the SID and the GID. If the caller needs to know the +group names as well, this request allows them to iterate over the list +of the groups and resolve them one-by-one. + +Reviewed-by: Pavel Březina +--- + src/responder/common/responder.h | 14 +++ + src/responder/common/responder_utils.c | 206 +++++++++++++++++++++++++++++++++ + 2 files changed, 220 insertions(+) + +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index dfe1ec455e355de263c3550306e53fea3ada85df..c09ecd4931c9e197fbdfb7835eb72f49cc6f6d3f 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -414,4 +414,18 @@ int sized_domain_name(TALLOC_CTX *mem_ctx, + const char *member_name, + struct sized_string **_name); + ++/* Given a ldb_result structure that contains a result of sysdb_initgroups ++ * where some groups might be just 'stubs' that don't have a name, but only ++ * a SID and a GID, resolve those incomplete groups into full group objects ++ */ ++struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct resp_ctx *rctx, ++ struct sss_domain_info *dom, ++ struct ldb_result *initgr_res); ++ ++int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct ldb_result **_initgr_named_res); ++ + #endif /* __SSS_RESPONDER_H__ */ +diff --git a/src/responder/common/responder_utils.c b/src/responder/common/responder_utils.c +index b02212dfd87c2b7c2ca6108d46f939447f0eaa25..7f5c0573087e9c6c885ae158d0677994fd538e2a 100644 +--- a/src/responder/common/responder_utils.c ++++ b/src/responder/common/responder_utils.c +@@ -23,6 +23,7 @@ + #include + + #include "responder/common/responder.h" ++#include "responder/common/cache_req/cache_req.h" + #include "util/util.h" + + static inline bool +@@ -193,3 +194,208 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx, + talloc_free(tmp_ctx); + return name; + } ++ ++struct resp_resolve_group_names_state { ++ struct tevent_context *ev; ++ struct resp_ctx *rctx; ++ struct sss_domain_info *dom; ++ struct ldb_result *initgr_res; ++ ++ bool needs_refresh; ++ unsigned int group_iter; ++ ++ struct ldb_result *initgr_named_res; ++}; ++ ++static void resp_resolve_group_done(struct tevent_req *subreq); ++static errno_t resp_resolve_group_next(struct tevent_req *req); ++static errno_t resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state); ++ ++struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct resp_ctx *rctx, ++ struct sss_domain_info *dom, ++ struct ldb_result *initgr_res) ++{ ++ struct resp_resolve_group_names_state *state; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct resp_resolve_group_names_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ state->ev = ev; ++ state->rctx = rctx; ++ state->dom = dom; ++ state->initgr_res = initgr_res; ++ ++ ret = resp_resolve_group_next(req); ++ if (ret == EOK) { ++ goto immediate; ++ } else if (ret != EAGAIN) { ++ goto immediate; ++ } ++ ++ return req; ++ ++immediate: ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ tevent_req_post(req, ev); ++ return req; ++} ++ ++static bool ++resp_resolve_group_needs_refresh(struct resp_resolve_group_names_state *state) ++{ ++ /* Refresh groups that have a non-zero GID, ++ * but are marked as non-POSIX ++ */ ++ bool is_posix; ++ uint64_t gid; ++ struct ldb_message *group_msg; ++ ++ group_msg = state->initgr_res->msgs[state->group_iter]; ++ ++ is_posix = ldb_msg_find_attr_as_bool(group_msg, SYSDB_POSIX, false); ++ gid = ldb_msg_find_attr_as_uint64(group_msg, SYSDB_GIDNUM, 0); ++ ++ if (is_posix == false && gid != 0) { ++ return true; ++ } ++ ++ return false; ++} ++ ++static errno_t resp_resolve_group_next(struct tevent_req *req) ++{ ++ struct cache_req_data *data; ++ uint64_t gid; ++ struct tevent_req *subreq; ++ struct resp_resolve_group_names_state *state; ++ ++ state = tevent_req_data(req, struct resp_resolve_group_names_state); ++ ++ while (state->group_iter < state->initgr_res->count ++ && !resp_resolve_group_needs_refresh(state)) { ++ state->group_iter++; ++ } ++ ++ if (state->group_iter >= state->initgr_res->count) { ++ /* All groups were refreshed */ ++ return EOK; ++ } ++ ++ /* Fire a request */ ++ gid = ldb_msg_find_attr_as_uint64(state->initgr_res->msgs[state->group_iter], ++ SYSDB_GIDNUM, 0); ++ if (gid == 0) { ++ return EINVAL; ++ } ++ ++ data = cache_req_data_id_attrs(state, CACHE_REQ_GROUP_BY_ID, gid, NULL); ++ if (data == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n"); ++ return ENOMEM; ++ } ++ ++ subreq = cache_req_send(state, ++ state->ev, ++ state->rctx, ++ state->rctx->ncache, ++ 0, ++ CACHE_REQ_ANY_DOM, ++ NULL, ++ data); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n"); ++ return ENOMEM; ++ } ++ ++ tevent_req_set_callback(subreq, resp_resolve_group_done, req); ++ return EAGAIN; ++} ++ ++static void resp_resolve_group_done(struct tevent_req *subreq) ++{ ++ struct resp_resolve_group_names_state *state; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct resp_resolve_group_names_state); ++ ++ ret = cache_req_single_domain_recv(state, subreq, NULL); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh group\n"); ++ /* Try to refresh the others on error */ ++ } ++ ++ state->group_iter++; ++ state->needs_refresh = true; ++ ++ ret = resp_resolve_group_next(req); ++ if (ret == EOK) { ++ ret = resp_resolve_group_reread_names(state); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ DEBUG(SSSDBG_TRACE_FUNC, "All groups are refreshed, done\n"); ++ tevent_req_done(req); ++ return; ++ } else if (ret != EAGAIN) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ /* Continue refreshing.. */ ++} ++ ++static errno_t ++resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state) ++{ ++ errno_t ret; ++ const char *username; ++ ++ /* re-read reply in case any groups were renamed */ ++ /* msgs[0] is the user entry */ ++ username = sss_view_ldb_msg_find_attr_as_string(state->dom, ++ state->initgr_res->msgs[0], ++ SYSDB_NAME, ++ NULL); ++ if (username == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n"); ++ return EINVAL; ++ } ++ ++ ret = sysdb_initgroups_with_views(state, ++ state->dom, ++ username, ++ &state->initgr_named_res); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Cannot re-read the group names\n"); ++ return ret; ++ } ++ ++ return EOK; ++} ++ ++int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct ldb_result **_initgr_named_res) ++{ ++ struct resp_resolve_group_names_state *state = NULL; ++ state = tevent_req_data(req, struct resp_resolve_group_names_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *_initgr_named_res = talloc_steal(mem_ctx, state->initgr_named_res); ++ return EOK; ++} +-- +2.9.4 + diff --git a/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch b/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch new file mode 100644 index 0000000..b6c6eaa --- /dev/null +++ b/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch @@ -0,0 +1,121 @@ +From f5bee70057370c72ed111b50937e3252e36ccefb Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 9 May 2017 12:21:32 +0200 +Subject: [PATCH 162/166] IFP: Only format the output name to the short version + before output +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The ifp_user_get_attr_done() request handler was reused for both +GetUserGroups and GetUserAttrs requests. Yet, it performed output +formatting of name and nameAlias. + +This is bad, because the output formatting should really be done only +during output. Also, it broke any post-processing of the returned +message which the request might do later. + +Reviewed-by: Pavel Březina +--- + src/responder/ifp/ifpsrv_cmd.c | 64 ++++++++++++------------------------------ + 1 file changed, 18 insertions(+), 46 deletions(-) + +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index e4d6c42ef35ef372472803d3d26b17d4181021a8..915f77e38e94c703f6c67e8d5fdcc59d189943be 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -181,26 +181,6 @@ static void ifp_user_get_attr_process(struct tevent_req *req) + } + + static errno_t +-ifp_user_get_attr_replace_space(TALLOC_CTX *mem_ctx, +- struct ldb_message_element *el, +- const char sub) +-{ +- int i; +- +- for (i = 0; i < el->num_values; i++) { +- el->values[i].data = (uint8_t *) sss_replace_space(mem_ctx, +- (const char *) el->values[i].data, +- sub); +- if (el->values[i].data == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed, skipping\n"); +- return ENOMEM; +- } +- } +- +- return EOK; +-} +- +-static errno_t + ifp_user_get_attr_handle_reply(struct sss_domain_info *domain, + struct ifp_req *ireq, + const char **attrs, +@@ -234,6 +214,24 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain, + } + + if (res->count > 0) { ++ ret = ifp_ldb_el_output_name(ireq->ifp_ctx->rctx, res->msgs[0], ++ SYSDB_NAME, domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert SYSDB_NAME to output format [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return sbus_request_finish(ireq->dbus_req, NULL); ++ } ++ ++ ret = ifp_ldb_el_output_name(ireq->ifp_ctx->rctx, res->msgs[0], ++ SYSDB_NAME_ALIAS, domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert SYSDB_NAME_ALIAS to output format [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return sbus_request_finish(ireq->dbus_req, NULL); ++ } ++ + for (ai = 0; attrs[ai]; ai++) { + el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]); + if (el == NULL || el->num_values == 0) { +@@ -243,18 +241,6 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain, + continue; + } + +- /* Normalize white space in user names */ +- if (ireq->ifp_ctx->rctx->override_space != '\0' && +- strcmp(attrs[ai], SYSDB_NAME) == 0) { +- ret = ifp_user_get_attr_replace_space(ireq, el, +- ireq->ifp_ctx->rctx->override_space); +- if (ret != EOK) { +- DEBUG(SSSDBG_MINOR_FAILURE, "Cannot normalize %s\n", +- attrs[ai]); +- continue; +- } +- } +- + ret = ifp_add_ldb_el_to_dict(&iter_dict, el); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, +@@ -575,20 +561,6 @@ static void ifp_user_get_attr_done(struct tevent_req *subreq) + } + } + +- ret = ifp_ldb_el_output_name(state->rctx, state->res->msgs[0], +- SYSDB_NAME, state->dom); +- if (ret != EOK) { +- tevent_req_error(req, ret); +- return; +- } +- +- ret = ifp_ldb_el_output_name(state->rctx, state->res->msgs[0], +- SYSDB_NAME_ALIAS, state->dom); +- if (ret != EOK) { +- tevent_req_error(req, ret); +- return; +- } +- + tevent_req_done(req); + } + +-- +2.9.4 + diff --git a/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch b/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch new file mode 100644 index 0000000..b33cbba --- /dev/null +++ b/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch @@ -0,0 +1,204 @@ +From 3891e94330a5df632a8db1a6f1d642cf2fa96579 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 24 May 2017 21:32:28 +0200 +Subject: [PATCH 163/166] IFP: Resolve group names from GIDs if required +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The AD provider only converts SIDs to GIDs during initgroups +to improve performance. But this is not sufficient for the +org.freedesktop.sssd.infopipe.GetUserGroups method, which needs to return +names. + +We need to resolve the GIDs to names ourselves in that method. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3392 + +Reviewed-by: Pavel Březina +--- + src/responder/ifp/ifpsrv_cmd.c | 115 +++++++++++++++++++++++++++++++---------- + 1 file changed, 89 insertions(+), 26 deletions(-) + +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index 915f77e38e94c703f6c67e8d5fdcc59d189943be..70728e1bb656fd032b7f1c240683e8aa3b91a726 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -259,7 +259,18 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain, + return sbus_request_finish(ireq->dbus_req, reply); + } + ++struct ifp_user_get_groups_state { ++ struct resp_ctx *rctx; ++ ++ struct ifp_attr_req *group_attr_req; ++ ++ struct ldb_result *res; ++ struct ldb_result *res_names; ++ struct sss_domain_info *dom; ++}; ++ + static void ifp_user_get_groups_process(struct tevent_req *req); ++static void ifp_user_get_groups_names_resolved(struct tevent_req *req); + static errno_t ifp_user_get_groups_reply(struct sss_domain_info *domain, + struct ifp_req *ireq, + struct ldb_result *res); +@@ -269,7 +280,7 @@ int ifp_user_get_groups(struct sbus_request *dbus_req, + { + struct ifp_req *ireq; + struct ifp_ctx *ifp_ctx; +- struct ifp_attr_req *group_req; ++ struct ifp_user_get_groups_state *state; + struct tevent_req *req; + errno_t ret; + +@@ -284,68 +295,120 @@ int ifp_user_get_groups(struct sbus_request *dbus_req, + return ifp_req_create_handle_failure(dbus_req, ret); + } + +- group_req = talloc_zero(ireq, struct ifp_attr_req); +- if (group_req == NULL) { ++ state = talloc_zero(ireq, struct ifp_user_get_groups_state); ++ if (state == NULL) { + return sbus_request_finish(dbus_req, NULL); + } +- group_req->ireq = ireq; +- group_req->name = arg_user; ++ state->rctx = ifp_ctx->rctx; + +- group_req->attrs = talloc_zero_array(group_req, const char *, 2); +- if (group_req->attrs == NULL) { ++ state->group_attr_req = talloc_zero(state, struct ifp_attr_req); ++ if (state->group_attr_req == NULL) { + return sbus_request_finish(dbus_req, NULL); + } ++ state->group_attr_req->ireq = ireq; ++ state->group_attr_req->name = arg_user; + +- group_req->attrs[0] = talloc_strdup(group_req->attrs, SYSDB_MEMBEROF); +- if (group_req->attrs[0] == NULL) { ++ state->group_attr_req->attrs = talloc_zero_array(state->group_attr_req, ++ const char *, 2); ++ if (state->group_attr_req->attrs == NULL) { ++ return sbus_request_finish(dbus_req, NULL); ++ } ++ ++ state->group_attr_req->attrs[0] = talloc_strdup(state->group_attr_req->attrs, ++ SYSDB_MEMBEROF); ++ if (state->group_attr_req->attrs[0] == NULL) { + return sbus_request_finish(dbus_req, NULL); + } + + DEBUG(SSSDBG_FUNC_DATA, + "Looking up groups of user [%s] on behalf of %"PRIi64"\n", +- group_req->name, group_req->ireq->dbus_req->client); ++ state->group_attr_req->name, ++ state->group_attr_req->ireq->dbus_req->client); + + req = ifp_user_get_attr_send(ireq, ifp_ctx->rctx, + ifp_ctx->rctx->ncache, SSS_DP_INITGROUPS, +- group_req->name, group_req->attrs); ++ state->group_attr_req->name, ++ state->group_attr_req->attrs); + if (req == NULL) { + return sbus_request_finish(dbus_req, NULL); + } +- tevent_req_set_callback(req, ifp_user_get_groups_process, group_req); ++ tevent_req_set_callback(req, ++ ifp_user_get_groups_process, ++ state); + return EOK; + } + + static void ifp_user_get_groups_process(struct tevent_req *req) + { +- struct ifp_attr_req *group_req; ++ struct ifp_user_get_groups_state *state; ++ struct ifp_attr_req *group_attr_req; + errno_t ret; +- struct ldb_result *res; +- struct sss_domain_info *dom; + +- group_req = tevent_req_callback_data(req, struct ifp_attr_req); ++ state = tevent_req_callback_data(req, struct ifp_user_get_groups_state); ++ group_attr_req = state->group_attr_req; + +- ret = ifp_user_get_attr_recv(group_req, req, &res, &dom); ++ ret = ifp_user_get_attr_recv(group_attr_req, req, &state->res, &state->dom); + talloc_zfree(req); + if (ret == ENOENT) { +- sbus_request_fail_and_finish(group_req->ireq->dbus_req, +- sbus_error_new(group_req->ireq->dbus_req, ++ sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req, ++ sbus_error_new(group_attr_req->ireq->dbus_req, + DBUS_ERROR_FAILED, + "No such user\n")); + return; + } else if (ret != EOK) { +- sbus_request_fail_and_finish(group_req->ireq->dbus_req, +- sbus_error_new(group_req->ireq->dbus_req, ++ sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req, ++ sbus_error_new(group_attr_req->ireq->dbus_req, + DBUS_ERROR_FAILED, + "Failed to read attribute\n")); + return; + } + +- ret = ifp_user_get_groups_reply(dom, group_req->ireq, res); ++ req = resp_resolve_group_names_send(state, ++ state->rctx->ev, ++ state->rctx, ++ state->dom, ++ state->res); ++ if (req == NULL) { ++ sbus_request_finish(group_attr_req->ireq->dbus_req, NULL); ++ return; ++ } ++ tevent_req_set_callback(req, ++ ifp_user_get_groups_names_resolved, ++ state); ++} ++ ++static void ifp_user_get_groups_names_resolved(struct tevent_req *req) ++{ ++ struct ifp_user_get_groups_state *state; ++ struct ifp_attr_req *group_attr_req; ++ errno_t ret; ++ ++ state = tevent_req_callback_data(req, struct ifp_user_get_groups_state); ++ group_attr_req = state->group_attr_req; ++ ++ ret = resp_resolve_group_names_recv(state, req, &state->res_names); ++ talloc_zfree(req); + if (ret != EOK) { +- sbus_request_fail_and_finish(group_req->ireq->dbus_req, +- sbus_error_new(group_req->ireq->dbus_req, +- DBUS_ERROR_FAILED, +- "Failed to build a reply\n")); ++ sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req, ++ sbus_error_new(group_attr_req->ireq->dbus_req, ++ DBUS_ERROR_FAILED, ++ "Failed to resolve groupnames\n")); ++ return; ++ } ++ ++ if (state->res_names == NULL) { ++ state->res_names = state->res; ++ } ++ ++ ret = ifp_user_get_groups_reply(state->dom, ++ group_attr_req->ireq, ++ state->res_names); ++ if (ret != EOK) { ++ sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req, ++ sbus_error_new( ++ group_attr_req->ireq->dbus_req, ++ DBUS_ERROR_FAILED, ++ "Failed to build a reply\n")); + return; + } + } +-- +2.9.4 + diff --git a/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch b/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch new file mode 100644 index 0000000..caebbc1 --- /dev/null +++ b/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch @@ -0,0 +1,56 @@ +From 85b74b966ec1d417ce76b05cbf3351b20c0981b2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 17 May 2017 15:43:25 +0200 +Subject: [PATCH 164/166] ldap: handle certmap errors gracefully + +Currently the LDAP user lookup request errors out if e.g. there is no +matching rule for a certificate. This might cause the related domain to +go offline. + +With this patch the request returns that no user was found for the given +certificate but overall result is that the request finishes +successfully. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3405 + +Reviewed-by: Jakub Hrozek +--- + src/providers/ldap/ldap_id.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index 7400dc1f57e30cc6ae5f939ffa628a1e9dd47e06..557712e8dc2b2bde664b4054fa2f8eb39df84d73 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -258,6 +258,27 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_cert_derb64_to_ldap_filter failed.\n"); ++ ++ /* Typically sss_cert_derb64_to_ldap_filter() will fail if there ++ * is no mapping rule matching the current certificate. But this ++ * just means that no matching user can be found so we can finish ++ * the request with this result. Even if ++ * sss_cert_derb64_to_ldap_filter() would fail for other reason ++ * there is no need to return an error which might cause the ++ * domain go offline. */ ++ ++ if (noexist_delete) { ++ ret = sysdb_remove_cert(state->domain, filter_value); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Ignoring error while removing user certificate " ++ "[%d]: %s\n", ret, sss_strerror(ret)); ++ } ++ } ++ ++ ret = EOK; ++ state->sdap_ret = ENOENT; ++ state->dp_error = DP_ERR_OK; + goto done; + } + +-- +2.9.4 + diff --git a/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch b/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch new file mode 100644 index 0000000..0a3ecf7 --- /dev/null +++ b/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch @@ -0,0 +1,53 @@ +From 0e1416e65c99aca947e589bfa56d5bc832c023d6 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Sat, 27 May 2017 14:39:45 +0200 +Subject: [PATCH 165/166] SECRETS: Fix warning Wpointer-bool-conversion + +Debug messages would always say that verify_peer and verify_host +are enabled. Even though they would be explicitly disabled. + +src/responder/secrets/proxy.c:143:18: error: + address of 'cfg->verify_peer' will always evaluate to + 'true' [-Werror,-Wpointer-bool-conversion] + (&cfg->verify_peer ? "true" : "false")); + ~~~~~^~~~~~~~~~~ ~ +src/util/debug.h:108:32: note: expanded from macro 'DEBUG' + format, ##__VA_ARGS__); \ + ^~~~~~~~~~~ +src/responder/secrets/proxy.c:149:18: error: + address of 'cfg->verify_host' will always evaluate to + 'true' [-Werror,-Wpointer-bool-conversion] + (&cfg->verify_host ? "true" : "false")); + ~~~~~^~~~~~~~~~~ ~ +src/util/debug.h:108:32: note: expanded from macro 'DEBUG' + format, ##__VA_ARGS__); \ + ^~~~~~~~~~~ + +Reviewed-by: Jakub Hrozek +--- + src/responder/secrets/proxy.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c +index 9c2aa425d414728d10aa830f640632e98def3c1c..a4e97f83ef406e71a1e6509a6b719c47afdfd5b8 100644 +--- a/src/responder/secrets/proxy.c ++++ b/src/responder/secrets/proxy.c +@@ -140,13 +140,13 @@ static int proxy_sec_get_cfg(struct proxy_context *pctx, + true, &cfg->verify_peer); + if (ret) goto done; + DEBUG(SSSDBG_CONF_SETTINGS, "verify_peer: %s\n", +- (&cfg->verify_peer ? "true" : "false")); ++ cfg->verify_peer ? "true" : "false"); + + ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_host", + true, &cfg->verify_host); + if (ret) goto done; + DEBUG(SSSDBG_CONF_SETTINGS, "verify_host: %s\n", +- (&cfg->verify_host ? "true" : "false")); ++ cfg->verify_host ? "true" : "false"); + + ret = proxy_get_config_string(pctx, cfg, false, secreq, + "capath", &cfg->capath); +-- +2.9.4 + diff --git a/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch b/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch new file mode 100644 index 0000000..f9eb273 --- /dev/null +++ b/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch @@ -0,0 +1,50 @@ +From 62cebc27bd0bdb2c12531203fd79f231e96eab7b Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 2 Jun 2017 11:17:18 +0200 +Subject: [PATCH 166/166] IPA: Fix the PAM error code that auth code expects to + start migration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Recent patches which adds support for PKINIT in krb5_child changed a +return code which is used to indicate to the IPA provider that password +migration should be tried. + +With this patch krb5_child properly returns PAM_CRED_ERR as expected by +the IPA provider in this case. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3394 + +Reviewed-by: Simo Sorce +Reviewed-by: Lukáš Slebodník +--- + src/providers/krb5/krb5_child.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index cbbc892bee0365892ac66d3654c974d325166b60..3cd8bfba76a35acd2c885ee2aac4765a6c1cc03c 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -1540,6 +1540,17 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + ++ /* Special case for IPA password migration */ ++ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ++ && kerr == KRB5_PREAUTH_FAILED ++ && kr->pkinit_prompting == false ++ && kr->password_prompting == false ++ && kr->otp == false ++ && sss_authtok_get_type(kr->pd->authtok) ++ == SSS_AUTHTOK_TYPE_PASSWORD) { ++ return ERR_CREDS_INVALID; ++ } ++ + /* If during authentication either the MIT Kerberos pkinit + * pre-auth module is missing or no Smartcard is inserted and only + * pkinit is available KRB5_PREAUTH_FAILED is returned. +-- +2.9.4 + diff --git a/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch b/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch new file mode 100644 index 0000000..b8e2d31 --- /dev/null +++ b/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch @@ -0,0 +1,52 @@ +From 1ac8b82addfa0a4c94321d5cb72b7991755e61f8 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Fri, 2 Jun 2017 11:56:55 +0200 +Subject: [PATCH 167/169] pam_sss: Fix checking of empty string cert_user +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +src/sss_client/pam_sss.c: In function ‘eval_response’: +src/sss_client/pam_sss.c:998:64: error: comparison between pointer and zero character constant [-Werror=pointer-compare] + if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') { + ^~ +src/sss_client/pam_sss.c:998:50: note: did you mean to dereference the pointer? + if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') { + ^ +src/sss_client/pam_sss.c:1010:42: error: comparison between pointer and zero character constant [-Werror=pointer-compare] + && pi->cert_user != '\0') { + ^~ +src/sss_client/pam_sss.c:1010:28: note: did you mean to dereference the pointer? + && pi->cert_user != '\0') { + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit c62dc2ac02253e130991db0f6acd60ce1a2753f1) +--- + src/sss_client/pam_sss.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index 1c06079967e3d9076d537c3de8aba93e13f76d09..9732459e6fb7ce01c9445c423cf0a583ca36e036 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -995,7 +995,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + break; + } + +- if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') { ++ if (type == SSS_PAM_CERT_INFO && *pi->cert_user == '\0') { + D(("Invalid CERT message")); + break; + } +@@ -1007,7 +1007,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + } + + if ((pi->pam_user == NULL || *(pi->pam_user) == '\0') +- && pi->cert_user != '\0') { ++ && *pi->cert_user != '\0') { + ret = pam_set_item(pamh, PAM_USER, pi->cert_user); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_USER during " +-- +2.9.4 + diff --git a/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch b/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch new file mode 100644 index 0000000..67e6f57 --- /dev/null +++ b/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch @@ -0,0 +1,124 @@ +From 992a6410a3100cc64f9f2ea674fda9151fa5d474 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 29 May 2017 14:58:33 +0200 +Subject: [PATCH 168/169] CACHE_REQ: Simplify _search_ncache_filter() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Let's make the result and input/output argument for +_search_ncache_filter() and free it inside the function whenever it's +needed instead of leaving this responsibility for the caller. + +Related: +https://pagure.io/SSSD/sssd/issue/3362 + +Signed-off-by: Fabiano Fidêncio +Reviewed-by: Pavel Březina +(cherry picked from commit c8193b1602cf44740b59f5dfcdc5330508c0c365) +--- + src/responder/common/cache_req/cache_req_search.c | 27 ++++++----------------- + 1 file changed, 7 insertions(+), 20 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c +index 70448a7639bc9f98d380b8edce9d130adfa0ceb2..d3aaa7542ddfd28716fbf9cdcedfeadb649dbaa0 100644 +--- a/src/responder/common/cache_req/cache_req_search.c ++++ b/src/responder/common/cache_req/cache_req_search.c +@@ -86,7 +86,6 @@ static void cache_req_search_ncache_add(struct cache_req *cr) + + static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, + struct cache_req *cr, +- struct ldb_result *result, + struct ldb_result **_result) + { + TALLOC_CTX *tmp_ctx; +@@ -106,8 +105,6 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, + "This request type does not support filtering " + "result by negative cache\n"); + +- *_result = talloc_steal(mem_ctx, result); +- + ret = EOK; + goto done; + } +@@ -115,11 +112,11 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Filtering out results by negative cache\n"); + +- msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, result->count); ++ msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, (*_result)->count); + msg_count = 0; + +- for (size_t i = 0; i < result->count; i++) { +- name = sss_get_name_from_msg(cr->domain, result->msgs[i]); ++ for (size_t i = 0; i < (*_result)->count; i++) { ++ name = sss_get_name_from_msg(cr->domain, (*_result)->msgs[i]); + if (name == NULL) { + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, + "sss_get_name_from_msg() returned NULL, which should never " +@@ -141,7 +138,7 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, + goto done; + } + +- msgs[msg_count] = talloc_steal(msgs, result->msgs[i]); ++ msgs[msg_count] = talloc_steal(msgs, (*_result)->msgs[i]); + msg_count++; + } + +@@ -157,6 +154,7 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx, + goto done; + } + ++ talloc_zfree(*_result); + *_result = talloc_steal(mem_ctx, filtered_result); + ret = EOK; + +@@ -419,10 +417,8 @@ static void cache_req_search_oob_done(struct tevent_req *subreq) + + static void cache_req_search_done(struct tevent_req *subreq) + { +- TALLOC_CTX *tmp_ctx; + struct cache_req_search_state *state; + struct tevent_req *req; +- struct ldb_result *result = NULL; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); +@@ -431,14 +427,8 @@ static void cache_req_search_done(struct tevent_req *subreq) + state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr); + talloc_zfree(subreq); + +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- ret = ENOMEM; +- goto done; +- } +- + /* Get result from cache again. */ +- ret = cache_req_search_cache(tmp_ctx, state->cr, &result); ++ ret = cache_req_search_cache(state, state->cr, &state->result); + if (ret != EOK) { + if (ret == ENOENT) { + /* Only store entry in negative cache if DP request succeeded +@@ -451,8 +441,7 @@ static void cache_req_search_done(struct tevent_req *subreq) + } + + /* ret == EOK */ +- ret = cache_req_search_ncache_filter(state, state->cr, result, +- &state->result); ++ ret = cache_req_search_ncache_filter(state, state->cr, &state->result); + if (ret != EOK) { + goto done; + } +@@ -461,8 +450,6 @@ static void cache_req_search_done(struct tevent_req *subreq) + "Returning updated object [%s]\n", state->cr->debugobj); + + done: +- talloc_free(tmp_ctx); +- + if (ret != EOK) { + tevent_req_error(req, ret); + return; +-- +2.9.4 + diff --git a/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch b/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch new file mode 100644 index 0000000..6d53d21 --- /dev/null +++ b/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch @@ -0,0 +1,61 @@ +From 79f389eb400eddc133824b079f8bd49ced24643b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 17 May 2017 14:43:39 +0200 +Subject: [PATCH 169/169] CACHE_REQ_SEARCH: Check for filtered users/groups + also on cache_req_send() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +cache_req_send() may take some shortcuts in case the object is found in +the cache and it's still valid. + +This behaviour may lead to exposing filtered users and groups when +they're searched by their uid/gid. + +A solution for this issue was proposed on 4ef0b19a but, unfortunately, +didn't take into consideration that this shortcut could be taken. + +There are basically two really easy ways to test this issue: + 1) Using enumeration: + - Set "enumerate = True" in the domain section + - restart SSSD cleaning up the cache; + - getent passwd + - Wait a little bit till the entry_negative_timeout is expired + - getent passwd + + 2) Not using enumeration: + - getent passwd + - Wait a little bit till the entry_negative_timeout is expired + - getent passwd + +A test covering this code path will be added in the follow-up commit. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3362 + +Signed-off-by: Fabiano Fidêncio +Reviewed-by: Pavel Březina +(cherry picked from commit 4c09cd008967c5c0ec358dc658ffc6fc1cef2697) +--- + src/responder/common/cache_req/cache_req_search.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c +index d3aaa7542ddfd28716fbf9cdcedfeadb649dbaa0..56d0345cd8f98de574961d3c9628ae7a4c24f9be 100644 +--- a/src/responder/common/cache_req/cache_req_search.c ++++ b/src/responder/common/cache_req/cache_req_search.c +@@ -334,6 +334,10 @@ cache_req_search_send(TALLOC_CTX *mem_ctx, + + done: + if (ret == EOK) { ++ ret = cache_req_search_ncache_filter(state, cr, &state->result); ++ } ++ ++ if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); +-- +2.9.4 + diff --git a/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch b/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch new file mode 100644 index 0000000..78326bf --- /dev/null +++ b/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch @@ -0,0 +1,34 @@ +From eb3f60eacc6279a6bd97eff7d7be0cc081a7bf9a Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Thu, 8 Jun 2017 12:32:44 +0200 +Subject: [PATCH 170/171] cache_req: Do not use default_domain_suffix with + netgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Resolves: +https://pagure.io/SSSD/sssd/issue/3428 + +Reviewed-by: Pavel Březina +(cherry picked from commit c83e265bbb5b2f2aa4f0067263753c8403c383f9) +--- + src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +index 4d8bb18579a286042b00528190dadd52fdd7c75c..ef0775d0b8eac4d679450f436d8427cff9c04582 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c ++++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c +@@ -112,7 +112,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = { + .name = "Netgroup by name", + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, +- .ignore_default_domain = false, ++ .ignore_default_domain = true, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +-- +2.9.4 + diff --git a/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch b/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch new file mode 100644 index 0000000..22655c1 --- /dev/null +++ b/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch @@ -0,0 +1,47 @@ +From 0956acb31884e87ef48c3be8c59960acfc03a547 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 8 Jun 2017 11:06:02 +0200 +Subject: [PATCH 171/171] krb5: disable enterprise principals during password + changes + +Currently using enterprise principals during password changes does not +work reliable. + +First there is a special behavior if canonicalization, which in general +should be used together with enterprise principals, is enabled with AD, +see https://pagure.io/SSSD/sssd/issue/1405 and +https://pagure.io/SSSD/sssd/issue/1615 for details. As a result of this +SSSD currently disables canonicalization during password changes. + +Additionally it looks like MIT Kerberos does not handle canonicalized +principals well, even if canonicalization is enabled, if not the default +krbtgt/REALM@REALM but kadmin/changepw@REALM is requested. Since it is +currently not clear what is the expected behavior here it make sense to +completely disable enterprise principals during password changes for the +time being. + +Resolves https://pagure.io/SSSD/sssd/issue/3426 + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 614057ea85c05d3a6d4b62217a41b8b5db8d5d38) +--- + src/providers/krb5/krb5_child_handler.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c +index 11ac867e62d2ff96b827cf6d4ff341fc8ff0a286..0007f92a61ba711aed6be5ee28664e5f7de0f226 100644 +--- a/src/providers/krb5/krb5_child_handler.c ++++ b/src/providers/krb5/krb5_child_handler.c +@@ -143,7 +143,8 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + return EINVAL; + } + +- if (kr->pd->cmd == SSS_CMD_RENEW || kr->is_offline) { ++ if (kr->pd->cmd == SSS_CMD_RENEW || kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ++ || kr->pd->cmd == SSS_PAM_CHAUTHTOK || kr->is_offline) { + use_enterprise_principal = false; + } else { + use_enterprise_principal = dp_opt_get_bool(kr->krb5_ctx->opts, +-- +2.9.4 + diff --git a/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch b/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch new file mode 100644 index 0000000..7c9d403 --- /dev/null +++ b/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch @@ -0,0 +1,44 @@ +From c58aac42664dd1a04edb37b0874109a6a88d0da1 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 5 Jun 2017 09:43:46 +0200 +Subject: [PATCH 172/181] pam_sss: Fix leaking of memory in case of failures +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Found by coverity. + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 818d01b4a0d332fff06db33c0c985b8c0f1417c7) +--- + src/sss_client/pam_sss.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index 9732459e6fb7ce01c9445c423cf0a583ca36e036..303809b9ea05b5a8709c05ae230d5f289b57de31 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -1517,10 +1517,12 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi) + if (pi->user_name_hint) { + ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv); + if (ret != PAM_SUCCESS) { ++ free(prompt); + return ret; + } + if (conv == NULL || conv->conv == NULL) { + logger(pamh, LOG_ERR, "No conversation function"); ++ free(prompt); + return PAM_SYSTEM_ERR; + } + +@@ -1540,6 +1542,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi) + mesg[1] = &((*mesg)[1]); + + ret = conv->conv(2, mesg, &resp, conv->appdata_ptr); ++ free(prompt); + if (ret != PAM_SUCCESS) { + D(("Conversation failure: %s.", pam_strerror(pamh, ret))); + return ret; +-- +2.9.4 + diff --git a/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch b/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch new file mode 100644 index 0000000..be61e37 --- /dev/null +++ b/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch @@ -0,0 +1,372 @@ +From a35b5c33a76857ad9223363e15558facec5c269d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Thu, 8 Jun 2017 11:46:25 +0200 +Subject: [PATCH 173/181] IFP: Add domain and domainname attributes to the user +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +org.freedekstop.sssd.infopipe.Users.User gets two new attributes: +- domain: object path of user's domain +- domainname: user's domain name + +org.freedekstop.sssd.infopipe.GetUserAttr can now request new attribute: +- domainname: user's domain name + +Resolves: +https://pagure.io/SSSD/sssd/issue/2714 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 37d2194cc9ea4d0254c88a3419e2376572562bab) +--- + src/responder/ifp/ifp_iface.c | 2 + + src/responder/ifp/ifp_iface.xml | 2 + + src/responder/ifp/ifp_iface_generated.c | 18 ++++++++ + src/responder/ifp/ifp_iface_generated.h | 4 ++ + src/responder/ifp/ifp_private.h | 4 ++ + src/responder/ifp/ifp_users.c | 46 ++++++++++++++++++++ + src/responder/ifp/ifp_users.h | 8 ++++ + src/responder/ifp/ifpsrv_cmd.c | 8 ++++ + src/responder/ifp/ifpsrv_util.c | 74 ++++++++++++++++++++++++++++++++- + src/tests/cmocka/test_ifp.c | 12 ++++-- + 10 files changed, 173 insertions(+), 5 deletions(-) + +diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c +index e413e74f955c067a0efbe385a08b4b2cc6f2bba1..3293b92d750d33b2ecf77a03098c5169d052c924 100644 +--- a/src/responder/ifp/ifp_iface.c ++++ b/src/responder/ifp/ifp_iface.c +@@ -104,6 +104,8 @@ struct iface_ifp_users_user iface_ifp_users_user = { + .get_loginShell = ifp_users_user_get_login_shell, + .get_uniqueID = ifp_users_user_get_unique_id, + .get_groups = ifp_users_user_get_groups, ++ .get_domain = ifp_users_user_get_domain, ++ .get_domainname = ifp_users_user_get_domainname, + .get_extraAttributes = ifp_users_user_get_extra_attributes + }; + +diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml +index 0a23f56907f64c4c24db3ec3c0a312adbdb3edc8..ce071bb999bd207b8cc81f054da80de52a13d3df 100644 +--- a/src/responder/ifp/ifp_iface.xml ++++ b/src/responder/ifp/ifp_iface.xml +@@ -188,6 +188,8 @@ + + + ++ ++ + + + +diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c +index 211646b6760d15e0df55ac20b9611b800b11d16c..51db4a9e5c7d72663f8845bd0da22d3f21526be8 100644 +--- a/src/responder/ifp/ifp_iface_generated.c ++++ b/src/responder/ifp/ifp_iface_generated.c +@@ -982,6 +982,24 @@ const struct sbus_property_meta iface_ifp_users_user__properties[] = { + NULL, /* no invoker */ + }, + { ++ "domain", /* name */ ++ "o", /* type */ ++ SBUS_PROPERTY_READABLE, ++ offsetof(struct iface_ifp_users_user, get_domain), ++ sbus_invoke_get_o, ++ 0, /* not writable */ ++ NULL, /* no invoker */ ++ }, ++ { ++ "domainname", /* name */ ++ "s", /* type */ ++ SBUS_PROPERTY_READABLE, ++ offsetof(struct iface_ifp_users_user, get_domainname), ++ sbus_invoke_get_s, ++ 0, /* not writable */ ++ NULL, /* no invoker */ ++ }, ++ { + "extraAttributes", /* name */ + "a{sas}", /* type */ + SBUS_PROPERTY_READABLE, +diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h +index e69fc3a3efc6bdcef5d6539790908795818cd857..76f729fcb268e9c07668b3a5ee5bbd7d0b44ca16 100644 +--- a/src/responder/ifp/ifp_iface_generated.h ++++ b/src/responder/ifp/ifp_iface_generated.h +@@ -88,6 +88,8 @@ + #define IFACE_IFP_USERS_USER_LOGINSHELL "loginShell" + #define IFACE_IFP_USERS_USER_UNIQUEID "uniqueID" + #define IFACE_IFP_USERS_USER_GROUPS "groups" ++#define IFACE_IFP_USERS_USER_DOMAIN "domain" ++#define IFACE_IFP_USERS_USER_DOMAINNAME "domainname" + #define IFACE_IFP_USERS_USER_EXTRAATTRIBUTES "extraAttributes" + + /* constants for org.freedesktop.sssd.infopipe.Groups */ +@@ -288,6 +290,8 @@ struct iface_ifp_users_user { + void (*get_loginShell)(struct sbus_request *, void *data, const char **); + void (*get_uniqueID)(struct sbus_request *, void *data, const char **); + void (*get_groups)(struct sbus_request *, void *data, const char ***, int *); ++ void (*get_domain)(struct sbus_request *, void *data, const char **); ++ void (*get_domainname)(struct sbus_request *, void *data, const char **); + void (*get_extraAttributes)(struct sbus_request *, void *data, hash_table_t **); + }; + +diff --git a/src/responder/ifp/ifp_private.h b/src/responder/ifp/ifp_private.h +index e800070a59f77f8ce58a2fc402e616bb773e996b..a6e5701b8d1ebb27af0c35fa3ebe0c6c00d16bd6 100644 +--- a/src/responder/ifp/ifp_private.h ++++ b/src/responder/ifp/ifp_private.h +@@ -70,6 +70,10 @@ errno_t ifp_req_create(struct sbus_request *dbus_req, + /* Returns an appropriate DBus error for specific ifp_req_create failures */ + int ifp_req_create_handle_failure(struct sbus_request *dbus_req, errno_t err); + ++errno_t ifp_add_value_to_dict(DBusMessageIter *iter_dict, ++ const char *key, ++ const char *value); ++ + errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict, + struct ldb_message_element *el); + const char ** +diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c +index 188194f2ab356d0e67b0f26b003f3a9ce48e6acd..90b947ed9ca345fbeba6772c90f898451a0868aa 100644 +--- a/src/responder/ifp/ifp_users.c ++++ b/src/responder/ifp/ifp_users.c +@@ -1328,6 +1328,52 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req, + *_size = num_groups; + } + ++void ifp_users_user_get_domain(struct sbus_request *sbus_req, ++ void *data, ++ const char **_out) ++{ ++ const char *domainname; ++ ++ *_out = NULL; ++ ifp_users_user_get_domainname(sbus_req, data, &domainname); ++ ++ if (domainname == NULL) { ++ return; ++ } ++ ++ *_out = sbus_opath_compose(sbus_req, IFP_PATH_DOMAINS, ++ domainname); ++} ++ ++void ifp_users_user_get_domainname(struct sbus_request *sbus_req, ++ void *data, ++ const char **_out) ++{ ++ struct ifp_ctx *ifp_ctx; ++ struct sss_domain_info *domain; ++ errno_t ret; ++ ++ *_out = NULL; ++ ++ ifp_ctx = talloc_get_type(data, struct ifp_ctx); ++ if (ifp_ctx == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n"); ++ return; ++ } ++ ++ if (!ifp_is_user_attr_allowed(ifp_ctx, "domainname")) { ++ DEBUG(SSSDBG_TRACE_ALL, "Attribute domainname is not allowed\n"); ++ return; ++ } ++ ++ ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, NULL); ++ if (ret != EOK) { ++ return; ++ } ++ ++ *_out = domain->name; ++} ++ + void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req, + void *data, + hash_table_t **_out) +diff --git a/src/responder/ifp/ifp_users.h b/src/responder/ifp/ifp_users.h +index f8fefeb7f658b6e0a5f72371da1b025d69e6f412..715a8bc31996bfd93c21dbe263f2567bd0b50b03 100644 +--- a/src/responder/ifp/ifp_users.h ++++ b/src/responder/ifp/ifp_users.h +@@ -103,6 +103,14 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req, + const char ***_out, + int *_size); + ++void ifp_users_user_get_domain(struct sbus_request *sbus_req, ++ void *data, ++ const char **_out); ++ ++void ifp_users_user_get_domainname(struct sbus_request *sbus_req, ++ void *data, ++ const char **_out); ++ + void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req, + void *data, + hash_table_t **_out); +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index 70728e1bb656fd032b7f1c240683e8aa3b91a726..d86aed57206ba8f0a6facbd64051fa7c901513f3 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -233,6 +233,14 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain, + } + + for (ai = 0; attrs[ai]; ai++) { ++ if (strcmp(attrs[ai], "domainname") == 0) { ++ ret = ifp_add_value_to_dict(&iter_dict, "domainname", ++ domain->name); ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot add attribute %s to message\n", attrs[ai]); ++ continue; ++ } ++ + el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]); + if (el == NULL || el->num_values == 0) { + DEBUG(SSSDBG_MINOR_FAILURE, +diff --git a/src/responder/ifp/ifpsrv_util.c b/src/responder/ifp/ifpsrv_util.c +index 5866d30d8a5845c21f5b05fc5de150162eba747e..643881515fb4805ae93ba56c3bca9d1da7796319 100644 +--- a/src/responder/ifp/ifpsrv_util.c ++++ b/src/responder/ifp/ifpsrv_util.c +@@ -29,7 +29,7 @@ + #define IFP_USER_DEFAULT_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, \ + SYSDB_GIDNUM, SYSDB_GECOS, \ + SYSDB_HOMEDIR, SYSDB_SHELL, \ +- "groups", \ ++ "groups", "domain", "domainname", \ + NULL} + + errno_t ifp_req_create(struct sbus_request *dbus_req, +@@ -100,6 +100,78 @@ int ifp_req_create_handle_failure(struct sbus_request *dbus_req, errno_t err) + "Cannot create IFP request\n")); + } + ++errno_t ifp_add_value_to_dict(DBusMessageIter *iter_dict, ++ const char *key, ++ const char *value) ++{ ++ DBusMessageIter iter_dict_entry; ++ DBusMessageIter iter_dict_val; ++ DBusMessageIter iter_array; ++ dbus_bool_t dbret; ++ ++ if (value == NULL || key == NULL) { ++ return EINVAL; ++ } ++ ++ dbret = dbus_message_iter_open_container(iter_dict, ++ DBUS_TYPE_DICT_ENTRY, NULL, ++ &iter_dict_entry); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ /* Start by appending the key */ ++ dbret = dbus_message_iter_append_basic(&iter_dict_entry, ++ DBUS_TYPE_STRING, &key); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ dbret = dbus_message_iter_open_container(&iter_dict_entry, ++ DBUS_TYPE_VARIANT, ++ DBUS_TYPE_ARRAY_AS_STRING ++ DBUS_TYPE_STRING_AS_STRING, ++ &iter_dict_val); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ /* Open container for values */ ++ dbret = dbus_message_iter_open_container(&iter_dict_val, ++ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, ++ &iter_array); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ dbret = dbus_message_iter_append_basic(&iter_array, ++ DBUS_TYPE_STRING, ++ &value); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ dbret = dbus_message_iter_close_container(&iter_dict_val, ++ &iter_array); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ dbret = dbus_message_iter_close_container(&iter_dict_entry, ++ &iter_dict_val); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ dbret = dbus_message_iter_close_container(iter_dict, ++ &iter_dict_entry); ++ if (!dbret) { ++ return ENOMEM; ++ } ++ ++ return EOK; ++} ++ + errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict, + struct ldb_message_element *el) + { +diff --git a/src/tests/cmocka/test_ifp.c b/src/tests/cmocka/test_ifp.c +index 21c5475d1c74cd8325815653166bef194ea84f7b..45f718341222c6803a65130741590e10e7aded84 100644 +--- a/src/tests/cmocka/test_ifp.c ++++ b/src/tests/cmocka/test_ifp.c +@@ -269,7 +269,7 @@ void test_attr_acl(void **state) + const char *exp_defaults[] = { SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, SYSDB_SHELL, +- "groups", NULL }; ++ "groups", "domain", "domainname", NULL }; + attr_parse_test(exp_defaults, NULL); + + /* Test adding some attributes to the defaults */ +@@ -277,13 +277,14 @@ void test_attr_acl(void **state) + SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, SYSDB_SHELL, +- "groups", NULL }; ++ "groups", "domain", "domainname", NULL }; + attr_parse_test(exp_add, "+telephoneNumber, +streetAddress"); + + /* Test removing some attributes to the defaults */ + const char *exp_rm[] = { SYSDB_NAME, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, "groups", ++ "domain", "domainname", + NULL }; + attr_parse_test(exp_rm, "-"SYSDB_SHELL ",-"SYSDB_UIDNUM); + +@@ -292,6 +293,7 @@ void test_attr_acl(void **state) + SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, "groups", ++ "domain", "domainname", + NULL }; + attr_parse_test(exp_add_rm, "+telephoneNumber, -"SYSDB_SHELL); + +@@ -299,7 +301,8 @@ void test_attr_acl(void **state) + const char *exp_add_rm_override[] = { SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, SYSDB_SHELL, +- "groups", NULL }; ++ "groups", "domain", ++ "domainname", NULL }; + attr_parse_test(exp_add_rm_override, + "+telephoneNumber, -telephoneNumber, +telephoneNumber"); + +@@ -307,7 +310,8 @@ void test_attr_acl(void **state) + const char *rm_all[] = { NULL }; + attr_parse_test(rm_all, "-"SYSDB_NAME ", -"SYSDB_UIDNUM + ", -"SYSDB_GIDNUM ", -"SYSDB_GECOS +- ", -"SYSDB_HOMEDIR ", -"SYSDB_SHELL", -groups"); ++ ", -"SYSDB_HOMEDIR ", -"SYSDB_SHELL", -groups, " ++ "-domain, -domainname"); + + /* Malformed list */ + attr_parse_test(NULL, "missing_plus_or_minus"); +-- +2.9.4 + diff --git a/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch b/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch new file mode 100644 index 0000000..4d39c22 --- /dev/null +++ b/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch @@ -0,0 +1,39 @@ +From 271679e7a7c0c50e39c7a0989dbae77385475c60 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 14 Jun 2017 18:25:21 +0200 +Subject: [PATCH 174/181] IFP: Fix error handling in + ifp_user_get_attr_handle_reply() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This bug was introduced in 37d2194cc9ea4d0254c88a3419e2376572562bab + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 15a76bb7bd9791a3ed1ae416f70753d32c6ff599) +--- + src/responder/ifp/ifpsrv_cmd.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index d86aed57206ba8f0a6facbd64051fa7c901513f3..fc9161e82e906ac7dde2712ffc7c0cbb58c519b7 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -236,9 +236,11 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain, + if (strcmp(attrs[ai], "domainname") == 0) { + ret = ifp_add_value_to_dict(&iter_dict, "domainname", + domain->name); +- DEBUG(SSSDBG_MINOR_FAILURE, +- "Cannot add attribute %s to message\n", attrs[ai]); +- continue; ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot add attribute domainname to message\n"); ++ continue; ++ } + } + + el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]); +-- +2.9.4 + diff --git a/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch b/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch new file mode 100644 index 0000000..748f9e8 --- /dev/null +++ b/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch @@ -0,0 +1,76 @@ +From 10b75d84300726e5e311b0488352b891f106d631 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 24 May 2017 00:35:23 +0200 +Subject: [PATCH 175/181] SYSDB: Return ERR_NO_TS when there's no timestamp + cache present +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This change affects sysdb_search_ts_{users,groups} functions and is +mainly needed in order to avoid breaking our current tests due to the +changes planned for fixing https://pagure.io/SSSD/sssd/issue/3369. + +Related: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 01c6bb9b47401f9f14c4cfe5c5f03fce2e63629b) +--- + src/db/sysdb_ops.c | 4 ++-- + src/db/sysdb_search.c | 8 ++++++++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 4d7b2abd8026c90aaf4e7be687102e459cf3690e..12f8095d2edc60ffab09c92d64f968892c577bbf 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -3520,7 +3520,7 @@ int sysdb_search_ts_users(TALLOC_CTX *mem_ctx, + ZERO_STRUCT(*res); + + if (domain->sysdb->ldb_ts == NULL) { +- return ENOENT; ++ return ERR_NO_TS; + } + + ret = sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb_ts, +@@ -3737,7 +3737,7 @@ int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx, + ZERO_STRUCT(*res); + + if (domain->sysdb->ldb_ts == NULL) { +- return ENOENT; ++ return ERR_NO_TS; + } + + ret = sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb_ts, +diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c +index 474bc08f0b2fe3b0289cbea96fbf2619ced271e7..6b4b51383d89788052ab7e4b572e86abba5330db 100644 +--- a/src/db/sysdb_search.c ++++ b/src/db/sysdb_search.c +@@ -587,6 +587,10 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, + ret = sysdb_search_ts_users(tmp_ctx, domain, ts_filter, + sysdb_ts_cache_attrs, + &ts_res); ++ if (ret == ERR_NO_TS) { ++ ret = ENOENT; ++ } ++ + if (ret != EOK && ret != ENOENT) { + goto done; + } +@@ -1088,6 +1092,10 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, + ret = sysdb_search_ts_groups(tmp_ctx, domain, ts_filter, + sysdb_ts_cache_attrs, + &ts_res); ++ if (ret == ERR_NO_TS) { ++ ret = ENOENT; ++ } ++ + if (ret != EOK && ret != ENOENT) { + goto done; + } +-- +2.9.4 + diff --git a/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch b/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch new file mode 100644 index 0000000..38fdfa6 --- /dev/null +++ b/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch @@ -0,0 +1,90 @@ +From 67e592572e655f19326cf821bbbe43411e8c7b06 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 23 May 2017 22:44:24 +0200 +Subject: [PATCH 176/181] SYSDB: Internally expose sysdb_search_ts_matches() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This function will be used in the follow-up patches. As it's going to be +"exposed", let's also rename it from search_ts_matches() to +sysdb_search_ts_matches(). + +Related: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 347be58e1769ba90b49a7e5ec1678ef66987f6cd) +--- + src/db/sysdb_private.h | 7 +++++++ + src/db/sysdb_search.c | 20 ++++++++++---------- + 2 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h +index dfddd2dda9e593bd02d52dee7d06f520a11bbdf6..433220dcc0c35366dbbee41525e6c5932eb897f9 100644 +--- a/src/db/sysdb_private.h ++++ b/src/db/sysdb_private.h +@@ -260,6 +260,13 @@ int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx, + const char **attrs, + struct ldb_result *res); + ++errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ const char *attrs[], ++ struct ldb_result *ts_res, ++ const char *filter, ++ struct ldb_result **_res); ++ + /* Compares the modifyTimestamp attribute between old_entry and + * new_entry. Returns true if they differ (or either entry is missing + * the attribute) and false if the attribute is the same +diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c +index 6b4b51383d89788052ab7e4b572e86abba5330db..0c04b84a584e047a0ba8243c9216547ea2791e60 100644 +--- a/src/db/sysdb_search.c ++++ b/src/db/sysdb_search.c +@@ -482,12 +482,12 @@ done: + return ret; + } + +-static errno_t search_ts_matches(TALLOC_CTX *mem_ctx, +- struct sysdb_ctx *sysdb, +- const char *attrs[], +- struct ldb_result *ts_res, +- const char *filter, +- struct ldb_result **_res) ++errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx, ++ struct sysdb_ctx *sysdb, ++ const char *attrs[], ++ struct ldb_result *ts_res, ++ const char *filter, ++ struct ldb_result **_res) + { + char *dn_filter; + TALLOC_CTX *tmp_ctx = NULL; +@@ -595,8 +595,8 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, + goto done; + } + +- ret = search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, +- name_filter, &ts_cache_res); ++ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, ++ name_filter, &ts_cache_res); + if (ret != EOK && ret != ENOENT) { + goto done; + } +@@ -1100,8 +1100,8 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, + goto done; + } + +- ret = search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, +- name_filter, &ts_cache_res); ++ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, ++ name_filter, &ts_cache_res); + if (ret != EOK && ret != ENOENT) { + goto done; + } +-- +2.9.4 + diff --git a/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch b/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch new file mode 100644 index 0000000..89eb87a --- /dev/null +++ b/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch @@ -0,0 +1,157 @@ +From 7d926fb2e8fe21e3fa51bc341189d33658600daf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 24 May 2017 11:52:23 +0200 +Subject: [PATCH 177/181] SYSDB: Make the usage of the filter more generic for + search_ts_matches() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In order to make this function re-usable in different parts of our code, +let's start passing an already built filter to it instead of having the +specific code building the name filter there. + +Related: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 8ad57e17779b3ec60246ac58c1691ee15745084c) +--- + src/db/sysdb_search.c | 67 +++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 54 insertions(+), 13 deletions(-) + +diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c +index 0c04b84a584e047a0ba8243c9216547ea2791e60..f488442afcc6eef114437a7110722759f86fe19e 100644 +--- a/src/db/sysdb_search.c ++++ b/src/db/sysdb_search.c +@@ -489,7 +489,6 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx, + const char *filter, + struct ldb_result **_res) + { +- char *dn_filter; + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_result *res; + errno_t ret; +@@ -501,7 +500,7 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx, + } + + tmp_ctx = talloc_new(NULL); +- if (!tmp_ctx) { ++ if (tmp_ctx == NULL) { + return ENOMEM; + } + +@@ -511,7 +510,43 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx, + goto done; + } + +- dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME, filter); ++ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL, ++ LDB_SCOPE_SUBTREE, attrs, "%s", filter); ++ if (ret) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ *_res = talloc_steal(mem_ctx, res); ++ ret = EOK; ++ ++done: ++ talloc_zfree(tmp_ctx); ++ return ret; ++} ++ ++static errno_t sysdb_enum_dn_filter(TALLOC_CTX *mem_ctx, ++ struct ldb_result *ts_res, ++ const char *name_filter, ++ char **_dn_filter) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ char *dn_filter; ++ errno_t ret; ++ ++ if (ts_res->count == 0) { ++ *_dn_filter = NULL; ++ ret = EOK; ++ goto done; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME, ++ name_filter); + if (dn_filter == NULL) { + ret = ENOMEM; + goto done; +@@ -535,15 +570,9 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx, + goto done; + } + +- ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL, +- LDB_SCOPE_SUBTREE, attrs, "%s", dn_filter); +- if (ret) { +- ret = sysdb_error_to_errno(ret); +- goto done; +- } +- ++ *_dn_filter = talloc_steal(mem_ctx, dn_filter); + ret = EOK; +- *_res = talloc_steal(mem_ctx, res); ++ + done: + talloc_zfree(tmp_ctx); + return ret; +@@ -558,6 +587,7 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, + TALLOC_CTX *tmp_ctx; + static const char *attrs[] = SYSDB_PW_ATTRS; + char *filter = NULL; ++ char *dn_filter = NULL; + const char *ts_filter = NULL; + struct ldb_dn *base_dn; + struct ldb_result *res; +@@ -595,8 +625,13 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, + goto done; + } + ++ ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, name_filter, &dn_filter); ++ if (ret != EOK) { ++ goto done; ++ } ++ + ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, +- name_filter, &ts_cache_res); ++ dn_filter, &ts_cache_res); + if (ret != EOK && ret != ENOENT) { + goto done; + } +@@ -1052,6 +1087,7 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, + const char *filter = NULL; + const char *ts_filter = NULL; + const char *base_filter; ++ char *dn_filter = NULL; + struct ldb_dn *base_dn; + struct ldb_result *res; + struct ldb_result ts_res; +@@ -1100,8 +1136,13 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, + goto done; + } + ++ ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, name_filter, &dn_filter); ++ if (ret != EOK) { ++ goto done; ++ } ++ + ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, +- name_filter, &ts_cache_res); ++ dn_filter, &ts_cache_res); + if (ret != EOK && ret != ENOENT) { + goto done; + } +-- +2.9.4 + diff --git a/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch b/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch new file mode 100644 index 0000000..b4cbbf8 --- /dev/null +++ b/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch @@ -0,0 +1,55 @@ +From 891e9c7cb924830334a42864ef2582e545f42723 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 29 May 2017 13:32:59 +0200 +Subject: [PATCH 178/181] SYSDB_OPS: Mark an entry as expired also in the + timestamp cache +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As the cleanup task will start using new methods for searching the users +and groups which have to be cleaned up, SSSD starts relying more in a +more consistent state of the timestamp cache on pretty much everything +related to the cleanup task. + +One of the things that would cause SSSD some problems is not having the +ghost user expired in the persistent cache but not in the timestamp +cache. + +With this patch, the entry is also expired in the timestamp cache when +it's present. + +Related: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 9883d1e2913ff0c1db479f1ece8148e03155c7f3) +--- + src/db/sysdb_ops.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 12f8095d2edc60ffab09c92d64f968892c577bbf..ae26470487f859fe1de1dc364b6a05b9793a0545 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -5065,6 +5065,15 @@ errno_t sysdb_mark_entry_as_expired_ldb_dn(struct sss_domain_info *dom, + goto done; + } + ++ if (dom->sysdb->ldb_ts != NULL) { ++ ret = ldb_modify(dom->sysdb->ldb_ts, msg); ++ if (ret != LDB_SUCCESS) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Could not mark an entry as expired in the timestamp cache\n"); ++ /* non-fatal */ ++ } ++ } ++ + ret = EOK; + + done: +-- +2.9.4 + diff --git a/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch b/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch new file mode 100644 index 0000000..e6512c2 --- /dev/null +++ b/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch @@ -0,0 +1,49 @@ +From 256e1b4162832570e10a85579d2b14ed7b54b7f2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 29 May 2017 13:29:26 +0200 +Subject: [PATCH 179/181] SYSDB_OPS: Invalidate a cache entry also in the + ts_cache +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Similarly to what has been in the previous commit (expiring an entry +also in the timestamp cache), we should do the same when invalidating an +entry. + +Related: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit a71f1a655dcc2ca6dc16bb8eb1c4c9e24cfe2c3e) +--- + src/db/sysdb_ops.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index ae26470487f859fe1de1dc364b6a05b9793a0545..ed936f0cb1a37155aabef96db1d267eb03ec0ed9 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -5160,6 +5160,17 @@ int sysdb_invalidate_cache_entry(struct sss_domain_info *domain, + goto done; + } + ++ if (sysdb->ldb_ts != NULL) { ++ ret = sysdb_set_cache_entry_attr(sysdb->ldb_ts, entry_dn, ++ attrs, SYSDB_MOD_REP); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot set attrs in the timestamp cache for %s, %d [%s]\n", ++ ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret)); ++ /* non-fatal */ ++ } ++ } ++ + DEBUG(SSSDBG_FUNC_DATA, + "Cache entry [%s] has been invalidated.\n", + ldb_dn_get_linearized(entry_dn)); +-- +2.9.4 + diff --git a/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch b/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch new file mode 100644 index 0000000..5bfa491 --- /dev/null +++ b/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch @@ -0,0 +1,278 @@ +From 11c34233ac7385c6f2a65c5cc57dfefb1cae48cd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 7 Jun 2017 15:07:10 +0200 +Subject: [PATCH 180/181] SYSDB: Introduce + _search_{users,groups}_by_timestamp() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +These new two sysdb methods are going to be used, at least for now, +uniquely and exclusively in the cleanup task. + +The reason for adding those is that during the cleanup task a timestamp +search is done in the persistent cache, which doesn't have the updated +timestamps, returning then a wrong result that ends up in having all the +users being removed from the cache. + +The persistent cache doesn't have its entries' timestamps updated +because those are kept updated in the timestamp cache, therefore these +new two methods end up doing: +- if the timestamp cache is present: + - search for the entries solely in the timestamp cache; + - get the needed attributes from these entries from the persistent + cache; +- otherwise: + - search for the entries in the persistent cache; + - merge its results with timestamp cache's results; + +Related: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 41708e1e500e7cada3d3e606aa2b8b9869a5c734) +--- + src/db/sysdb.h | 14 +++++ + src/db/sysdb_ops.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 192 insertions(+) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 62c561be9452a284a8ddf8ebb45720265852c8b0..21d6cf4fc90a050e203e1609be5ee267a618dda9 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -1142,6 +1142,13 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msgs); + ++int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ const char *sub_filter, ++ const char **attrs, ++ size_t *_msgs_count, ++ struct ldb_message ***_msgs); ++ + int sysdb_delete_user(struct sss_domain_info *domain, + const char *name, uid_t uid); + +@@ -1152,6 +1159,13 @@ int sysdb_search_groups(TALLOC_CTX *mem_ctx, + size_t *msgs_count, + struct ldb_message ***msgs); + ++int sysdb_search_groups_by_timestamp(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ const char *sub_filter, ++ const char **attrs, ++ size_t *_msgs_count, ++ struct ldb_message ***_msgs); ++ + int sysdb_delete_group(struct sss_domain_info *domain, + const char *name, gid_t gid); + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index ed936f0cb1a37155aabef96db1d267eb03ec0ed9..7ca6575ce75dab7805236c9f48dbf28a2f3946d2 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -374,6 +374,58 @@ enum sysdb_obj_type { + SYSDB_GROUP + }; + ++static errno_t cleanup_dn_filter(TALLOC_CTX *mem_ctx, ++ struct ldb_result *ts_res, ++ const char *object_class, ++ const char *filter, ++ char **_dn_filter) ++{ ++ TALLOC_CTX *tmp_ctx; ++ char *dn_filter; ++ errno_t ret; ++ ++ if (ts_res->count == 0) { ++ *_dn_filter = NULL; ++ return EOK; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn_filter = talloc_asprintf(tmp_ctx, "(&(%s)%s(|", object_class, filter); ++ if (dn_filter == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (size_t i = 0; i < ts_res->count; i++) { ++ dn_filter = talloc_asprintf_append( ++ dn_filter, ++ "(%s=%s)", ++ SYSDB_DN, ++ ldb_dn_get_linearized(ts_res->msgs[i]->dn)); ++ if (dn_filter == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ dn_filter = talloc_asprintf_append(dn_filter, "))"); ++ if (dn_filter == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ *_dn_filter = talloc_steal(mem_ctx, dn_filter); ++ ret = EOK; ++ ++done: ++ talloc_zfree(tmp_ctx); ++ return ret; ++} ++ + static int sysdb_search_by_name(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name, +@@ -3503,6 +3555,69 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx, + attrs); + } + ++int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ const char *sub_filter, ++ const char **attrs, ++ size_t *_msgs_count, ++ struct ldb_message ***_msgs) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_result *res; ++ struct ldb_result ts_res; ++ struct ldb_message **msgs; ++ size_t msgs_count; ++ char *dn_filter = NULL; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sysdb_search_ts_users(tmp_ctx, domain, sub_filter, NULL, &ts_res); ++ if (ret == ERR_NO_TS) { ++ ret = sysdb_cache_search_users(tmp_ctx, domain, domain->sysdb->ldb, ++ sub_filter, attrs, &msgs_count, &msgs); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ goto immediately; ++ } else if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = cleanup_dn_filter(tmp_ctx, &ts_res, SYSDB_UC, sub_filter, &dn_filter); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, ++ &ts_res, dn_filter, &res); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ msgs_count = res->count; ++ msgs = res->msgs; ++ ++immediately: ++ *_msgs_count = msgs_count; ++ *_msgs = talloc_steal(mem_ctx, msgs); ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ + int sysdb_search_ts_users(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, +@@ -3720,6 +3835,69 @@ int sysdb_search_groups(TALLOC_CTX *mem_ctx, + attrs); + } + ++int sysdb_search_groups_by_timestamp(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ const char *sub_filter, ++ const char **attrs, ++ size_t *_msgs_count, ++ struct ldb_message ***_msgs) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_result *res; ++ struct ldb_result ts_res; ++ struct ldb_message **msgs; ++ size_t msgs_count; ++ char *dn_filter = NULL; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sysdb_search_ts_groups(tmp_ctx, domain, sub_filter, NULL, &ts_res); ++ if (ret == ERR_NO_TS) { ++ ret = sysdb_cache_search_groups(tmp_ctx, domain, domain->sysdb->ldb, ++ sub_filter, attrs, &msgs_count, &msgs); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ goto immediately; ++ } else if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = cleanup_dn_filter(tmp_ctx, &ts_res, SYSDB_GC, sub_filter, &dn_filter); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, ++ &ts_res, dn_filter, &res); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ msgs_count = res->count; ++ msgs = res->msgs; ++ ++immediately: ++ *_msgs_count = msgs_count; ++ *_msgs = talloc_steal(mem_ctx, msgs); ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ + int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, +-- +2.9.4 + diff --git a/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch b/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch new file mode 100644 index 0000000..7bba059 --- /dev/null +++ b/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch @@ -0,0 +1,48 @@ +From b96c69f0ab0ecd55b734c167763c3bfe2357c448 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Wed, 7 Jun 2017 15:17:15 +0200 +Subject: [PATCH 181/181] LDAP_ID_CLEANUP: Use sysdb_search_*_by_timestamp() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use the appropriate methods for searching users and groups bv timestamp. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3369 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 05e579691b51ac2f81ab0c828ff6fe57bd86a8b6) +--- + src/providers/ldap/ldap_id_cleanup.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c +index cde2ad81873d46edd5e05c4a701ea1742a012bd0..c85ce45918cf938a95ff85c31bfe0541f9ddd052 100644 +--- a/src/providers/ldap/ldap_id_cleanup.c ++++ b/src/providers/ldap/ldap_id_cleanup.c +@@ -219,7 +219,8 @@ static int cleanup_users(struct sdap_options *opts, + goto done; + } + +- ret = sysdb_search_users(tmpctx, dom, subfilter, attrs, &count, &msgs); ++ ret = sysdb_search_users_by_timestamp(tmpctx, dom, subfilter, attrs, ++ &count, &msgs); + if (ret == ENOENT) { + count = 0; + } else if (ret != EOK) { +@@ -394,7 +395,8 @@ static int cleanup_groups(TALLOC_CTX *memctx, + goto done; + } + +- ret = sysdb_search_groups(tmpctx, domain, subfilter, attrs, &count, &msgs); ++ ret = sysdb_search_groups_by_timestamp(tmpctx, domain, subfilter, attrs, ++ &count, &msgs); + if (ret == ENOENT) { + count = 0; + } else if (ret != EOK) { +-- +2.9.4 + diff --git a/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch b/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch new file mode 100644 index 0000000..4802ef1 --- /dev/null +++ b/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch @@ -0,0 +1,66 @@ +From b7aa85ea053aa78fa23de98d6c48e155f0cc06bc Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 12 Jun 2017 14:42:47 +0200 +Subject: [PATCH 182/182] krb5: use plain principal if password is expired +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Similar as in https://pagure.io/SSSD/sssd/issue/3426 enterprise +principals should be avoided while requesting a kadmin/changepw@REALM +principal for a password change. + +Resolves https://pagure.io/SSSD/sssd/issue/3419 + +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 7e2ec7caa2d1c17e475fff78c5025496b8695509) +--- + src/providers/krb5/krb5_child.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index 3cd8bfba76a35acd2c885ee2aac4765a6c1cc03c..3a76b900444dea50ec0b783496e22d25aad797ab 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -64,6 +64,7 @@ struct cli_opts { + struct krb5_req { + krb5_context ctx; + krb5_principal princ; ++ krb5_principal princ_orig; + char* name; + krb5_creds *creds; + bool otp; +@@ -1975,7 +1976,7 @@ static errno_t tgt_req_child(struct krb5_req *kr) + } + + set_changepw_options(kr->options); +- kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, ++ kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ_orig, + password_or_responder(password), + sss_krb5_prompter, kr, 0, + SSSD_KRB5_CHANGEPW_PRINCIPAL, +@@ -2303,6 +2304,8 @@ static int krb5_cleanup(struct krb5_req *kr) + sss_krb5_free_unparsed_name(kr->ctx, kr->name); + if (kr->princ != NULL) + krb5_free_principal(kr->ctx, kr->princ); ++ if (kr->princ_orig != NULL) ++ krb5_free_principal(kr->ctx, kr->princ_orig); + if (kr->ctx != NULL) + krb5_free_context(kr->ctx); + +@@ -2847,6 +2850,12 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline) + return kerr; + } + ++ kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ_orig); ++ if (kerr != 0) { ++ KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); ++ return kerr; ++ } ++ + kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); +-- +2.9.4 + diff --git a/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch b/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch new file mode 100644 index 0000000..1af69a1 --- /dev/null +++ b/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch @@ -0,0 +1,279 @@ +From 48b30d5a62e6af3d1f2b28eac3a2d39efa4349f1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 19 Jun 2017 09:05:00 +0200 +Subject: [PATCH 183/186] RESPONDER: Use fqnames as output when needed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As some regressions have been caused by not handling properly naming +conflicts when using shortnames, last explicitly use fully qualified +names as output in the following situations: +- domain resolution order is set; +- a trusted domain has been using `use_fully_qualified_name = false` + +In both cases we want to ensure that even handling shortnames as input, +the output will always be fully qualified. + +As part of this patch, our tests ended up being modified to reflect the +changes done. In other words, the tests related to shortnames now return +expect as return a fully qualified name for trusted domains. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3403 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 86526891366c4bc3e1ee861143b736d2670a6ba8) +--- + src/confdb/confdb.h | 1 + + src/db/sysdb_subdomains.c | 7 ++ + src/responder/common/cache_req/cache_req_domain.c | 14 +++ + src/responder/common/cache_req/cache_req_domain.h | 8 ++ + src/tests/cmocka/test_nss_srv.c | 104 +++++++++------------- + src/util/usertools.c | 2 +- + 6 files changed, 72 insertions(+), 64 deletions(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 797353141edcccbf3341d161ca598c99492e54fe..32a422155abef428e8a75fc83a5fe14620c7028e 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -291,6 +291,7 @@ struct sss_domain_info { + bool enumerate; + char **sd_enumerate; + bool fqnames; ++ bool output_fqnames; + bool mpg; + bool ignore_group_members; + uint32_t id_min; +diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c +index e2a4f7bb1fcdf20b6b7e04efc7f396d1c3d08f0f..2789cc4949fb7be9ad272d7613ed18a64fa8a20a 100644 +--- a/src/db/sysdb_subdomains.c ++++ b/src/db/sysdb_subdomains.c +@@ -129,6 +129,13 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + dom->mpg = mpg; + dom->state = DOM_ACTIVE; + ++ /* use fully qualified names as output in order to avoid causing ++ * conflicts with users who have the same name and either the ++ * shortname user resolution is enabled or the trusted domain has ++ * been explicitly set to use non-fully qualified names as input. ++ */ ++ dom->output_fqnames = true; ++ + /* If the parent domain filters out group members, the subdomain should + * as well if configured */ + inherit_option = string_in_list(CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS, +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index 2c238c9966d322bb542fa2047313ee9e5144edee..b5f7f6c2ffabdbd92ee46b3020cee6ef7fec32d8 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -136,6 +136,12 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + cr_domain->fqnames = + cache_req_domain_use_fqnames(dom, enforce_non_fqnames); + ++ /* when using the domain resolution order, using shortnames as ++ * input is allowed by default. However, we really want to use ++ * the fully qualified name as output in order to avoid ++ * conflicts whith users who have the very same name. */ ++ cr_domain->domain->output_fqnames = true; ++ + DLIST_ADD_END(cr_domains, cr_domain, + struct cache_req_domain *); + break; +@@ -159,6 +165,14 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + cr_domain->fqnames = + cache_req_domain_use_fqnames(dom, enforce_non_fqnames); + ++ /* when using the domain resolution order, using shortnames as input ++ * is allowed by default. However, we really want to use the fully ++ * qualified name as output in order to avoid conflicts whith users ++ * who have the very same name. */ ++ if (resolution_order != NULL) { ++ cr_domain->domain->output_fqnames = true; ++ } ++ + DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); + } + +diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h +index 5bcbb9b493caf05bf71aac5cf7633ded91f22e73..3780a5d8d88d76e100738d28d1dd0e697edf5eae 100644 +--- a/src/responder/common/cache_req/cache_req_domain.h ++++ b/src/responder/common/cache_req/cache_req_domain.h +@@ -35,6 +35,14 @@ struct cache_req_domain * + cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, + const char *name); + ++/* ++ * This function may have a side effect of setting the output_fqnames' domain ++ * property when it's called. ++ * ++ * It happens as the output_fqnames' domain property must only be set depending ++ * on whether a domain resolution order is set or not, and the saner place to ++ * set it to all domains is when flattening those (thus, in this function). ++ */ + errno_t + cache_req_domain_new_list_from_domain_resolution_order( + TALLOC_CTX *mem_ctx, +diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c +index 03b5bcc302322551a32f5b8cfe4b7698947abbe7..ccedf96beaecfaa4232bbe456d5e5a8394098483 100644 +--- a/src/tests/cmocka/test_nss_srv.c ++++ b/src/tests/cmocka/test_nss_srv.c +@@ -1648,29 +1648,23 @@ static int test_nss_getgrnam_members_check_subdom(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- if (nss_test_ctx->subdom->fqnames) { +- exp_members[0] = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- submember1.pw_name); +- assert_non_null(exp_members[0]); ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); + +- exp_members[1] = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- submember2.pw_name); +- assert_non_null(exp_members[1]); ++ exp_members[1] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember2.pw_name); ++ assert_non_null(exp_members[1]); + +- expected.gr_name = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- testsubdomgroup.gr_name); +- assert_non_null(expected.gr_name); +- } else { +- exp_members[0] = submember1.pw_name; +- exp_members[1] = submember2.pw_name; +- expected.gr_name = testsubdomgroup.gr_name; +- } ++ expected.gr_name = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ testsubdomgroup.gr_name); ++ assert_non_null(expected.gr_name); + + assert_int_equal(status, EOK); + +@@ -1744,15 +1738,11 @@ static int test_nss_getgrnam_check_mix_dom(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- if (nss_test_ctx->subdom->fqnames) { +- exp_members[0] = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- submember1.pw_name); +- assert_non_null(exp_members[0]); +- } else { +- exp_members[0] = submember1.pw_name; +- } ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); + exp_members[1] = testmember1.pw_name; + exp_members[2] = testmember2.pw_name; + +@@ -1840,15 +1830,12 @@ static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- if (nss_test_ctx->subdom->fqnames) { +- exp_members[0] = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- submember1.pw_name); +- assert_non_null(exp_members[0]); +- } else { +- exp_members[0] = submember1.pw_name; +- } ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); ++ + if (nss_test_ctx->tctx->dom->fqnames) { + exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names, + nss_test_ctx->tctx->dom, testmember1.pw_name); +@@ -1961,37 +1948,28 @@ static int test_nss_getgrnam_check_mix_subdom(uint32_t status, + tmp_ctx = talloc_new(nss_test_ctx); + assert_non_null(tmp_ctx); + +- if (nss_test_ctx->subdom->fqnames) { +- exp_members[0] = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- submember1.pw_name); +- assert_non_null(exp_members[0]); ++ exp_members[0] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember1.pw_name); ++ assert_non_null(exp_members[0]); + +- exp_members[1] = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- submember2.pw_name); +- assert_non_null(exp_members[1]); +- } else { +- exp_members[0] = submember1.pw_name; +- exp_members[1] = submember2.pw_name; +- } ++ exp_members[1] = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ submember2.pw_name); ++ assert_non_null(exp_members[1]); + + /* Important: this member is from a non-qualified domain, so his name will + * not be qualified either + */ + exp_members[2] = testmember1.pw_name; + +- if (nss_test_ctx->subdom->fqnames) { +- expected.gr_name = sss_tc_fqname(tmp_ctx, +- nss_test_ctx->subdom->names, +- nss_test_ctx->subdom, +- testsubdomgroup.gr_name); +- assert_non_null(expected.gr_name); +- } else { +- expected.gr_name = testsubdomgroup.gr_name; +- } ++ expected.gr_name = sss_tc_fqname(tmp_ctx, ++ nss_test_ctx->subdom->names, ++ nss_test_ctx->subdom, ++ testsubdomgroup.gr_name); ++ assert_non_null(expected.gr_name); + + assert_int_equal(status, EOK); + +diff --git a/src/util/usertools.c b/src/util/usertools.c +index 5dfe6d7765b8032c7447de75e10c6c2a1d4c49ec..83131da1cac25e60a5ec3fffa995a545673e53b9 100644 +--- a/src/util/usertools.c ++++ b/src/util/usertools.c +@@ -867,7 +867,7 @@ int sss_output_fqname(TALLOC_CTX *mem_ctx, + goto done; + } + +- if (domain->fqnames) { ++ if (domain->output_fqnames || domain->fqnames) { + output_name = sss_tc_fqname(tmp_ctx, domain->names, + domain, output_name); + if (output_name == NULL) { +-- +2.9.4 + diff --git a/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch b/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch new file mode 100644 index 0000000..e6bd53b --- /dev/null +++ b/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch @@ -0,0 +1,128 @@ +From 3fc92dcfbd67f82d26d7db46026f1fa1b69e2c70 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Tue, 20 Jun 2017 14:22:48 +0200 +Subject: [PATCH 184/186] DOMAIN: Add + sss_domain_info_{get,set}_output_fqnames() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Let's avoid setting a domain's property directly from cr_domain code. + +In order to do so, let's introduce a setter, which may help us in the +future whenever we decide to make sss_domain_info an opaque structure. + +For completeness, a getter has also been introduced and used in the +usertools code. + +Related: +https://pagure.io/SSSD/sssd/issue/3403 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Jakub Hrozek +(cherry picked from commit fa2fc8a2908619031292eaf375eb1a510b8b2eba) +--- + src/confdb/confdb.h | 5 ++++- + src/responder/common/cache_req/cache_req_domain.c | 4 ++-- + src/util/domain_info_utils.c | 11 +++++++++++ + src/util/usertools.c | 2 +- + src/util/util.h | 5 +++++ + 5 files changed, 23 insertions(+), 4 deletions(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 32a422155abef428e8a75fc83a5fe14620c7028e..2ba1bc47ee11f699726cefaf7c3335d2a8afee49 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -291,7 +291,6 @@ struct sss_domain_info { + bool enumerate; + char **sd_enumerate; + bool fqnames; +- bool output_fqnames; + bool mpg; + bool ignore_group_members; + uint32_t id_min; +@@ -355,6 +354,10 @@ struct sss_domain_info { + + struct certmap_info **certmaps; + bool user_name_hint; ++ ++ /* Do not use the _output_fqnames property directly in new code, but rather ++ * use sss_domain_info_{get,set}_output_fqnames(). */ ++ bool output_fqnames; + }; + + /** +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index b5f7f6c2ffabdbd92ee46b3020cee6ef7fec32d8..c2b5abb74f3bd3d5055f29a4523f29b05feb2014 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -140,7 +140,7 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + * input is allowed by default. However, we really want to use + * the fully qualified name as output in order to avoid + * conflicts whith users who have the very same name. */ +- cr_domain->domain->output_fqnames = true; ++ sss_domain_info_set_output_fqnames(cr_domain->domain, true); + + DLIST_ADD_END(cr_domains, cr_domain, + struct cache_req_domain *); +@@ -170,7 +170,7 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx, + * qualified name as output in order to avoid conflicts whith users + * who have the very same name. */ + if (resolution_order != NULL) { +- cr_domain->domain->output_fqnames = true; ++ sss_domain_info_set_output_fqnames(cr_domain->domain, true); + } + + DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index 541058a16d585155b3b51511740f7db45281e2fd..45c74f089d0fdeaf6b5b50d7e5058df1716ff777 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -904,3 +904,14 @@ const char *sss_domain_type_str(struct sss_domain_info *dom) + } + return "Unknown"; + } ++ ++void sss_domain_info_set_output_fqnames(struct sss_domain_info *domain, ++ bool output_fqnames) ++{ ++ domain->output_fqnames = output_fqnames; ++} ++ ++bool sss_domain_info_get_output_fqnames(struct sss_domain_info *domain) ++{ ++ return domain->output_fqnames; ++} +diff --git a/src/util/usertools.c b/src/util/usertools.c +index 83131da1cac25e60a5ec3fffa995a545673e53b9..33f4f7811c843704fff32db3a9ac54b3438f9d37 100644 +--- a/src/util/usertools.c ++++ b/src/util/usertools.c +@@ -867,7 +867,7 @@ int sss_output_fqname(TALLOC_CTX *mem_ctx, + goto done; + } + +- if (domain->output_fqnames || domain->fqnames) { ++ if (sss_domain_info_get_output_fqnames(domain) || domain->fqnames) { + output_name = sss_tc_fqname(tmp_ctx, domain->names, + domain, output_name); + if (output_name == NULL) { +diff --git a/src/util/util.h b/src/util/util.h +index 5ba4c36ca88e325c20a3b1ecc8080a11ca276dcf..72d4116e1206e9cc69715edc45bf5b9b91e37e6b 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -571,6 +571,11 @@ errno_t sssd_domain_init(TALLOC_CTX *mem_ctx, + const char *db_path, + struct sss_domain_info **_domain); + ++void sss_domain_info_set_output_fqnames(struct sss_domain_info *domain, ++ bool output_fqname); ++ ++bool sss_domain_info_get_output_fqnames(struct sss_domain_info *domain); ++ + #define IS_SUBDOMAIN(dom) ((dom)->parent != NULL) + + #define DOM_HAS_VIEWS(dom) ((dom)->has_views) +-- +2.9.4 + diff --git a/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch b/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch new file mode 100644 index 0000000..fa94e3d --- /dev/null +++ b/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch @@ -0,0 +1,27 @@ +From ab8afcc8befcfa436008da41944cf258513631e6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Wed, 7 Jun 2017 14:37:42 +0200 +Subject: [PATCH 185/186] GPO: Fix typo in DEBUG message + +Reviewed-by: Jakub Hrozek +(cherry picked from commit b1d34059533eb50f6e5a4ac7b6fa1bb6fa60a445) +--- + src/providers/ad/ad_gpo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c +index a8196b4d637eff86a01b342821592bffc214f1ab..2ee284bdc71fcec1c73997f785f7c2c7f387f0b3 100644 +--- a/src/providers/ad/ad_gpo.c ++++ b/src/providers/ad/ad_gpo.c +@@ -2110,7 +2110,7 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq) + &state->num_dacl_filtered_gpos); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, +- "Unable to filter GPO list by DACKL: [%d](%s)\n", ++ "Unable to filter GPO list by DACL: [%d](%s)\n", + ret, sss_strerror(ret)); + goto done; + } +-- +2.9.4 + diff --git a/SOURCES/0186-SDAP-Update-parent-sdap_list.patch b/SOURCES/0186-SDAP-Update-parent-sdap_list.patch new file mode 100644 index 0000000..431f35a --- /dev/null +++ b/SOURCES/0186-SDAP-Update-parent-sdap_list.patch @@ -0,0 +1,97 @@ +From 69b69d84ca9fd3453fa83281fc90e34f413a32f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20=C5=BDidek?= +Date: Wed, 14 Jun 2017 19:02:10 +0200 +Subject: [PATCH 186/186] SDAP: Update parent sdap_list + +Update parent sdap_list with newly created subdomain sdap domain. + +Preiously, we inherited the parent sdap_list and used it also in the +subdomain's context (this was introduced recently with commit +c4ddb9ccab670f9c0d0377680237b62f9f91c496), but it caused problems +that were difficult to debug (we somewhere rewrite part of the list +incorrectly). + +This patch reverses to the previous bahavior, where every subdomain +has it's own sdap_list, however this time the parrent domain's +sdap_list is updated so that it has correct information about +search bases of the child domains. + +We should ideally have just one sdap_list to avoid the updating +completely, but this would require more refactoring in the sdap +code. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3421 + +Reviewed-by: Jakub Hrozek +(cherry picked from commit 630aea13063c4b242b3433d16ca4346a1a38429b) +--- + src/providers/ad/ad_subdomains.c | 38 +++++++++++++++++++++++++++++++++++--- + 1 file changed, 35 insertions(+), 3 deletions(-) + +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index c9b79dd9d6840802cddc067eef9d5110cf8d0778..e35041c5ad73cb0fcaaaad96333fc17dd3a17638 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -141,6 +141,35 @@ static bool is_domain_enabled(const char *domain, + } + + static errno_t ++update_parent_sdap_list(struct sdap_domain *parent_list, ++ struct sdap_domain *child_sdap) ++{ ++ struct sdap_domain *sditer; ++ ++ DLIST_FOR_EACH(sditer, parent_list) { ++ if (sditer->dom == child_sdap->dom) { ++ break; ++ } ++ } ++ ++ if (sditer == NULL) { ++ /* Nothing to do */ ++ return EOK; ++ } ++ ++ /* Update the search bases */ ++ sditer->search_bases = child_sdap->search_bases; ++ sditer->user_search_bases = child_sdap->user_search_bases; ++ sditer->group_search_bases = child_sdap->group_search_bases; ++ sditer->netgroup_search_bases = child_sdap->netgroup_search_bases; ++ sditer->sudo_search_bases = child_sdap->sudo_search_bases; ++ sditer->service_search_bases = child_sdap->service_search_bases; ++ sditer->autofs_search_bases = child_sdap->autofs_search_bases; ++ ++ return EOK; ++} ++ ++static errno_t + ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct sss_domain_info *subdom, +@@ -221,9 +250,6 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + ad_id_ctx->sdap_id_ctx->opts = ad_options->id; + ad_options->id_ctx = ad_id_ctx; + +- /* We need to pass the sdap list from parent */ +- ad_id_ctx->sdap_id_ctx->opts->sdom = id_ctx->sdap_id_ctx->opts->sdom; +- + /* use AD plugin */ + srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res, + default_host_dbs, +@@ -267,6 +293,12 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + "bases.", subdom->name); + } + ++ ret = update_parent_sdap_list(id_ctx->sdap_id_ctx->opts->sdom, ++ sdom); ++ if (ret != EOK) { ++ return ret; ++ } ++ + *_subdom_id_ctx = ad_id_ctx; + return EOK; + } +-- +2.9.4 + diff --git a/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch b/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch new file mode 100644 index 0000000..724b886 --- /dev/null +++ b/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch @@ -0,0 +1,84 @@ +From d6c7d35fdb4d416360a855a37b8c2164f053b470 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 11 Jul 2017 18:26:01 +0200 +Subject: [PATCH 187/190] RESPONDERS: Fix terminating idle connections +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The client_idle_handler() function tried to schedule another tevent +timer to check for idle client connections in case the current +connection was still valid, but in doing so, it also stored the current +time into the last_request_time field of the client context. + +This kept the connection always alive, because the last_request_time +could then never be older than the timeout. + +This patch changes the setup_client_idle_timer() function to only do +what the synopsis says and set the idle timer. The caller (usually the +function that accepts the connection) is supposed to store the request +time itself. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3448 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Fabiano Fidêncio +--- + src/responder/common/responder_common.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 9d4889be652c6d6fb974b59001a9ac77b496e9ab..9d706f9799ef1b31122d8380fbf9c53ba0cc9e68 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -607,7 +607,15 @@ static void accept_fd_handler(struct tevent_context *ev, + cctx->ev = ev; + cctx->rctx = rctx; + +- /* Set up the idle timer */ ++ /* Record the new time and set up the idle timer */ ++ ret = reset_client_idle_timer(cctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Could not create idle timer for client. " ++ "This connection may not auto-terminate\n"); ++ /* Non-fatal, continue */ ++ } ++ + ret = setup_client_idle_timer(cctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, +@@ -634,7 +642,7 @@ static void client_idle_handler(struct tevent_context *ev, + if (cctx->last_request_time > now) { + DEBUG(SSSDBG_IMPORTANT_INFO, + "Time shift detected, re-scheduling the client timeout\n"); +- goto end; ++ goto done; + } + + if ((now - cctx->last_request_time) > cctx->rctx->client_idle_timeout) { +@@ -648,7 +656,7 @@ static void client_idle_handler(struct tevent_context *ev, + return; + } + +-end: ++done: + setup_client_idle_timer(cctx); + } + +@@ -661,11 +669,9 @@ errno_t reset_client_idle_timer(struct cli_ctx *cctx) + + static errno_t setup_client_idle_timer(struct cli_ctx *cctx) + { +- time_t now = time(NULL); + struct timeval tv = + tevent_timeval_current_ofs(cctx->rctx->client_idle_timeout/2, 0); + +- cctx->last_request_time = now; + talloc_zfree(cctx->idle); + + cctx->idle = tevent_add_timer(cctx->ev, cctx, tv, client_idle_handler, cctx); +-- +2.9.4 + diff --git a/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch b/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch new file mode 100644 index 0000000..c587a0f --- /dev/null +++ b/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch @@ -0,0 +1,135 @@ +From fd008eddbf069014d8f17944d018ad3d85d5679f Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 19 Jul 2017 14:22:17 +0200 +Subject: [PATCH 188/190] TESTS: Integration test for idle timeout +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The secrets responder test was chosen even though the bug was in the generic +responder code b/c it runs a single responder process, so it's trivial to +read the PID of the responder under test. + +Changes subprocess.call() for os.execv() so that the setup function can +return the secret responder PID right away. + +The client timeout in the test has to be at least 10 seconds because +internally, the responders don't allow a shorter timeout. + +Regression test for https://pagure.io/SSSD/sssd/issue/3448 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Fabiano Fidêncio +--- + src/tests/intg/test_secrets.py | 75 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 62 insertions(+), 13 deletions(-) + +diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py +index 202f43e61cb0e4986394ad2b32da5abdcb0be3e9..1be31318b194de1550bc84af16260bf1503567dc 100644 +--- a/src/tests/intg/test_secrets.py ++++ b/src/tests/intg/test_secrets.py +@@ -55,9 +55,9 @@ def create_sssd_secrets_fixture(request): + assert secpid >= 0 + + if secpid == 0: +- if subprocess.call([resp_path, "--uid=0", "--gid=0"]) != 0: +- print("sssd_secrets failed to start") +- sys.exit(99) ++ os.execv(resp_path, ("--uid=0", "--gid=0")) ++ print("sssd_secrets failed to start") ++ sys.exit(99) + else: + sock_path = os.path.join(config.RUNSTATEDIR, "secrets.socket") + sck = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +@@ -83,13 +83,8 @@ def create_sssd_secrets_fixture(request): + return secpid + + +-@pytest.fixture +-def setup_for_secrets(request): +- """ +- Just set up the local provider for tests and enable the secrets +- responder +- """ +- conf = unindent("""\ ++def generate_sec_config(): ++ return unindent("""\ + [sssd] + domains = local + services = nss +@@ -100,11 +95,19 @@ def setup_for_secrets(request): + [secrets] + max_secrets = 10 + max_payload_size = 2 +- """).format(**locals()) ++ """) ++ ++ ++@pytest.fixture ++def setup_for_secrets(request): ++ """ ++ Just set up the local provider for tests and enable the secrets ++ responder ++ """ ++ conf = generate_sec_config() + + create_conf_fixture(request, conf) +- create_sssd_secrets_fixture(request) +- return None ++ return create_sssd_secrets_fixture(request) + + + def get_secrets_socket(): +@@ -386,3 +389,49 @@ def test_containers(setup_for_secrets, secrets_cli): + with pytest.raises(HTTPError) as err406: + cli.create_container(container) + assert str(err406.value).startswith("406") ++ ++ ++def get_num_fds(pid): ++ procpath = os.path.join("/proc/", str(pid), "fd") ++ return len([fdname for fdname in os.listdir(procpath)]) ++ ++ ++@pytest.fixture ++def setup_for_cli_timeout_test(request): ++ """ ++ Same as the generic setup, except a short client_idle_timeout so that ++ the test_idle_timeout() test closes the fd towards the client. ++ """ ++ conf = generate_sec_config() + \ ++ unindent(""" ++ client_idle_timeout = 10 ++ """).format() ++ ++ create_conf_fixture(request, conf) ++ return create_sssd_secrets_fixture(request) ++ ++ ++def test_idle_timeout(setup_for_cli_timeout_test): ++ """ ++ Test that idle file descriptors are reaped after the idle timeout ++ passes ++ """ ++ secpid = setup_for_cli_timeout_test ++ sock_path = get_secrets_socket() ++ ++ nfds_pre = get_num_fds(secpid) ++ ++ sock = socket.socket(family=socket.AF_UNIX) ++ sock.connect(sock_path) ++ time.sleep(1) ++ nfds_conn = get_num_fds(secpid) ++ assert nfds_pre + 1 == nfds_conn ++ # With the idle timeout set to 10 seconds, we need to sleep at least 15, ++ # because the internal timer ticks every timeout/2 seconds, so it would ++ # tick at 5, 10 and 15 seconds and the client timeout check uses a ++ # greater-than comparison, so the 10-seconds tick wouldn't yet trigger ++ # disconnect ++ time.sleep(15) ++ ++ nfds_post = get_num_fds(secpid) ++ assert nfds_pre == nfds_post +-- +2.9.4 + diff --git a/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch b/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch new file mode 100644 index 0000000..e353c96 --- /dev/null +++ b/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch @@ -0,0 +1,36 @@ +From 0442102b2e5c6f1bc331ca2078baff8a7b4a50cb Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 20 Jul 2017 10:10:58 +0200 +Subject: [PATCH 189/190] MAN: Document that client_idle_timeout can't be + shorter than 10 seconds +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To ensure the client timeout is not too low and clients do not reconnect +too often, the client_idle_timeout is forced to be 10 seconds minimum. + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Fabiano Fidêncio +--- + src/man/sssd.conf.5.xml | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index a35f2807eac8bb89d6cb1dd0a48f738d71a7578f..89729575c724622af817f1c05a94d4ae8f1ece2d 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -621,7 +621,9 @@ + a client of an SSSD process can hold onto a file + descriptor without communicating on it. This value + is limited in order to avoid resource exhaustion +- on the system. ++ on the system. The timeout can't be shorter than ++ 10 seconds. If a lower value is configured, it ++ will be adjusted to 10 seconds. + + + Default: 60 +-- +2.9.4 + diff --git a/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch b/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch new file mode 100644 index 0000000..c652a4d --- /dev/null +++ b/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch @@ -0,0 +1,38 @@ +From 55e8b436443cfae1c3b2155be7325d53760f7271 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 20 Jul 2017 20:01:14 +0200 +Subject: [PATCH 190/190] ad_account_can_shortcut: shortcut if ID is unknown +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If sss_idmap_unix_to_sid() returns an error we can assume that the given +POSIX ID is not from the current domain and can be skipped. This is e.g. +the case in the IPA provider if a POSIX ID used in the IPA domain is +checked in a trusted id-mapped AD domain before the IPA domain is +checked. + +Resolves https://pagure.io/SSSD/sssd/issue/3452 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Jakub Hrozek +--- + src/providers/ad/ad_id.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c +index d1f6c444f5ddbcbbac6ff7f41fb6c8bf9ca976cb..e14ada386f16851a65097952c85e57b7acda14aa 100644 +--- a/src/providers/ad/ad_id.c ++++ b/src/providers/ad/ad_id.c +@@ -86,6 +86,8 @@ static bool ad_account_can_shortcut(struct sdap_idmap_ctx *idmap_ctx, + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: " + "[%s]\n", filter_value, idmap_error_string(err)); ++ /* assume id is from a different domain */ ++ shortcut = true; + goto done; + } + /* fall through */ +-- +2.9.4 + diff --git a/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch b/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch new file mode 100644 index 0000000..54279f3 --- /dev/null +++ b/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch @@ -0,0 +1,211 @@ +From 5c159808818fcea77822815b5f1131809c0e673c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 11 Jul 2017 12:41:57 +0200 +Subject: [PATCH 191/191] sudo: add a threshold option to reduce size of rules + refresh filter +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If a large number of rules is expired at one time the ldap filter may +become too large to be processed by server. This commits adds a new +option "sudo_threshold" to sudo responder. If the threshold is +exceeded a full refreshed is done instead of rules refresh. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3478 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit a5f300adf19ec9c3087c62bd93a5175db799687a) +--- + src/confdb/confdb.h | 2 ++ + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.conf | 1 + + src/man/sssd.conf.5.xml | 19 +++++++++++++++++++ + src/responder/sudo/sudosrv.c | 11 +++++++++++ + src/responder/sudo/sudosrv_get_sudorules.c | 25 ++++++++++++++++++++----- + src/responder/sudo/sudosrv_private.h | 1 + + 8 files changed, 56 insertions(+), 5 deletions(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 2ba1bc47ee11f699726cefaf7c3335d2a8afee49..884b5bd1a493ca9a71654536524125eb8c7c4533 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -139,6 +139,8 @@ + #define CONFDB_DEFAULT_SUDO_TIMED false + #define CONFDB_SUDO_INVERSE_ORDER "sudo_inverse_order" + #define CONFDB_DEFAULT_SUDO_INVERSE_ORDER false ++#define CONFDB_SUDO_THRESHOLD "sudo_threshold" ++#define CONFDB_DEFAULT_SUDO_THRESHOLD 50 + + /* autofs */ + #define CONFDB_AUTOFS_CONF_ENTRY "config/autofs" +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index 75515ab5c68822538728900482296b9159e1547e..137a8fa4d526cb10f3136c62f3c7104d9ecb7599 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -107,6 +107,7 @@ option_strings = { + # [sudo] + 'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'), + 'sudo_inverse_order' : _('If true, SSSD will switch back to lower-wins ordering logic'), ++ 'sudo_threshold' : _('Maximum number of rules that can be refreshed at once. If this is exceeded, full refresh is performed.'), + + # [autofs] + 'autofs_negative_timeout' : _('Negative cache timeout length (seconds)'), +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index d6506b7c3cee13f7c5400a546deb787e755abc8b..0bdcfdfbefd6cb24e0c01cb9746dbb98c63a31d2 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -144,6 +144,7 @@ option = cache_first + # sudo service + option = sudo_timed + option = sudo_inverse_order ++option = sudo_threshold + + [rule/allowed_autofs_options] + validator = ini_allowed_options +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index f86589ecefa0b9e046aba781ded107f8e94395d6..9d5eaaaa23c4c5395b155563de1cdf7752aa3dde 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -79,6 +79,7 @@ pam_app_services = str, None, false + # sudo service + sudo_timed = bool, None, false + sudo_inverse_order = bool, None, false ++sudo_threshold = int, None, false + + [autofs] + # autofs service +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 89729575c724622af817f1c05a94d4ae8f1ece2d..d508df82d1d99af7835079c928839dc3cc7c28cb 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -1376,6 +1376,25 @@ pam_account_locked_message = Account locked, please contact help desk. + + + ++ ++ ++ sudo_threshold (integer) ++ ++ ++ Maximum number of expired rules that can be ++ refreshed at once. If number of expired rules ++ is below threshold, those rules are refreshed ++ with rules refresh mechanism. If ++ the threshold is exceeded a ++ full refresh of sudo rules is ++ triggered instead. ++ ++ ++ Default: 50 ++ ++ ++ ++ + + + +diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c +index b427878d4dbe9090824a01386a7475be88b699c0..dca70ea4afc0e6df6d1b1864338c7b1091a98fee 100644 +--- a/src/responder/sudo/sudosrv.c ++++ b/src/responder/sudo/sudosrv.c +@@ -148,6 +148,17 @@ int sudo_process_init(TALLOC_CTX *mem_ctx, + goto fail; + } + ++ /* Get sudo_inverse_order option */ ++ ret = confdb_get_int(sudo_ctx->rctx->cdb, ++ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_THRESHOLD, ++ CONFDB_DEFAULT_SUDO_THRESHOLD, ++ &sudo_ctx->threshold); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n", ++ ret, strerror(ret)); ++ goto fail; ++ } ++ + ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n"); +diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c +index cfdbfc9c9c66d96f774822d6a4d4aaaf1327abe3..3272e634d895acf4854309371779a00cf1525126 100644 +--- a/src/responder/sudo/sudosrv_get_sudorules.c ++++ b/src/responder/sudo/sudosrv_get_sudorules.c +@@ -479,6 +479,7 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct sss_domain_info *domain, ++ int threshold, + uid_t uid, + const char *username, + char **groups) +@@ -520,9 +521,20 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx, + DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules of [%s@%s]\n", + num_rules, username, domain->name); + +- subreq = sss_dp_get_sudoers_send(state, rctx, domain, false, +- SSS_DP_SUDO_REFRESH_RULES, +- username, num_rules, rules); ++ if (num_rules > threshold) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Rules threshold [%d] is reached, performing full refresh " ++ "instead.\n", threshold); ++ ++ subreq = sss_dp_get_sudoers_send(state, rctx, domain, false, ++ SSS_DP_SUDO_FULL_REFRESH, ++ username, 0, NULL); ++ } else { ++ subreq = sss_dp_get_sudoers_send(state, rctx, domain, false, ++ SSS_DP_SUDO_REFRESH_RULES, ++ username, num_rules, rules); ++ } ++ + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; +@@ -609,6 +621,7 @@ struct sudosrv_get_rules_state { + struct sss_domain_info *domain; + char **groups; + bool inverse_order; ++ int threshold; + + struct sysdb_attrs **rules; + uint32_t num_rules; +@@ -640,6 +653,7 @@ struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx, + state->type = type; + state->uid = uid; + state->inverse_order = sudo_ctx->inverse_order; ++ state->threshold = sudo_ctx->threshold; + + DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username); + +@@ -696,8 +710,9 @@ static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq) + } + + subreq = sudosrv_refresh_rules_send(state, state->ev, state->rctx, +- state->domain, state->uid, +- state->username, state->groups); ++ state->domain, state->threshold, ++ state->uid, state->username, ++ state->groups); + if (subreq == NULL) { + ret = ENOMEM; + goto done; +diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h +index 94f3c4458ab20e64db3e0bfce726d5d30a70a202..c76bdd3955bc29b7ba2cda58c503a4c616d7e63a 100644 +--- a/src/responder/sudo/sudosrv_private.h ++++ b/src/responder/sudo/sudosrv_private.h +@@ -48,6 +48,7 @@ struct sudo_ctx { + */ + bool timed; + bool inverse_order; ++ int threshold; + }; + + struct sudo_cmd_ctx { +-- +2.13.5 + diff --git a/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch b/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch new file mode 100644 index 0000000..c33350b --- /dev/null +++ b/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch @@ -0,0 +1,57 @@ +From b9a2edea74ea04a09301f91fffb6835df72d8760 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 22 Aug 2017 13:09:18 +0200 +Subject: [PATCH 192/192] libwbclient: Change return code for + wbcAuthenticateUserEx + +Samba-4.6 change behaviour of few functions +New version of code make sure session info for user is stored in cache. +It is a performance optimisation to prevent contacting KDC for each +session. More details in samba bug +https://bugzilla.samba.org/show_bug.cgi?id=11259 + +Old return code WBC_SSSD_NOT_IMPLEMENTED was translated +to NT_STATUS_LOGON_FAILURE which caused many failures. + + [2017/08/21 11:34:15.044321, 5, pid=27742, effective(0, 0), real(0, 0)] + ../libcli/security/security_token.c:53(security_token_debug) + Security token: (NULL) + [2017/08/21 11:34:15.044330, 5, pid=27742, effective(0, 0), real(0, 0)] + ../source3/auth/token_util.c:640(debug_unix_user_token) + UNIX token of user 0 + Primary group is 0 and contains 0 supplementary groups + [2017/08/21 11:34:15.044349, 4, pid=27742, effective(0, 0), real(0, 0)] + ../source3/smbd/sec_ctx.c:439(pop_sec_ctx) + pop_sec_ctx (0, 0) - sec_ctx_stack_ndx = 0 + [2017/08/21 11:34:15.044360, 1, pid=27742, effective(0, 0), real(0, 0)] + ../source3/smbd/sesssetup.c:290(reply_sesssetup_and_X_spnego) + Failed to generate session_info (user and group token) for session + setup: NT_STATUS_LOGON_FAILURE + +Resolves: +https://pagure.io/SSSD/sssd/issue/3461 + +Reviewed-by: Sumit Bose +(cherry picked from commit 725d04cd21016dc6092a9f03cd363bb83d7c054c) +(cherry picked from commit aede6a1f4412f133e4b3fd76944f764d76fc4868) +(cherry picked from commit 260062d946e7cc265e2671f88b1662276431c0bb) +--- + src/sss_client/libwbclient/wbc_pam_sssd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/sss_client/libwbclient/wbc_pam_sssd.c b/src/sss_client/libwbclient/wbc_pam_sssd.c +index 174cf1310fad0243036fe591978cc89700903896..77698f523e6e7aeb37d4db50b469d1604d7ee595 100644 +--- a/src/sss_client/libwbclient/wbc_pam_sssd.c ++++ b/src/sss_client/libwbclient/wbc_pam_sssd.c +@@ -49,7 +49,7 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params, + *error = NULL; + } + +- WBC_SSSD_NOT_IMPLEMENTED; ++ return WBC_ERR_WINBIND_NOT_AVAILABLE; + } + + /* Trigger a verification of the trust credentials of a specific domain */ +-- +2.9.4 + diff --git a/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch b/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch new file mode 100644 index 0000000..47b85fb --- /dev/null +++ b/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch @@ -0,0 +1,666 @@ +From 2fca2f1b77c0e9ae82e1a24bbf89fbc3115a5e24 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 6 Sep 2017 16:42:20 +0200 +Subject: [PATCH 193/194] IPA: fix handling of certmap_ctx +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch fixes a use-after-free in the AD provider part and +initializes the certmap_ctx with data from the cache at startup. + +Related to https://pagure.io/SSSD/sssd/issue/3508 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Jakub Hrozek +(cherry picked from commit f2e70ec742cd7aab82b74d7e4b424ba3258da7aa) +--- + Makefile.am | 20 +++ + src/providers/ipa/ipa_init.c | 7 + + src/providers/ipa/ipa_subdomains.c | 53 +------ + src/providers/ipa/ipa_subdomains_server.c | 4 +- + src/providers/ldap/ldap_common.h | 5 + + src/providers/ldap/ldap_id.c | 5 +- + src/providers/ldap/sdap.h | 4 +- + src/providers/ldap/sdap_certmap.c | 152 +++++++++++++++++++ + src/tests/cmocka/test_sdap_certmap.c | 244 ++++++++++++++++++++++++++++++ + 9 files changed, 441 insertions(+), 53 deletions(-) + create mode 100644 src/providers/ldap/sdap_certmap.c + create mode 100644 src/tests/cmocka/test_sdap_certmap.c + +diff --git a/Makefile.am b/Makefile.am +index 503c8cfd795b503f566431c08a56a56147180322..907c3256a154ebe2aae5a1667744e1dfbe8abaae 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -257,6 +257,7 @@ if HAVE_CMOCKA + test_search_bases \ + test_ldap_auth \ + test_sdap_access \ ++ test_sdap_certmap \ + sdap-tests \ + test_sysdb_ts_cache \ + test_sysdb_views \ +@@ -2662,6 +2663,24 @@ test_sdap_access_LDADD = \ + libdlopen_test_providers.la \ + $(NULL) + ++test_sdap_certmap_SOURCES = \ ++ src/tests/cmocka/test_sdap_certmap.c \ ++ src/providers/ldap/sdap_certmap.c \ ++ $(NULL) ++test_sdap_certmap_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(TALLOC_CFLAGS) \ ++ $(POPT_CFLAGS) \ ++ $(NULL) ++test_sdap_certmap_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(TALLOC_LIBS) \ ++ $(POPT_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ libsss_certmap.la \ ++ $(NULL) ++ + ad_access_filter_tests_SOURCES = \ + src/tests/cmocka/test_ad_access_filter.c + ad_access_filter_tests_LDADD = \ +@@ -3706,6 +3725,7 @@ libsss_ldap_common_la_SOURCES = \ + src/providers/ldap/sdap_child_helpers.c \ + src/providers/ldap/sdap_fd_events.c \ + src/providers/ldap/sdap_id_op.c \ ++ src/providers/ldap/sdap_certmap.c \ + src/providers/ldap/sdap_idmap.c \ + src/providers/ldap/sdap_idmap.h \ + src/providers/ldap/sdap_range.c \ +diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c +index 7dec4d1fb8541a48470d4e44f10838e5bea67ad5..2b58b1341463f8947d51dee2076dbe92e3093558 100644 +--- a/src/providers/ipa/ipa_init.c ++++ b/src/providers/ipa/ipa_init.c +@@ -649,6 +649,13 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx, + return ENOMEM; + } + ++ ret = sdap_init_certmap(sdap_id_ctx, sdap_id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to initialized certificate mapping.\n"); ++ return ret; ++ } ++ + return EOK; + } + +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index ef348adf4a36e870f44387bd700f5c2beea3bfd6..6f0ff50bde234f72d62f43635d9a787316c78430 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -311,25 +311,6 @@ struct priv_sss_debug { + int level; + }; + +-void ext_debug(void *private, const char *file, long line, const char *function, +- const char *format, ...) +-{ +- va_list ap; +- struct priv_sss_debug *data = private; +- int level = SSSDBG_OP_FAILURE; +- +- if (data != NULL) { +- level = data->level; +- } +- +- if (DEBUG_IS_SET(level)) { +- va_start(ap, format); +- sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED, +- format, ap); +- va_end(ap); +- } +-} +- + static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct sdap_options *sdap_opts, +@@ -344,7 +325,6 @@ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx, + size_t c; + size_t lc = 0; + int ret; +- struct sss_certmap_ctx *certmap_ctx = NULL; + const char **ocs = NULL; + bool user_name_hint = false; + +@@ -444,50 +424,29 @@ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx, + + certmap_list[lc] = NULL; + +- ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &certmap_ctx); +- if (ret != 0) { +- DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n"); ++ ret = sdap_setup_certmap(sdap_opts->sdap_certmap_ctx, certmap_list); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sdap_setup_certmap failed.\n"); + goto done; + } + +- for (c = 0; certmap_list[c] != NULL; c++) { +- DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n", +- certmap_list[c]->name, +- certmap_list[c]->priority, +- certmap_list[c]->match_rule, +- certmap_list[c]->map_rule); +- +- ret = sss_certmap_add_rule(certmap_ctx, certmap_list[c]->priority, +- certmap_list[c]->match_rule, +- certmap_list[c]->map_rule, +- certmap_list[c]->domains); +- if (ret != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "sss_certmap_add_rule failed for rule [%s], skipping. " +- "Please check for typos and if rule syntax is supported.\n", +- certmap_list[c]->name); +- goto done; +- } +- } +- + ret = sysdb_update_certmap(domain->sysdb, certmap_list, user_name_hint); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed"); + goto done; + } + +- sss_certmap_free_ctx(sdap_opts->certmap_ctx); +- sdap_opts->certmap_ctx = talloc_steal(sdap_opts, certmap_ctx); +- + if (_certmap_list != NULL) { + *_certmap_list = certmap_list; ++ } else { ++ talloc_free(certmap_list); + } ++ + ret = EOK; + + done: + talloc_free(ocs); + if (ret != EOK) { +- sss_certmap_free_ctx(certmap_ctx); + talloc_free(certmap_list); + } + +diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c +index 443d83824f329b9d8c3d8e820113e1029f832240..56470ac824feaa59eecbd9f442682220237c2412 100644 +--- a/src/providers/ipa/ipa_subdomains_server.c ++++ b/src/providers/ipa/ipa_subdomains_server.c +@@ -361,8 +361,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, + id_ctx->sdap_id_ctx->opts->idmap_ctx; + + /* Set up the certificate mapping context */ +- ad_id_ctx->sdap_id_ctx->opts->certmap_ctx = +- id_ctx->sdap_id_ctx->opts->certmap_ctx; ++ ad_id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx = ++ id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx; + + *_ad_id_ctx = ad_id_ctx; + return EOK; +diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h +index 1acda4147576503b18f61e0bb56f8efd2263fd44..0510b7d5ab5121bd96f699e8e59520a2a18a604f 100644 +--- a/src/providers/ldap/ldap_common.h ++++ b/src/providers/ldap/ldap_common.h +@@ -362,4 +362,9 @@ sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, + errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx, + struct sdap_id_ctx *id_ctx); + ++errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx); ++ ++errno_t sdap_setup_certmap(struct sdap_certmap_ctx *sdap_certmap_ctx, ++ struct certmap_info **certmap_list); ++struct sss_certmap_ctx *sdap_get_sss_certmap(struct sdap_certmap_ctx *ctx); + #endif /* _LDAP_COMMON_H_ */ +diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c +index 557712e8dc2b2bde664b4054fa2f8eb39df84d73..93204d35ea3782c9aa5d622a962c295869472631 100644 +--- a/src/providers/ldap/ldap_id.c ++++ b/src/providers/ldap/ldap_id.c +@@ -252,9 +252,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, + } + + ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name, +- ctx->opts->certmap_ctx, +- state->domain, +- &user_filter); ++ sdap_get_sss_certmap(ctx->opts->sdap_certmap_ctx), ++ state->domain, &user_filter); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_cert_derb64_to_ldap_filter failed.\n"); +diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h +index afdc01948eefe9dda943c8c7ad01a42dd76a1da8..c85fbe9e78e5eefa7e33ea8055730118b0871a4c 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -446,6 +446,8 @@ struct sdap_ext_member_ctx { + ext_member_recv_fn_t ext_member_resolve_recv; + }; + ++struct sdap_certmap_ctx; ++ + struct sdap_options { + struct dp_option *basic; + struct sdap_attr_map *gen_map; +@@ -481,7 +483,7 @@ struct sdap_options { + enum dc_functional_level dc_functional_level; + + /* Certificate mapping support */ +- struct sss_certmap_ctx *certmap_ctx; ++ struct sdap_certmap_ctx *sdap_certmap_ctx; + }; + + struct sdap_server_opts { +diff --git a/src/providers/ldap/sdap_certmap.c b/src/providers/ldap/sdap_certmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..fcf88a9c69482c8668d486cd2ab0ba37c847e46d +--- /dev/null ++++ b/src/providers/ldap/sdap_certmap.c +@@ -0,0 +1,152 @@ ++ ++/* ++ SSSD ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "util/util.h" ++#include "lib/certmap/sss_certmap.h" ++#include "providers/ldap/ldap_common.h" ++ ++struct sdap_certmap_ctx { ++ struct sss_certmap_ctx *certmap_ctx; ++}; ++ ++struct priv_sss_debug { ++ int level; ++}; ++ ++static void ext_debug(void *private, const char *file, long line, ++ const char *function, const char *format, ...) ++{ ++ va_list ap; ++ struct priv_sss_debug *data = private; ++ int level = SSSDBG_OP_FAILURE; ++ ++ if (data != NULL) { ++ level = data->level; ++ } ++ ++ if (DEBUG_IS_SET(level)) { ++ va_start(ap, format); ++ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED, ++ format, ap); ++ va_end(ap); ++ } ++} ++ ++struct sss_certmap_ctx *sdap_get_sss_certmap(struct sdap_certmap_ctx *ctx) ++{ ++ return ctx == NULL ? NULL : ctx->certmap_ctx; ++} ++ ++errno_t sdap_setup_certmap(struct sdap_certmap_ctx *sdap_certmap_ctx, ++ struct certmap_info **certmap_list) ++{ ++ int ret; ++ struct sss_certmap_ctx *sss_certmap_ctx = NULL; ++ size_t c; ++ ++ if (sdap_certmap_ctx == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Missing sdap_certmap_ctx.\n"); ++ return EINVAL; ++ } ++ ++ if (certmap_list == NULL || *certmap_list == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "No certmap data, nothing to do.\n"); ++ ret = EOK; ++ goto done; ++ } ++ ++ ret = sss_certmap_init(sdap_certmap_ctx, ext_debug, NULL, &sss_certmap_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n"); ++ goto done; ++ } ++ ++ for (c = 0; certmap_list[c] != NULL; c++) { ++ DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n", ++ certmap_list[c]->name, ++ certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule); ++ ++ ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule, ++ certmap_list[c]->domains); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_certmap_add_rule failed for rule [%s] " ++ "with error [%d][%s], skipping. " ++ "Please check for typos and if rule syntax is supported.\n", ++ certmap_list[c]->name, ret, sss_strerror(ret)); ++ continue; ++ } ++ } ++ ++ ret = EOK; ++ ++done: ++ if (ret == EOK) { ++ sss_certmap_free_ctx(sdap_certmap_ctx->certmap_ctx); ++ sdap_certmap_ctx->certmap_ctx = sss_certmap_ctx; ++ } else { ++ sss_certmap_free_ctx(sss_certmap_ctx); ++ } ++ ++ return ret; ++} ++ ++errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx) ++{ ++ int ret; ++ bool hint; ++ struct certmap_info **certmap_list = NULL; ++ ++ if (id_ctx->opts->sdap_certmap_ctx == NULL) { ++ id_ctx->opts->sdap_certmap_ctx = talloc_zero(mem_ctx, ++ struct sdap_certmap_ctx); ++ if (id_ctx->opts->sdap_certmap_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); ++ return ENOMEM; ++ } ++ } ++ ++ ret = sysdb_get_certmap(mem_ctx, id_ctx->be->domain->sysdb, ++ &certmap_list, &hint); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n"); ++ goto done; ++ } ++ ++ ret = sdap_setup_certmap(id_ctx->opts->sdap_certmap_ctx, certmap_list); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sdap_setup_certmap failed.\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(certmap_list); ++ ++ return ret; ++} +diff --git a/src/tests/cmocka/test_sdap_certmap.c b/src/tests/cmocka/test_sdap_certmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..9df5666844c8582a3fdb5b086720f1f2819f53f3 +--- /dev/null ++++ b/src/tests/cmocka/test_sdap_certmap.c +@@ -0,0 +1,244 @@ ++/* ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2017 Red Hat ++ ++ SSSD tests - sdap certmap ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "providers/ldap/ldap_common.h" ++#include "tests/common.h" ++#include "db/sysdb.h" ++ ++#define TESTS_PATH "certmap_" BASE_FILE_STEM ++#define TEST_CONF_DB "test_sysdb_certmap.ldb" ++#define TEST_ID_PROVIDER "ldap" ++#define TEST_DOM_NAME "certmap_test" ++ ++struct certmap_info map_a = { discard_const("map_a"), 11, ++ NULL, discard_const("(abc=def)"), ++ NULL }; ++struct certmap_info map_b = { discard_const("map_b"), UINT_MAX, ++ NULL, NULL, NULL }; ++struct certmap_info *certmap[] = { &map_a, &map_b, NULL }; ++ ++struct certmap_test_ctx { ++ struct sss_test_ctx *tctx; ++ struct sdap_id_ctx *id_ctx; ++}; ++ ++static int test_sysdb_setup(void **state) ++{ ++ int ret; ++ struct certmap_test_ctx *test_ctx; ++ struct sss_test_conf_param params[] = { ++ { NULL, NULL }, /* Sentinel */ ++ }; ++ ++ assert_true(leak_check_setup()); ++ ++ test_ctx = talloc_zero(global_talloc_context, ++ struct certmap_test_ctx); ++ assert_non_null(test_ctx); ++ check_leaks_push(test_ctx); ++ ++ test_dom_suite_setup(TESTS_PATH); ++ ++ test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, ++ TEST_CONF_DB, TEST_DOM_NAME, ++ TEST_ID_PROVIDER, params); ++ assert_non_null(test_ctx->tctx); ++ ++ ret = sysdb_update_certmap(test_ctx->tctx->sysdb, certmap, false); ++ assert_int_equal(ret, EOK); ++ ++ test_ctx->id_ctx = talloc_zero(test_ctx->tctx, struct sdap_id_ctx); ++ assert_non_null(test_ctx->id_ctx); ++ ++ test_ctx->id_ctx->opts = talloc_zero(test_ctx->tctx, struct sdap_options); ++ assert_non_null(test_ctx->id_ctx->opts); ++ ++ test_ctx->id_ctx->be = talloc_zero(test_ctx->tctx, struct be_ctx); ++ assert_non_null(test_ctx->id_ctx->be); ++ test_ctx->id_ctx->be->domain = test_ctx->tctx->dom; ++ ++ *state = test_ctx; ++ return 0; ++} ++ ++static int test_sysdb_teardown(void **state) ++{ ++ struct certmap_test_ctx *test_ctx = ++ talloc_get_type(*state, struct certmap_test_ctx); ++ ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); ++ talloc_free(test_ctx->tctx); ++ assert_true(check_leaks_pop(test_ctx)); ++ talloc_free(test_ctx); ++ assert_true(leak_check_teardown()); ++ return 0; ++} ++ ++static void test_sdap_certmap_init(void **state) ++{ ++ int ret; ++ struct certmap_test_ctx *test_ctx = talloc_get_type(*state, ++ struct certmap_test_ctx); ++ ++ ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); ++ assert_int_equal(ret, EOK); ++ ++ talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); ++} ++ ++static void test_sdap_get_sss_certmap(void **state) ++{ ++ int ret; ++ struct certmap_test_ctx *test_ctx = talloc_get_type(*state, ++ struct certmap_test_ctx); ++ struct sss_certmap_ctx *sss_certmap_ctx; ++ ++ sss_certmap_ctx = sdap_get_sss_certmap(NULL); ++ assert_null(sss_certmap_ctx); ++ ++ ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); ++ assert_int_equal(ret, EOK); ++ ++ sss_certmap_ctx = sdap_get_sss_certmap( ++ test_ctx->id_ctx->opts->sdap_certmap_ctx); ++ assert_non_null(sss_certmap_ctx); ++ ++ talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); ++} ++ ++static void test_sdap_certmap_init_twice(void **state) ++{ ++ int ret; ++ struct certmap_test_ctx *test_ctx = talloc_get_type(*state, ++ struct certmap_test_ctx); ++ struct sdap_certmap_ctx *sdap_certmap_ref; ++ struct sss_certmap_ctx *sss_certmap_ref; ++ ++ ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); ++ assert_int_equal(ret, EOK); ++ ++ sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx; ++ sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref); ++ ++ ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); ++ assert_int_equal(ret, EOK); ++ ++ assert_ptr_equal(sdap_certmap_ref, ++ test_ctx->id_ctx->opts->sdap_certmap_ctx); ++ assert_ptr_not_equal(sss_certmap_ref, ++ sdap_get_sss_certmap(sdap_certmap_ref)); ++ ++ talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); ++} ++ ++ ++static void test_sdap_setup_certmap(void **state) ++{ ++ int ret; ++ struct certmap_test_ctx *test_ctx = talloc_get_type(*state, ++ struct certmap_test_ctx); ++ struct sdap_certmap_ctx *sdap_certmap_ref; ++ struct sss_certmap_ctx *sss_certmap_ref; ++ ++ ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); ++ assert_int_equal(ret, EOK); ++ ++ sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx; ++ sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref); ++ ++ ret = sdap_setup_certmap(NULL, NULL); ++ assert_int_equal(ret, EINVAL); ++ assert_ptr_equal(sdap_certmap_ref, ++ test_ctx->id_ctx->opts->sdap_certmap_ctx); ++ assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref)); ++ ++ ret = sdap_setup_certmap(NULL, certmap); ++ assert_int_equal(ret, EINVAL); ++ assert_ptr_equal(sdap_certmap_ref, ++ test_ctx->id_ctx->opts->sdap_certmap_ctx); ++ assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref)); ++ ++ ret = sdap_setup_certmap(sdap_certmap_ref, certmap); ++ assert_int_equal(ret, EOK); ++ assert_ptr_equal(sdap_certmap_ref, ++ test_ctx->id_ctx->opts->sdap_certmap_ctx); ++ assert_ptr_not_equal(sss_certmap_ref, ++ sdap_get_sss_certmap(sdap_certmap_ref)); ++ ++ talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ int rv; ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_sdap_certmap_init, ++ test_sysdb_setup, ++ test_sysdb_teardown), ++ cmocka_unit_test_setup_teardown(test_sdap_get_sss_certmap, ++ test_sysdb_setup, ++ test_sysdb_teardown), ++ cmocka_unit_test_setup_teardown(test_sdap_certmap_init_twice, ++ test_sysdb_setup, ++ test_sysdb_teardown), ++ cmocka_unit_test_setup_teardown(test_sdap_setup_certmap, ++ test_sysdb_setup, ++ test_sysdb_teardown), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++ tests_set_cwd(); ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ ++ return rv; ++} +-- +2.13.5 + diff --git a/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch b/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch new file mode 100644 index 0000000..c2f4d5d --- /dev/null +++ b/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch @@ -0,0 +1,95 @@ +From 27ef368b4105f19382360fe62f944b36ca74adb7 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 6 Sep 2017 12:20:25 +0200 +Subject: [PATCH 194/194] certmap: make sure eku_oid_list is always allocated +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If there are only OIDs in a part of a matching rule a NULL pointer +dereference might occur. + +Related to https://pagure.io/SSSD/sssd/issue/3508 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Jakub Hrozek +(cherry picked from commit f5a8cd60c6f377af1954b58f007d16cf3f6dc846) +--- + src/lib/certmap/sss_certmap_krb5_match.c | 21 ++++++++++++--------- + src/tests/cmocka/test_certmap.c | 17 +++++++++++++++++ + 2 files changed, 29 insertions(+), 9 deletions(-) + +diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c +index e40f17b8ace46e61087e0a2fa570a362a84cead2..0a77ac225d73f3506e102fdbdc9084faa0f19cf0 100644 +--- a/src/lib/certmap/sss_certmap_krb5_match.c ++++ b/src/lib/certmap/sss_certmap_krb5_match.c +@@ -179,19 +179,17 @@ static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx, + goto done; + } + ++ comp->eku_oid_list = talloc_zero_array(comp, const char *, ++ eku_list_size + 1); ++ if (comp->eku_oid_list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ + for (c = 0; eku_list[c] != NULL; c++) { + for (k = 0; ext_key_usage[k].name != NULL; k++) { + CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name); + if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) { +- if (comp->eku_oid_list == NULL) { +- comp->eku_oid_list = talloc_zero_array(comp, const char *, +- eku_list_size + 1); +- if (comp->eku_oid_list == NULL) { +- ret = ENOMEM; +- goto done; +- } +- } +- + comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list, + ext_key_usage[k].oid); + if (comp->eku_oid_list[e] == NULL) { +@@ -225,6 +223,11 @@ CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name); + } + } + ++ if (e == 0) { ++ talloc_free(comp->eku_oid_list); ++ comp->eku_oid_list = NULL; ++ } ++ + ret = 0; + + done: +diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c +index c998443d086eaa72cc2a05c38ddfc5ba590a1ce7..e732bb214476943d0f723b318ab64d3b4156cace 100644 +--- a/src/tests/cmocka/test_certmap.c ++++ b/src/tests/cmocka/test_certmap.c +@@ -445,6 +445,23 @@ static void test_sss_certmap_add_matching_rule(void **state) + assert_null( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]); + ++ ret = sss_certmap_add_rule(ctx, 96, ++ "KRB5:1.2.3", ++ NULL, NULL); ++ assert_int_equal(ret, 0); ++ assert_non_null(ctx->prio_list); ++ assert_non_null(ctx->prio_list->rule_list); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); ++ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, ++ relation_and); ++ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku); ++ assert_true(string_in_list("1.2.3", ++ discard_const( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), ++ true)); ++ assert_null( ++ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[1]); ++ + /* SAN tests */ + ret = sss_certmap_add_rule(ctx, 89, "KRB5:abc", NULL, NULL); + assert_int_equal(ret, 0); +-- +2.13.5 + diff --git a/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch b/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch new file mode 100644 index 0000000..2841962 --- /dev/null +++ b/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch @@ -0,0 +1,62 @@ +From cf75c30a42059480eca4c352598ceb3760c27e46 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 21 Aug 2017 11:42:43 +0200 +Subject: [PATCH 195/195] cache_req: Look for name attribute also in + nss_cmd_getsidbyid +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We always check negcache after getting data from backend since commit +4c09cd008967c5c0ec358dc658ffc6fc1cef2697 because we usually do have a name +in begging of requests "* by ID". + +We were not interested in name in request sid by id before. However, function +cache_req_search_ncache_filter always expect name otherwise it returns +ERR_INTERNAL. + +[sssd[nss]] [cache_req_set_plugin] (0x2000): CR #8: Setting "Object by ID" plugin +[sssd[nss]] [cache_req_send] (0x0400): CR #8: New request 'Object by ID' +[sssd[nss]] [cache_req_select_domains] (0x0400): CR #8: Performing a multi-domain search +[sssd[nss]] [cache_req_search_domains] (0x0400): CR #8: Search will check the cache and check the data provider +[sssd[nss]] [cache_req_validate_domain_type] (0x2000): Request type POSIX-only for domain sssdad2012r2.com type POSIX is valid +[sssd[nss]] [cache_req_set_domain] (0x0400): CR #8: Using domain [sssdad2012r2.com] +[sssd[nss]] [cache_req_search_send] (0x0400): CR #8: Looking up ID:233600513@sssdad2012r2.com +[sssd[nss]] [cache_req_search_ncache] (0x0400): CR #8: Checking negative cache for [ID:233600513@sssdad2012r2.com] +[sssd[nss]] [sss_ncache_check_str] (0x2000): Checking negative cache for [NCE/UID/sssdad2012r2.com/233600513] +[sssd[nss]] [cache_req_search_ncache] (0x0400): CR #8: [ID:233600513@sssdad2012r2.com] is not present in negative cache +[sssd[nss]] [cache_req_search_cache] (0x0400): CR #8: Looking up [ID:233600513@sssdad2012r2.com] in cache +[sssd[nss]] [cache_req_search_send] (0x0400): CR #8: Returning [ID:233600513@sssdad2012r2.com] from cache +[sssd[nss]] [cache_req_search_ncache_filter] (0x0400): CR #8: Filtering out results by negative cache +[sssd[nss]] [cache_req_search_ncache_filter] (0x0020): CR #8: sss_get_name_from_msg() returned NULL, which should never happen in this scenario! +[sssd[nss]] [cache_req_process_result] (0x0400): CR #8: Finished: Error 1432158209: Internal Error +[sssd[nss]] [nss_protocol_done] (0x4000): Sending reply: error [1432158209]: Internal Error +[sssd[nss]] [client_recv] (0x0200): Client disconnected! + +Resolves: +https://pagure.io/SSSD/sssd/issue/3485 + +Reviewed-by: Pavel Březina +(cherry picked from commit 2e72ababbbadda4c4036f99528460c1d595e0941) +--- + src/responder/common/cache_req/cache_req_data.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c +index 5ab1493b81dbcd1529f1124a2bb1f99d3ae82281..3c365e2fe5826fd58c75f07b08193e5566db2563 100644 +--- a/src/responder/common/cache_req/cache_req_data.c ++++ b/src/responder/common/cache_req/cache_req_data.c +@@ -26,7 +26,9 @@ static const char ** + cache_req_data_create_attrs(TALLOC_CTX *mem_ctx, + const char **requested) + { +- static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS }; ++ static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS, SYSDB_NAME, ++ OVERRIDE_PREFIX SYSDB_NAME, ++ SYSDB_DEFAULT_OVERRIDE_NAME }; + static size_t defnum = sizeof(defattrs) / sizeof(defattrs[0]); + const char **attrs; + size_t reqnum; +-- +2.13.5 + diff --git a/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch b/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch new file mode 100644 index 0000000..2fe32d6 --- /dev/null +++ b/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch @@ -0,0 +1,461 @@ +From daa59b79602cfeff81223a7461e18f513178c9d4 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 18 Sep 2017 15:00:53 +0200 +Subject: [PATCH 196/196] sssd_client: add mutex protected call to the PAC + responder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +SSSD's plugin for MIT Kerberos to send the PAC to the PAC responder +currently uses sss_pac_make_request() which does not protect the +communication with the PAC responder with a mutex as e.g. the NSS and +PAM clients. + +If an application using threads loads this plugin via libkrb5 in +different threads and is heavily processing Kerberos tickets with PACs +chances are that two threads try to communicate with SSSD at once. In +this case one of the threads will miss a reply and will wait for it +until the default client timeout of 300s is passed. + +This patch adds a call which uses a mutex to protect the communication +which will avoid the 300s delay mentioned above. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3518 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Fabiano Fidêncio +(cherry picked from commit 1f331476e7d33bb03cc35a2a9064ee1cc5bed6cf) +--- + Makefile.am | 16 ++++ + src/sss_client/common.c | 30 +++++++ + src/sss_client/sss_cli.h | 7 ++ + src/sss_client/sss_pac_responder_client.c | 137 ++++++++++++++++++++++++++++++ + src/sss_client/sssd_pac.c | 4 +- + src/tests/intg/Makefile.am | 1 + + src/tests/intg/test_pac_responder.py | 120 ++++++++++++++++++++++++++ + 7 files changed, 313 insertions(+), 2 deletions(-) + create mode 100644 src/sss_client/sss_pac_responder_client.c + create mode 100644 src/tests/intg/test_pac_responder.py + +diff --git a/Makefile.am b/Makefile.am +index 907c3256a154ebe2aae5a1667744e1dfbe8abaae..cdd517d50679b876814303fb7d6c63d49bcd8d38 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -3501,6 +3501,9 @@ endif + if BUILD_WITH_LIBCURL + noinst_PROGRAMS += tcurl-test-tool + endif ++if BUILD_PAC_RESPONDER ++ noinst_PROGRAMS += sssd_pac_test_client ++endif + + if BUILD_AUTOFS + autofs_test_client_SOURCES = \ +@@ -4210,6 +4213,19 @@ sssd_pac_plugin_la_LDFLAGS = \ + -avoid-version \ + -module + ++sssd_pac_test_client_SOURCES = \ ++ src/sss_client/sss_pac_responder_client.c \ ++ src/sss_client/common.c \ ++ src/util/strtonum.c \ ++ $(NULL) ++sssd_pac_test_client_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++sssd_pac_test_client_LDADD = \ ++ $(CLIENT_LIBS) \ ++ -lpthread \ ++ $(NULL) ++ + # python[23] bindings + pysss_la_SOURCES = \ + $(SSSD_TOOLS_OBJ) \ +diff --git a/src/sss_client/common.c b/src/sss_client/common.c +index b7a5ed760ca379acdfd8f1d2bf95cee1aa271fd8..b527c046e2e3369934b4f9ea7efc1b52eb8c57ea 100644 +--- a/src/sss_client/common.c ++++ b/src/sss_client/common.c +@@ -821,6 +821,22 @@ int sss_pac_make_request(enum sss_cli_command cmd, + } + } + ++int sss_pac_make_request_with_lock(enum sss_cli_command cmd, ++ struct sss_cli_req_data *rd, ++ uint8_t **repbuf, size_t *replen, ++ int *errnop) ++{ ++ int ret; ++ ++ sss_pac_lock(); ++ ++ ret = sss_pac_make_request(cmd, rd, repbuf, replen, errnop); ++ ++ sss_pac_unlock(); ++ ++ return ret; ++} ++ + errno_t check_server_cred(int sockfd) + { + #ifdef HAVE_UCRED +@@ -1079,6 +1095,8 @@ static struct sss_mutex sss_pam_mtx = { .mtx = PTHREAD_MUTEX_INITIALIZER }; + + static struct sss_mutex sss_nss_mc_mtx = { .mtx = PTHREAD_MUTEX_INITIALIZER }; + ++static struct sss_mutex sss_pac_mtx = { .mtx = PTHREAD_MUTEX_INITIALIZER }; ++ + static void sss_mt_lock(struct sss_mutex *m) + { + pthread_mutex_lock(&m->mtx); +@@ -1121,6 +1139,16 @@ void sss_nss_mc_unlock(void) + sss_mt_unlock(&sss_nss_mc_mtx); + } + ++/* PAC mutex wrappers */ ++void sss_pac_lock(void) ++{ ++ sss_mt_lock(&sss_pac_mtx); ++} ++void sss_pac_unlock(void) ++{ ++ sss_mt_unlock(&sss_pac_mtx); ++} ++ + #else + + /* sorry no mutexes available */ +@@ -1130,6 +1158,8 @@ void sss_pam_lock(void) { return; } + void sss_pam_unlock(void) { return; } + void sss_nss_mc_lock(void) { return; } + void sss_nss_mc_unlock(void) { return; } ++void sss_pac_lock(void) { return; } ++void sss_pac_unlock(void) { return; } + #endif + + +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index d4198407f2f86c6594aee6a2a43775e429692df0..337fe9803d2df3167cd2da77107dbd077f35a51b 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -585,6 +585,11 @@ int sss_pac_make_request(enum sss_cli_command cmd, + uint8_t **repbuf, size_t *replen, + int *errnop); + ++int sss_pac_make_request_with_lock(enum sss_cli_command cmd, ++ struct sss_cli_req_data *rd, ++ uint8_t **repbuf, size_t *replen, ++ int *errnop); ++ + int sss_sudo_make_request(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + uint8_t **repbuf, size_t *replen, +@@ -634,6 +639,8 @@ void sss_pam_lock(void); + void sss_pam_unlock(void); + void sss_nss_mc_lock(void); + void sss_nss_mc_unlock(void); ++void sss_pac_lock(void); ++void sss_pac_unlock(void); + + errno_t sss_readrep_copy_string(const char *in, + size_t *offset, +diff --git a/src/sss_client/sss_pac_responder_client.c b/src/sss_client/sss_pac_responder_client.c +new file mode 100644 +index 0000000000000000000000000000000000000000..9eb0cbea6175ee273b23d9a975529d85c02fc603 +--- /dev/null ++++ b/src/sss_client/sss_pac_responder_client.c +@@ -0,0 +1,137 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "sss_client/sss_cli.h" ++ ++const uint8_t pac[] = { ++0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, ++0x02, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, ++0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x0c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xb8, ++0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, ++0x00, 0x00, 0xc8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, ++0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x02, 0x00, 0x30, 0xe3, 0xd6, 0x9e, 0x99, 0x2b, 0xd3, 0x01, 0xff, ++0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++0xff, 0x7f, 0xe2, 0xf7, 0x8a, 0xaf, 0x00, 0x0f, 0xd0, 0x01, 0xe2, 0xb7, 0xf4, ++0xd9, 0xc9, 0x0f, 0xd0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, ++0x06, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, ++0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x45, 0x02, 0x00, 0x00, ++0x50, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1c, ++0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x14, ++0x00, 0x20, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x24, 0x00, 0x02, 0x00, ++0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, ++0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, ++0x75, 0x00, 0x31, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x20, 0x00, 0x75, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, ++0xfd, 0xa2, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07, ++0x00, 0x00, 0x00, 0x5c, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x04, ++0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0xa6, 0x00, 0x00, 0x07, 0x00, 0x00, ++0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, ++0x41, 0x00, 0x44, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, ++0x00, 0x45, 0x00, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, ++0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x44, 0x00, 0x04, 0x00, 0x00, ++0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, ++0xf8, 0x12, 0x13, 0xdc, 0x47, 0xf3, 0x1c, 0x76, 0x47, 0x2f, 0x2e, 0xd7, 0x02, ++0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, ++0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, ++0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x29, 0xc9, 0x4f, 0xd9, ++0xc2, 0x3c, 0xc3, 0x78, 0x36, 0x55, 0x87, 0xf8, 0x54, 0x04, 0x00, 0x00, 0x05, ++0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, ++0x00, 0x00, 0x25, 0xe1, 0xff, 0x1c, 0xf7, 0x87, 0x6b, 0x2c, 0x25, 0xd2, 0x0c, ++0xe3, 0xf2, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x29, 0x89, 0x65, 0x2d, 0xd3, 0x01, ++0x06, 0x00, 0x74, 0x00, 0x75, 0x00, 0x31, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, ++0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, ++0x75, 0x00, 0x31, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x40, ++0x00, 0x61, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x76, 0x00, ++0x65, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x44, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x45, ++0x00, 0x56, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x76, 0x8e, ++0x25, 0x32, 0x7c, 0x85, 0x00, 0x32, 0xac, 0x8f, 0x02, 0x2c, 0x10, 0x00, 0x00, ++0x00, 0x6b, 0xe8, 0x51, 0x03, 0x30, 0xed, 0xca, 0x7d, 0xe2, 0x12, 0xa5, 0xde}; ++ ++enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result, ++ char *buffer, size_t buflen, int *errnop); ++static void *pac_client(void *arg) ++{ ++ struct sss_cli_req_data sss_data = { sizeof(pac), pac }; ++ int errnop = -1; ++ int ret; ++ size_t c; ++ ++ fprintf(stderr, "[%ld][%d][%ld][%s] started\n", time(NULL), getpid(), ++ syscall(SYS_gettid), ++ (char *) arg); ++ for (c = 0; c < 1000; c++) { ++ /* sss_pac_make_request() does not protect the client's file ++ * descriptor to the PAC responder. With this one thread will miss a ++ * reply for a SSS_GET_VERSION request and will wait until ++ * SSS_CLI_SOCKET_TIMEOUT is passed. ++ ++ ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data, ++ NULL, NULL, &errnop); ++ */ ++ ret = sss_pac_make_request_with_lock(SSS_PAC_ADD_PAC_USER, &sss_data, ++ NULL, NULL, &errnop); ++ if (ret != NSS_STATUS_SUCCESS ++ && !(ret == NSS_STATUS_UNAVAIL && errnop != ECONNREFUSED)) { ++ /* NSS_STATUS_UNAVAIL is returned if the PAC responder rejects ++ * the request which is ok becasue the client is waiting for a ++ * response here as well. Only errnop == ECONNREFUSED should ++ * be treated as error becasue this means that the PAC ++ * responder is not running. */ ++ fprintf(stderr, "pac: [%s][%d][%d]\n", (char *)arg, ret, errnop); ++ return ((void *)((uintptr_t)("X"))); ++ } ++ } ++ ++ fprintf(stderr, "[%ld][%s] done\n", time(NULL),(char *) arg); ++ return NULL; ++} ++ ++int main(void) ++{ ++ pthread_t thread1; ++ pthread_t thread2; ++ int ret; ++ void *t_ret; ++ ++ pthread_create(&thread1, NULL, pac_client, ++ ((void *)((uintptr_t)("Thread 1")))); ++ pthread_create(&thread2, NULL, pac_client, ++ ((void *)((uintptr_t)("Thread 2")))); ++ ++ ret = pthread_join(thread1, &t_ret); ++ if (ret != 0 || t_ret != NULL) { ++ fprintf(stderr, "Thread 1 failed.\n"); ++ return EIO; ++ } ++ ++ ret = pthread_join(thread2, &t_ret); ++ if (ret != 0 || t_ret != NULL) { ++ fprintf(stderr, "Thread 1 failed.\n"); ++ return EIO; ++ } ++ ++ return 0; ++} +diff --git a/src/sss_client/sssd_pac.c b/src/sss_client/sssd_pac.c +index 1d98e38826b36aed199b32880a7e27de905a4592..8444834a7f148787e847f5e8e21186c8701b2de7 100644 +--- a/src/sss_client/sssd_pac.c ++++ b/src/sss_client/sssd_pac.c +@@ -169,8 +169,8 @@ static krb5_error_code sssdpac_verify(krb5_context kcontext, + sss_data.len = sssdctx->data.length; + sss_data.data = sssdctx->data.data; + +- ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data, +- NULL, NULL, &errnop); ++ ret = sss_pac_make_request_with_lock(SSS_PAC_ADD_PAC_USER, &sss_data, ++ NULL, NULL, &errnop); + if (ret != 0) { + /* Ignore the error */ + } +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 8566106e9017a8d3c9e7a3898a3a886e2966e346..0af7c62ca243822d919619f3d0ebc852a317efc4 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -29,6 +29,7 @@ dist_noinst_DATA = \ + kdc.py \ + krb5utils.py \ + test_kcm.py \ ++ test_pac_responder.py \ + $(NULL) + + config.py: config.py.m4 +diff --git a/src/tests/intg/test_pac_responder.py b/src/tests/intg/test_pac_responder.py +new file mode 100644 +index 0000000000000000000000000000000000000000..4354a5d78da6a6627a27d0ca85c8a1d47419cedf +--- /dev/null ++++ b/src/tests/intg/test_pac_responder.py +@@ -0,0 +1,120 @@ ++# ++# SSSD PAC responder tests ++# ++# Copyright (c) 2017 Red Hat, Inc. ++# Author: Sumit Bose ++# ++# This is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 only ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++import os ++import stat ++import time ++import config ++import signal ++import subprocess ++import pytest ++from util import unindent ++ ++ ++def stop_sssd(): ++ with open(config.PIDFILE_PATH, "r") as pid_file: ++ pid = int(pid_file.read()) ++ os.kill(pid, signal.SIGTERM) ++ while True: ++ try: ++ os.kill(pid, signal.SIGCONT) ++ except: ++ break ++ time.sleep(1) ++ ++ ++def create_conf_fixture(request, contents): ++ """Generate sssd.conf and add teardown for removing it""" ++ conf = open(config.CONF_PATH, "w") ++ conf.write(contents) ++ conf.close() ++ os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR) ++ request.addfinalizer(lambda: os.unlink(config.CONF_PATH)) ++ ++ ++def create_sssd_fixture(request): ++ """Start sssd and add teardown for stopping it and removing state""" ++ if subprocess.call(["sssd", "-D", "-f"]) != 0: ++ raise Exception("sssd start failed") ++ ++ def teardown(): ++ try: ++ stop_sssd() ++ except: ++ pass ++ for path in os.listdir(config.DB_PATH): ++ os.unlink(config.DB_PATH + "/" + path) ++ for path in os.listdir(config.MCACHE_PATH): ++ os.unlink(config.MCACHE_PATH + "/" + path) ++ request.addfinalizer(teardown) ++ ++ ++@pytest.fixture ++def local_domain_only(request): ++ conf = unindent("""\ ++ [sssd] ++ domains = LOCAL ++ services = nss, pac ++ ++ [nss] ++ memcache_timeout = 0 ++ ++ [domain/LOCAL] ++ id_provider = local ++ min_id = 10000 ++ max_id = 20000 ++ """).format(**locals()) ++ create_conf_fixture(request, conf) ++ create_sssd_fixture(request) ++ return None ++ ++ ++@pytest.fixture ++def sssd_pac_test_client(request): ++ path = os.path.join(config.ABS_BUILDDIR, ++ "..", "..", "..", "sssd_pac_test_client") ++ if os.access(path, os.X_OK): ++ return path ++ ++ return None ++ ++ ++def timeout_handler(signum, frame): ++ raise Exception("Timeout") ++ ++ ++def test_multithreaded_pac_client(local_domain_only, sssd_pac_test_client): ++ """ ++ Test for ticket ++ https://pagure.io/SSSD/sssd/issue/3518 ++ """ ++ ++ if not sssd_pac_test_client: ++ pytest.skip("The sssd_pac_test_client is not available, skipping test") ++ ++ signal.signal(signal.SIGALRM, timeout_handler) ++ signal.alarm(10) ++ ++ try: ++ subprocess.check_call(sssd_pac_test_client) ++ except: ++ # cancel alarm ++ signal.alarm(0) ++ raise Exception("sssd_pac_test_client failed") ++ ++ signal.alarm(0) +-- +2.13.5 + diff --git a/SOURCES/0197-sysdb-sanitize-search-filter-input.patch b/SOURCES/0197-sysdb-sanitize-search-filter-input.patch new file mode 100644 index 0000000..0b2134d --- /dev/null +++ b/SOURCES/0197-sysdb-sanitize-search-filter-input.patch @@ -0,0 +1,140 @@ +From 944295de4cf4eeba75d4f6bd476a4f59743e1813 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 5 Oct 2017 11:07:38 +0200 +Subject: [PATCH 197/197] sysdb: sanitize search filter input +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch sanitizes the input for sysdb searches by UPN/email, SID and +UUID. + +This security issue was assigned CVE-2017-12173 + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Jakub Hrozek +(cherry picked from commit 1f2662c8f97c9c0fa250055d4b6750abfc6d0835) +--- + src/db/sysdb_ops.c | 43 +++++++++++++++++++++++++++++++++++-------- + src/tests/sysdb-tests.c | 7 +++++++ + 2 files changed, 42 insertions(+), 8 deletions(-) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 7ca6575ce75dab7805236c9f48dbf28a2f3946d2..408af9f389edbe0aff0fb8b96f49f0c4463a620a 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -601,6 +601,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx, + int ret; + const char *def_attrs[] = { SYSDB_NAME, SYSDB_UPN, SYSDB_CANONICAL_UPN, + SYSDB_USER_EMAIL, NULL }; ++ char *sanitized; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +@@ -608,6 +609,12 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx, + goto done; + } + ++ ret = sss_filter_sanitize(tmp_ctx, upn, &sanitized); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n"); ++ goto done; ++ } ++ + if (domain_scope == true) { + base_dn = sysdb_user_base_dn(tmp_ctx, domain); + } else { +@@ -620,7 +627,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx, + + ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, + base_dn, LDB_SCOPE_SUBTREE, attrs ? attrs : def_attrs, +- SYSDB_PWUPN_FILTER, upn, upn, upn); ++ SYSDB_PWUPN_FILTER, sanitized, sanitized, sanitized); + if (ret != EOK) { + ret = sysdb_error_to_errno(ret); + goto done; +@@ -4757,17 +4764,31 @@ static errno_t sysdb_search_object_by_str_attr(TALLOC_CTX *mem_ctx, + bool expect_only_one_result, + struct ldb_result **_res) + { +- char *filter; ++ char *filter = NULL; + errno_t ret; ++ char *sanitized = NULL; + +- filter = talloc_asprintf(NULL, filter_tmpl, str); ++ if (str == NULL) { ++ return EINVAL; ++ } ++ ++ ret = sss_filter_sanitize(NULL, str, &sanitized); ++ if (ret != EOK || sanitized == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n"); ++ goto done; ++ } ++ ++ filter = talloc_asprintf(NULL, filter_tmpl, sanitized); + if (filter == NULL) { +- return ENOMEM; ++ ret = ENOMEM; ++ goto done; + } + + ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, + expect_only_one_result, _res); + ++done: ++ talloc_free(sanitized); + talloc_free(filter); + return ret; + } +@@ -4856,7 +4877,8 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx, + struct ldb_result **res) + { + int ret; +- char *user_filter; ++ char *user_filter = NULL; ++ char *filter = NULL; + + ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT, + NULL, NULL, &user_filter); +@@ -4865,10 +4887,15 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx, + return ret; + } + +- ret = sysdb_search_object_by_str_attr(mem_ctx, domain, +- SYSDB_USER_CERT_FILTER, +- user_filter, attrs, false, res); ++ filter = talloc_asprintf(NULL, SYSDB_USER_CERT_FILTER, user_filter); + talloc_free(user_filter); ++ if (filter == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, false, res); ++ ++ talloc_free(filter); + + return ret; + } +diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c +index 6ec82ce4ca5c4f918bc9f3144c21f33b270ea47e..588eb9ef02033ec061b4187964fe562da84e86c8 100644 +--- a/src/tests/sysdb-tests.c ++++ b/src/tests/sysdb-tests.c +@@ -6444,6 +6444,13 @@ START_TEST(test_upn_basic) + fail_unless(strcmp(str, UPN_PRINC) == 0, + "Expected [%s], got [%s].", UPN_PRINC, str); + ++ /* check if input is sanitized */ ++ ret = sysdb_search_user_by_upn(test_ctx, test_ctx->domain, false, ++ "abc@def.ghi)(name="UPN_USER_NAME")(abc=xyz", ++ NULL, &msg); ++ fail_unless(ret == ENOENT, ++ "sysdb_search_user_by_upn failed with un-sanitized input."); ++ + talloc_free(test_ctx); + } + END_TEST +-- +2.13.5 + diff --git a/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch b/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch new file mode 100644 index 0000000..25422d6 --- /dev/null +++ b/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch @@ -0,0 +1,60 @@ +From 2d39f46386fb36b5a68f41644ce22c15bf6ccb67 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 31 Aug 2017 22:30:25 +0200 +Subject: [PATCH 198/199] ipa: make sure view name is initialized at startup + +sysdb_master_domain_update() can only set the view name properly if it was not +set before but it might be called multiple times before the view name is +available if the cache is empty. Since ipa_apply_view() keeps track if +the view name was already set at startup or not the name can safely be +cleaned here before sysdb_master_domain_update() is called. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3501 + +Reviewed-by: Jakub Hrozek +(cherry picked from commit f00591a4615720640cf01b1c408315b57dd397dc) +--- + src/providers/ipa/ipa_subdomains.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c +index 6f0ff50bde234f72d62f43635d9a787316c78430..0cb4d405e45689e9548ad3652e7260f2265bd1fe 100644 +--- a/src/providers/ipa/ipa_subdomains.c ++++ b/src/providers/ipa/ipa_subdomains.c +@@ -739,6 +739,18 @@ done: + return ret; + } + ++static void clean_view_name(struct sss_domain_info *domain) ++{ ++ struct sss_domain_info *dom = domain; ++ ++ while (dom) { ++ dom->has_views = false; ++ talloc_free(discard_const(dom->view_name)); ++ dom->view_name = NULL; ++ dom = get_next_domain(dom, SSS_GND_DESCEND); ++ } ++} ++ + static errno_t ipa_apply_view(struct sss_domain_info *domain, + struct ipa_id_ctx *ipa_id_ctx, + const char *view_name, +@@ -831,7 +843,12 @@ static errno_t ipa_apply_view(struct sss_domain_info *domain, + } + + if (!read_at_init) { +- /* refresh view data of all domains at startup */ ++ /* refresh view data of all domains at startup, since ++ * sysdb_master_domain_update and sysdb_update_subdomains might have ++ * been called earlier without the proper view name the name is ++ * cleaned here before the calls. This is acceptable because this is ++ * the initial setup (!read_at_init). */ ++ clean_view_name(domain); + ret = sysdb_master_domain_update(domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed " +-- +2.13.5 + diff --git a/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch b/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch new file mode 100644 index 0000000..ba0c504 --- /dev/null +++ b/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch @@ -0,0 +1,142 @@ +From a1634a3c4b977364fd7612efa3ee21872a8d578e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Fri, 20 Oct 2017 09:26:43 +0200 +Subject: [PATCH 199/199] CACHE_REQ: Copy the cr_domain list for each request +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Let's copy the cr_domain list for each request as this list may be +free'd due to a refresh domains request. + +Resolves: https://pagure.io/SSSD/sssd/issue/3551 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +(cherry picked from commit 0f44eefe2ce75a0814c8688495477f6c57f3d39a) +--- + src/responder/common/cache_req/cache_req.c | 14 +++++++-- + src/responder/common/cache_req/cache_req_domain.c | 38 +++++++++++++++++++++++ + src/responder/common/cache_req/cache_req_domain.h | 5 +++ + 3 files changed, 55 insertions(+), 2 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index 7d77eb7dd72a7ccf3d687eee8f746ab84176b487..83eab8ed2de0b5c7d25306d853aadc9b53cb4842 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -688,6 +688,7 @@ struct cache_req_state { + const char *domain_name; + + /* work data */ ++ struct cache_req_domain *cr_domains; + struct cache_req_result **results; + size_t num_results; + bool first_iteration; +@@ -940,6 +941,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req, + bool bypass_cache; + bool bypass_dp; + bool search; ++ errno_t ret; + + state = tevent_req_data(req, struct cache_req_state); + +@@ -951,12 +953,20 @@ static errno_t cache_req_select_domains(struct tevent_req *req, + return EOK; + } + ++ ret = cache_req_domain_copy_cr_domains(state, ++ state->cr->rctx->cr_domains, ++ &state->cr_domains); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_copy_cr_domains() failed\n"); ++ return EINVAL; ++ } ++ + if (domain_name != NULL) { + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing a single domain search\n"); + + cr_domain = cache_req_domain_get_domain_by_name( +- state->cr->rctx->cr_domains, domain_name); ++ state->cr_domains, domain_name); + if (cr_domain == NULL) { + return ERR_DOMAIN_NOT_FOUND; + } +@@ -965,7 +975,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req, + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Performing a multi-domain search\n"); + +- cr_domain = state->cr->rctx->cr_domains; ++ cr_domain = state->cr_domains; + check_next = true; + } + +diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c +index c2b5abb74f3bd3d5055f29a4523f29b05feb2014..8c9f155303b174f16b884eb66ba1c88a0256719d 100644 +--- a/src/responder/common/cache_req/cache_req_domain.c ++++ b/src/responder/common/cache_req/cache_req_domain.c +@@ -47,6 +47,44 @@ cache_req_domain_get_domain_by_name(struct cache_req_domain *domains, + return ret; + } + ++errno_t ++cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx, ++ struct cache_req_domain *src, ++ struct cache_req_domain **_dest) ++{ ++ struct cache_req_domain *cr_domains = NULL; ++ struct cache_req_domain *cr_domain; ++ struct cache_req_domain *iter; ++ errno_t ret; ++ ++ if (src == NULL) { ++ return EINVAL; ++ } ++ ++ DLIST_FOR_EACH(iter, src) { ++ cr_domain = talloc_zero(mem_ctx, struct cache_req_domain); ++ if (cr_domain == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cr_domain->domain = iter->domain; ++ cr_domain->fqnames = iter->fqnames; ++ ++ DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *); ++ } ++ ++ *_dest = cr_domains; ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ cache_req_domain_list_zfree(&cr_domains); ++ } ++ ++ return ret; ++} ++ + void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains) + { + struct cache_req_domain *p, *q, *r; +diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h +index 3780a5d8d88d76e100738d28d1dd0e697edf5eae..ebdc71dd635d5d8a5d06e30e96c5d4101b6d98bf 100644 +--- a/src/responder/common/cache_req/cache_req_domain.h ++++ b/src/responder/common/cache_req/cache_req_domain.h +@@ -50,6 +50,11 @@ cache_req_domain_new_list_from_domain_resolution_order( + const char *domain_resolution_order, + struct cache_req_domain **_cr_domains); + ++errno_t ++cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx, ++ struct cache_req_domain *src, ++ struct cache_req_domain **_dest); ++ + void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains); + + +-- +2.13.5 + diff --git a/SOURCES/0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec b/SOURCES/0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec new file mode 100644 index 0000000..f24afe3 --- /dev/null +++ b/SOURCES/0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec @@ -0,0 +1,26 @@ +From 8d38a4b28ab7af15406b244910f369ba1aff02db Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 30 Oct 2014 15:59:17 +0100 +Subject: [PATCH 93/93] NOUPSTREAM: Default to root if sssd user is not + specified + +--- + src/monitor/monitor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index 0dea327213a1ad04b6f69c0ffb0fb87254420796..20b4aef4ee94fd42de1585d7d7c2e01ea01845ac 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -925,7 +925,7 @@ static int get_service_user(struct mt_ctx *ctx) + + ret = confdb_get_string(ctx->cdb, ctx, CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_USER_RUNAS, +- SSSD_USER, &user_str); ++ "root", &user_str); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get the user to run as\n"); + return ret; +-- +1.9.3 + diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec new file mode 100644 index 0000000..b6134a5 --- /dev/null +++ b/SPECS/sssd.spec @@ -0,0 +1,3805 @@ +%global rhel7_minor %(%{__grep} -o "7.[0-9]*" /etc/redhat-release |%{__sed} -s 's/7.//') + +# we don't want to provide private python extension libs +%define __provides_exclude_from %{python_sitearch}/.*\.so$|%{_libdir}/%{name}/modules/libwbclient.so.*$ +%define _hardened_build 1 + + %global install_pcscd_polkit_rule 1 + +# Determine the location of the LDB modules directory +%global ldb_modulesdir %(pkg-config --variable=modulesdir ldb) +%global ldb_version 1.1.17 + + +%if (0%{?fedora} || 0%{?rhel} >= 7) + %global with_cifs_utils_plugin 1 +%else + %global with_cifs_utils_plugin_option --disable-cifs-idmap-plugin +%endif + + %global with_krb5_localauth_plugin 1 + +%global libwbc_alternatives_version 0.13 +%global libwbc_lib_version %{libwbc_alternatives_version}.0 +%global libwbc_alternatives_suffix %nil +%if 0%{?__isa_bits} == 64 +%global libwbc_alternatives_suffix -64 +%endif + +%global enable_systemtap 1 +%if (0%{?enable_systemtap} == 1) + %global enable_systemtap_opt --enable-systemtap +%endif + +%if (0%{?fedora} >= 23 || 0%{?rhel} >= 7) + %global with_kcm 1 + %global with_kcm_option --with-kcm +%else + %global with_kcm_option --without-kcm +%endif + +Name: sssd +Version: 1.15.2 +Release: 50%{?dist}.8 +Group: Applications/System +Summary: System Security Services Daemon +License: GPLv3+ +URL: https://pagure.io/SSSD/sssd/ +Source0: https://releases.pagure.org/SSSD/sssd/sssd-%{version}.tar.gz +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) + +### Patches ### +Patch0001: 0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch +Patch0002: 0002-split_on_separator-move-to-a-separate-file.patch +Patch0003: 0003-util-move-string_in_list-to-util_ext.patch +Patch0004: 0004-certmap-add-new-library-libsss_certmap.patch +Patch0005: 0005-certmap-add-placeholder-for-OpenSSL-implementation.patch +Patch0006: 0006-sysdb-add-sysdb_attrs_copy.patch +Patch0007: 0007-sdap_get_users_send-new-argument-mapped_attrs.patch +Patch0008: 0008-LDAP-always-store-the-certificate-from-the-request.patch +Patch0009: 0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch +Patch0010: 0010-sysdb-add-certmap-related-calls.patch +Patch0011: 0011-IPA-add-certmap-support.patch +Patch0012: 0012-nss-idmap-add-sss_nss_getlistbycert.patch +Patch0013: 0013-nss-allow-larger-buffer-for-certificate-based-reques.patch +Patch0014: 0014-IPA-Add-s2n-request-to-string-function.patch +Patch0015: 0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch +Patch0016: 0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch +Patch0017: 0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch +Patch0018: 0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch +Patch0019: 0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch +Patch0020: 0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch +Patch0021: 0021-UTIL-krb5-principal-un-marshalling.patch +Patch0022: 0022-KCM-Initial-responder-build-and-packaging.patch +Patch0023: 0023-KCM-request-parsing-and-sending-a-reply.patch +Patch0024: 0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch +Patch0025: 0025-KCM-Add-a-in-memory-credential-storage.patch +Patch0026: 0026-KCM-Implement-KCM-server-operations.patch +Patch0027: 0027-MAN-Add-a-manual-page-for-sssd-kcm.patch +Patch0028: 0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch +Patch0029: 0029-SECRETS-Create-DB-path-before-the-operation-itself.patch +Patch0030: 0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch +Patch0031: 0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch +Patch0032: 0032-TCURL-Support-HTTP-POST-for-creating-containers.patch +Patch0033: 0033-KCM-Store-ccaches-in-secrets.patch +Patch0034: 0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch +Patch0035: 0035-KCM-Queue-requests-by-the-same-UID.patch +Patch0036: 0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch +Patch0037: 0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch +Patch0038: 0038-intg-fix-configure-failure-with-strict-cflags.patch +Patch0039: 0039-intg-Remove-bashism-from-intgcheck-prepare.patch +Patch0040: 0040-UTIL-Introduce-subdomain_create_conf_path.patch +Patch0041: 0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch +Patch0042: 0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch +Patch0043: 0043-NSS-TESTS-Fix-subdomains-attribution.patch +Patch0044: 0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch +Patch0045: 0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch +Patch0046: 0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch +Patch0047: 0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch +Patch0048: 0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch +Patch0049: 0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch +Patch0050: 0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch +Patch0051: 0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch +Patch0052: 0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch +Patch0053: 0053-UTIL-Expose-replace_char-as-sss_replace_char.patch +Patch0054: 0054-Add-domain_resolution_order-config-option.patch +Patch0055: 0055-ssh-handle-binary-keys-correctly.patch +Patch0056: 0056-ssh-add-support-for-certificates-from-non-default-vi.patch +Patch0057: 0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch +Patch0058: 0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch +Patch0059: 0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch +Patch0060: 0060-IPA-enable-AD-user-lookup-by-certificate.patch +Patch0061: 0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch +Patch0062: 0062-CONFDB-Allow-configuring-application-sections-as-non.patch +Patch0063: 0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch +Patch0064: 0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch +Patch0065: 0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch +Patch0066: 0066-PAM-Remove-unneeded-memory-context.patch +Patch0067: 0067-PAM-Add-application-services.patch +Patch0068: 0068-SYSDB-Allow-storing-non-POSIX-users.patch +Patch0069: 0069-SYSDB-Only-generate-new-UID-in-local-domain.patch +Patch0070: 0070-LDAP-save-non-POSIX-users-in-application-domains.patch +Patch0071: 0071-LDAP-Relax-search-filters-in-application-domains.patch +Patch0072: 0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch +Patch0073: 0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch +Patch0074: 0074-tcurl-add-support-for-ssl-and-raw-output.patch +Patch0075: 0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch +Patch0076: 0076-tcurl-test-add-support-for-raw-output.patch +Patch0077: 0077-tcurl-test-add-support-for-tls-settings.patch +Patch0078: 0078-tcurl-add-support-for-http-basic-auth.patch +Patch0079: 0079-tcurl-test-allow-to-set-custom-headers.patch +Patch0080: 0080-tcurl-test-add-support-for-client-certificate.patch +Patch0081: 0081-ci-do-not-build-secrets-on-rhel6.patch +Patch0082: 0082-build-make-curl-required-by-secrets.patch +Patch0083: 0083-secrets-use-tcurl-in-proxy-provider.patch +Patch0084: 0084-secrets-remove-http-parser-code-in-proxy-provider.patch +Patch0085: 0085-secrets-allow-to-configure-certificate-check.patch +Patch0086: 0086-secrets-support-HTTP-basic-authentication-with-proxy.patch +Patch0087: 0087-secrets-fix-debug-message.patch +Patch0088: 0088-secrets-always-add-Content-Length-header.patch +Patch0089: 0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch +Patch0090: 0090-configure-fix-typo.patch +Patch0091: 0091-pam_test_client-add-service-and-environment-to-PAM-t.patch +Patch0092: 0092-pam_test_client-add-SSSD-getpwnam-lookup.patch +Patch0093: 0093-sss_sifp-update-method-names.patch +Patch0094: 0094-pam_test_client-add-InfoPipe-user-lookup.patch +Patch0095: 0095-sssctl-integrate-pam_test_client-into-sssctl.patch +Patch0096: 0096-i18n-adding-sssctl-files.patch +Patch0097: 0097-responders-do-not-leak-selinux-context-on-clients-de.patch +Patch0098: 0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch +Patch0099: 0099-config-check-Message-when-sssd.conf-is-missing.patch +Patch0100: 0100-sbus-check-connection-for-NULL-before-unregister-it.patch +Patch0101: 0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch +Patch0102: 0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch +Patch0103: 0103-Move-sized_output_name-and-sized_domain_name-into-re.patch +Patch0104: 0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch +Patch0105: 0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch +Patch0106: 0106-NSS-TESTS-Improve-non-fqnames-tests.patch +Patch0107: 0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch +Patch0108: 0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch +Patch0109: 0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch +Patch0110: 0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch +Patch0111: 0111-SECRETS-remove-unused-variable.patch +Patch0112: 0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch +Patch0113: 0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch +Patch0114: 0114-CONFDB-Fix-standalone-application-domains.patch +Patch0115: 0115-utils-add-sss_domain_is_forest_root.patch +Patch0116: 0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch +Patch0117: 0117-SDAP-Fix-handling-of-search-bases.patch +Patch0118: 0118-overrides-add-certificates-to-mapped-attribute.patch +Patch0119: 0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch +Patch0120: 0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch +Patch0121: 0121-PAM-check-matching-certificates-from-all-domains.patch +Patch0122: 0122-DP-Reduce-Data-Provider-log-level-noise.patch +Patch0123: 0123-NSS-Move-output-name-formatting-to-utils.patch +Patch0124: 0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch +Patch0125: 0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch +Patch0126: 0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch +Patch0127: 0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch +Patch0128: 0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch +Patch0129: 0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch +Patch0130: 0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch +Patch0131: 0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch +Patch0132: 0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch +Patch0133: 0133-cache_req-use-the-right-negative-cache-for-initgroup.patch +Patch0134: 0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch +Patch0135: 0135-pam-properly-support-UPN-logon-names.patch +Patch0136: 0136-KCM-Fix-the-per-client-serialization-queue.patch +Patch0137: 0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch +Patch0138: 0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch +Patch0139: 0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch +Patch0140: 0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch +Patch0141: 0141-IPA-Return-from-function-after-marking-a-request-as-.patch +Patch0142: 0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch +Patch0143: 0143-VALIDATORS-Add-subdomain-section.patch +Patch0144: 0144-VALIDATORS-Remove-application-section-domain.patch +Patch0145: 0145-VALIDATORS-Escape-special-regex-chars.patch +Patch0146: 0146-TESTS-Add-unit-tests-for-cfg-validation.patch +Patch0147: 0147-MAN-Fix-typo-in-trusted-domain-section.patch +Patch0148: 0148-VALIDATORS-Change-regex-for-app-domains.patch +Patch0149: 0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch +Patch0150: 0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch +Patch0151: 0151-test_config_check-Fix-few-issues.patch +Patch0152: 0152-KRB5-Fix-access_provider-krb5.patch +Patch0153: 0153-BUILD-Improve-error-messages-for-optional-dependenci.patch +Patch0154: 0154-RESPONDER_COMMON-update-certmaps-in-responders.patch +Patch0155: 0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch +Patch0156: 0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch +Patch0157: 0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch +Patch0158: 0158-PAM-send-user-name-hint-response-when-needed.patch +Patch0159: 0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch +Patch0160: 0160-sssctl-show-user-name-used-for-authentication-in-use.patch +Patch0161: 0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch +Patch0162: 0162-IFP-Only-format-the-output-name-to-the-short-version.patch +Patch0163: 0163-IFP-Resolve-group-names-from-GIDs-if-required.patch +Patch0164: 0164-ldap-handle-certmap-errors-gracefully.patch +Patch0165: 0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch +Patch0166: 0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch +Patch0167: 0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch +Patch0168: 0168-CACHE_REQ-Simplify-_search_ncache_filter.patch +Patch0169: 0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch +Patch0170: 0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch +Patch0171: 0171-krb5-disable-enterprise-principals-during-password-c.patch +Patch0172: 0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch +Patch0173: 0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch +Patch0174: 0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch +Patch0175: 0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch +Patch0176: 0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch +Patch0177: 0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch +Patch0178: 0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch +Patch0179: 0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch +Patch0180: 0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch +Patch0181: 0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch +Patch0182: 0182-krb5-use-plain-principal-if-password-is-expired.patch +Patch0183: 0183-RESPONDER-Use-fqnames-as-output-when-needed.patch +Patch0184: 0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch +Patch0185: 0185-GPO-Fix-typo-in-DEBUG-message.patch +Patch0186: 0186-SDAP-Update-parent-sdap_list.patch +Patch0187: 0187-RESPONDERS-Fix-terminating-idle-connections.patch +Patch0188: 0188-TESTS-Integration-test-for-idle-timeout.patch +Patch0189: 0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch +Patch0190: 0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch +Patch0191: 0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch +Patch0192: 0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch +Patch0193: 0193-IPA-fix-handling-of-certmap_ctx.patch +Patch0194: 0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch +Patch0195: 0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch +Patch0196: 0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch +Patch0197: 0197-sysdb-sanitize-search-filter-input.patch +Patch0198: 0198-ipa-make-sure-view-name-is-initialized-at-startup.patch +Patch0199: 0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch + +#This patch should not be removed in RHEL-7 +Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec + +### Dependencies ### + +Requires: sssd-common = %{version}-%{release} +Requires: sssd-ldap = %{version}-%{release} +Requires: sssd-krb5 = %{version}-%{release} +Requires: sssd-ipa = %{version}-%{release} +Requires: sssd-ad = %{version}-%{release} +Requires: sssd-proxy = %{version}-%{release} +Requires: python-sssdconfig = %{version}-%{release} + +%global servicename sssd +%global sssdstatedir %{_localstatedir}/lib/sss +%global dbpath %{sssdstatedir}/db +%global keytabdir %{sssdstatedir}/keytabs +%global pipepath %{sssdstatedir}/pipes +%global mcpath %{sssdstatedir}/mc +%global pubconfpath %{sssdstatedir}/pubconf +%global gpocachepath %{sssdstatedir}/gpo_cache +%global secdbpath %{sssdstatedir}/secrets + +### Build Dependencies ### + +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool +BuildRequires: m4 +BuildRequires: popt-devel +BuildRequires: libtalloc-devel +BuildRequires: libtevent-devel +BuildRequires: libtdb-devel + +# LDB needs a strict version match to build +BuildRequires: libldb-devel >= %{ldb_version} +BuildRequires: libdhash-devel >= 0.4.2 +BuildRequires: libcollection-devel +BuildRequires: libini_config-devel >= 1.3.0 +BuildRequires: dbus-devel +BuildRequires: dbus-libs +BuildRequires: openldap-devel +BuildRequires: pam-devel +BuildRequires: nss-devel +BuildRequires: nspr-devel +BuildRequires: pcre-devel +BuildRequires: libxslt +BuildRequires: libxml2 +BuildRequires: docbook-style-xsl +BuildRequires: krb5-devel >= 1.12 +BuildRequires: c-ares-devel +BuildRequires: python-devel +BuildRequires: check-devel +BuildRequires: doxygen +BuildRequires: libselinux-devel +BuildRequires: libsemanage-devel +BuildRequires: bind-utils +BuildRequires: keyutils-libs-devel +BuildRequires: gettext-devel +BuildRequires: pkgconfig +BuildRequires: diffstat +BuildRequires: findutils +BuildRequires: glib2-devel +BuildRequires: selinux-policy-targeted +BuildRequires: libnl3-devel +BuildRequires: systemd-devel +%if (0%{?with_cifs_utils_plugin} == 1) +BuildRequires: cifs-utils-devel +%endif +BuildRequires: libnfsidmap-devel +BuildRequires: samba4-devel >= 4.0.0-59beta2 +BuildRequires: libsmbclient-devel +BuildRequires: systemtap-sdt-devel +BuildRequires: jansson-devel +BuildRequires: http-parser-devel +BuildRequires: curl-devel +BuildRequires: libuuid-devel + +%description +Provides a set of daemons to manage access to remote directories and +authentication mechanisms. It provides an NSS and PAM interface toward +the system and a pluggable backend system to connect to multiple different +account sources. It is also the basis to provide client auditing and policy +services for projects like FreeIPA. + +The sssd subpackage is a meta-package that contains the deamon as well as all +the existing back ends. + +%package common +Summary: Common files for the SSSD +Group: Applications/System +License: GPLv3+ +# Conflicts +Conflicts: selinux-policy < 3.10.0-46 +Conflicts: sssd < 1.10.0-8%{?dist}.beta2 +# Due to ABI changes in rhel-7,5 (1.1.30/1.2.0) +# rhel-7.4 <= will never have libldb 1.2.0 due to samba-4.6.x +Conflicts: libldb >= 1.1.30 +# Requires +Requires: sssd-client%{?_isa} = %{version}-%{release} +Requires: libsss_idmap%{?_isa} = %{version}-%{release} +Requires: libsss_sudo%{?_isa} = %{version}-%{release} +Requires: libsss_autofs%{?_isa} = %{version}-%{release} +Requires(post): systemd-units chkconfig +Requires(preun): systemd-units chkconfig +Requires(postun): systemd-units chkconfig +# sssd-common owns sssd.service file and is restarted in posttrans +# libwbclient alternative might break restarting sssd +# gpo_child -> libsmbclient -> samba-client-libs -> libwbclient +OrderWithRequires: libwbclient +OrderWithRequires: sssd-libwbclient + +### Provides ### +Provides: libsss_sudo-devel = %{version}-%{release} +Obsoletes: libsss_sudo-devel <= 1.10.0-7%{?dist}.beta1 + +%description common +Common files for the SSSD. The common package includes all the files needed +to run a particular back end, however, the back ends are packaged in separate +subpackages such as sssd-ldap. + +%package client +Summary: SSSD Client libraries for NSS and PAM +Group: Applications/System +License: LGPLv3+ +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +Requires(post): /usr/sbin/alternatives +Requires(preun): /usr/sbin/alternatives + +%description client +Provides the libraries needed by the PAM and NSS stacks to connect to the SSSD +service. + +%package -n libsss_sudo +Summary: A library to allow communication between SUDO and SSSD +Group: Development/Libraries +License: LGPLv3+ +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n libsss_sudo +A utility library to allow communication between SUDO and SSSD + +%package -n libsss_autofs +Summary: A library to allow communication between Autofs and SSSD +Group: Development/Libraries +License: LGPLv3+ + +%description -n libsss_autofs +A utility library to allow communication between Autofs and SSSD + +%package tools +Summary: Userspace tools for use with the SSSD +Group: Applications/System +License: GPLv3+ +Requires: sssd-common = %{version}-%{release} +Requires: python-sss = %{version}-%{release} +Requires: python-sssdconfig = %{version}-%{release} + +%description tools +Provides userspace tools for manipulating users, groups, and nested groups in +SSSD when using id_provider = local in /etc/sssd/sssd.conf. + +Also provides several other administrative tools: + * sss_debuglevel to change the debug level on the fly + * sss_seed which pre-creates a user entry for use in kickstarts + * sss_obfuscate for generating an obfuscated LDAP password + * sssctl -- an sssd status and control utility + +%package -n python-sssdconfig +Summary: SSSD and IPA configuration file manipulation classes and functions +Group: Applications/System +License: GPLv3+ +BuildArch: noarch + +%description -n python-sssdconfig +Provides python2 files for manipulation SSSD and IPA configuration files. + +%package -n python-sss +Summary: Python2 bindings for sssd +Group: Development/Libraries +License: LGPLv3+ +Requires: sssd-common = %{version}-%{release} + +%description -n python-sss +Provides python2 module for manipulating users, groups, and nested groups in +SSSD when using id_provider = local in /etc/sssd/sssd.conf. + +Also provides several other useful python2 bindings: + * function for retrieving list of groups user belongs to. + * class for obfuscation of passwords + +%package -n python-sss-murmur +Summary: Python2 bindings for murmur hash function +Group: Development/Libraries +License: LGPLv3+ + +%description -n python-sss-murmur +Provides python2 module for calculating the murmur hash version 3 + +%package ldap +Summary: The LDAP back end of the SSSD +Group: Applications/System +License: GPLv3+ +Conflicts: sssd < 1.10.0-8.beta2 +Requires: sssd-common = %{version}-%{release} +Requires: sssd-krb5-common = %{version}-%{release} + +%description ldap +Provides the LDAP back end that the SSSD can utilize to fetch identity data +from and authenticate against an LDAP server. + +%package krb5-common +Summary: SSSD helpers needed for Kerberos and GSSAPI authentication +Group: Applications/System +License: GPLv3+ +Conflicts: sssd < 1.10.0-8.beta2 +Requires: cyrus-sasl-gssapi%{?_isa} +Requires: sssd-common = %{version}-%{release} + +%description krb5-common +Provides helper processes that the LDAP and Kerberos back ends can use for +Kerberos user or host authentication. + +%package krb5 +Summary: The Kerberos authentication back end for the SSSD +Group: Applications/System +License: GPLv3+ +Conflicts: sssd < 1.10.0-8.beta2 +Requires: sssd-common = %{version}-%{release} +Requires: sssd-krb5-common = %{version}-%{release} + +%description krb5 +Provides the Kerberos back end that the SSSD can utilize authenticate +against a Kerberos server. + +%package common-pac +Summary: Common files needed for supporting PAC processing +Group: Applications/System +License: GPLv3+ +Requires: sssd-common = %{version}-%{release} + +%description common-pac +Provides common files needed by SSSD providers such as IPA and Active Directory +for handling Kerberos PACs. + +%package ipa +Summary: The IPA back end of the SSSD +Group: Applications/System +License: GPLv3+ +Conflicts: sssd < 1.10.0-8.beta2 +Requires: sssd-common = %{version}-%{release} +Requires: sssd-krb5-common = %{version}-%{release} +Requires: libipa_hbac%{?_isa} = %{version}-%{release} +Requires: bind-utils +Requires: sssd-common-pac = %{version}-%{release} +Requires(pre): shadow-utils + +%description ipa +Provides the IPA back end that the SSSD can utilize to fetch identity data +from and authenticate against an IPA server. + +%package ad +Summary: The AD back end of the SSSD +Group: Applications/System +License: GPLv3+ +Conflicts: sssd < 1.10.0-8.beta2 +Requires: sssd-common = %{version}-%{release} +Requires: sssd-krb5-common = %{version}-%{release} +Requires: bind-utils +Requires: sssd-common-pac = %{version}-%{release} + +%description ad +Provides the Active Directory back end that the SSSD can utilize to fetch +identity data from and authenticate against an Active Directory server. + +%package proxy +Summary: The proxy back end of the SSSD +Group: Applications/System +License: GPLv3+ +Conflicts: sssd < 1.10.0-8.beta2 +Requires: sssd-common = %{version}-%{release} + +%description proxy +Provides the proxy back end which can be used to wrap an existing NSS and/or +PAM modules to leverage SSSD caching. + +%package -n libsss_idmap +Summary: FreeIPA Idmap library +Group: Development/Libraries +License: LGPLv3+ +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n libsss_idmap +Utility library to convert SIDs to Unix uids and gids + +%package -n libsss_idmap-devel +Summary: FreeIPA Idmap library +Group: Development/Libraries +License: LGPLv3+ +Requires: libsss_idmap = %{version}-%{release} + +%description -n libsss_idmap-devel +Utility library to SIDs to Unix uids and gids + +%package -n libipa_hbac +Summary: FreeIPA HBAC Evaluator library +Group: Development/Libraries +License: LGPLv3+ +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n libipa_hbac +Utility library to validate FreeIPA HBAC rules for authorization requests + +%package -n libipa_hbac-devel +Summary: FreeIPA HBAC Evaluator library +Group: Development/Libraries +License: LGPLv3+ +Requires: libipa_hbac = %{version}-%{release} + +%description -n libipa_hbac-devel +Utility library to validate FreeIPA HBAC rules for authorization requests + +%package -n python-libipa_hbac +Summary: Python2 bindings for the FreeIPA HBAC Evaluator library +Group: Development/Libraries +License: LGPLv3+ +Requires: libipa_hbac = %{version}-%{release} +Provides: libipa_hbac-python = %{version}-%{release} +Obsoletes: libipa_hbac-python < 1.12.90 + +%description -n python-libipa_hbac +The python-libipa_hbac contains the bindings so that libipa_hbac can be +used by Python applications. + +%package -n libsss_nss_idmap +Summary: Library for SID and certificate based lookups +Group: Development/Libraries +License: LGPLv3+ +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n libsss_nss_idmap +Utility library for SID and certificate based lookups + +%package -n libsss_nss_idmap-devel +Summary: Library for SID and certificate based lookups +Group: Development/Libraries +License: LGPLv3+ +Requires: libsss_nss_idmap = %{version}-%{release} + +%description -n libsss_nss_idmap-devel +Utility library for SID and certificate based lookups + +%package -n python-libsss_nss_idmap +Summary: Python2 bindings for libsss_nss_idmap +Group: Development/Libraries +License: LGPLv3+ +Requires: libsss_nss_idmap = %{version}-%{release} +Provides: libsss_nss_idmap-python = %{version}-%{release} +Obsoletes: libsss_nss_idmap-python < 1.12.90 + +%description -n python-libsss_nss_idmap +The python-libsss_nss_idmap contains the bindings so that libsss_nss_idmap can +be used by Python applications. + +%package dbus +Summary: The D-Bus responder of the SSSD +Group: Applications/System +License: GPLv3+ +Requires: sssd-common = %{version}-%{release} + +%description dbus +Provides the D-Bus responder of the SSSD, called the InfoPipe, that allows +the information from the SSSD to be transmitted over the system bus. + +%if (0%{?install_pcscd_polkit_rule} == 1) +%package polkit-rules +Summary: Rules for polkit integration for SSSD +Group: Applications/System +License: GPLv3+ +Requires: polkit >= 0.106 +Requires: sssd-common = %{version}-%{release} + +%description polkit-rules +Provides rules for polkit integration with SSSD. This is required +for smartcard support. +%endif + +%package -n libsss_simpleifp +Summary: The SSSD D-Bus responder helper library +Group: Development/Libraries +License: GPLv3+ +Requires: sssd-dbus = %{version}-%{release} +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n libsss_simpleifp +Provides library that simplifies D-Bus API for the SSSD InfoPipe responder. + +%package -n libsss_simpleifp-devel +Summary: The SSSD D-Bus responder helper library +Group: Development/Libraries +License: GPLv3+ +Requires: dbus-devel +Requires: libsss_simpleifp = %{version}-%{release} + +%description -n libsss_simpleifp-devel +Provides library that simplifies D-Bus API for the SSSD InfoPipe responder. + +%package libwbclient +Summary: The SSSD libwbclient implementation +Group: Applications/System +License: GPLv3+ and LGPLv3+ +Conflicts: libwbclient < 4.1.12 + +%description libwbclient +The SSSD libwbclient implementation. + +%package libwbclient-devel +Summary: Development libraries for the SSSD libwbclient implementation +Group: Development/Libraries +License: GPLv3+ and LGPLv3+ +Conflicts: libwbclient-devel < 4.1.12 + +%description libwbclient-devel +Development libraries for the SSSD libwbclient implementation. + +%package winbind-idmap +Summary: SSSD's idmap_sss Backend for Winbind +Group: Applications/System +License: GPLv3+ and LGPLv3+ + +%description winbind-idmap +The idmap_sss module provides a way for Winbind to call SSSD to map UIDs/GIDs +and SIDs. + +%package -n libsss_certmap +Summary: SSSD Certficate Mapping Library +Group: Development/Libraries +License: LGPLv3+ +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n libsss_certmap +Library to map certificates to users based on rules + +%package -n libsss_certmap-devel +Summary: SSSD Certficate Mapping Library +Group: Development/Libraries +License: LGPLv3+ +Requires: libsss_certmap = %{version}-%{release} + +%description -n libsss_certmap-devel +Library to map certificates to users based on rules + +%if (0%{?with_kcm} == 1) +%package kcm +Summary: An implementation of a Kerberos KCM server +Group: Applications/System +License: GPLv3+ +Requires: sssd-common = %{version}-%{release} + +%description kcm +An implementation of a Kerberos KCM server. Use this package if you want to +use the KCM: Kerberos credentials cache. +%endif + +%prep +# Update timestamps on the files touched by a patch, to avoid non-equal +# .pyc/.pyo files across the multilib peers within a build, where "Level" +# is the patch prefix option (e.g. -p1) +# Taken from specfile for python-simplejson +UpdateTimestamps() { + Level=$1 + PatchFile=$2 + + # Locate the affected files: + for f in $(diffstat $Level -l $PatchFile); do + # Set the files to have the same timestamp as that of the patch: + touch -r $PatchFile $f + done +} + +%setup -q + +for p in %patches ; do + %__patch -p1 -i $p + UpdateTimestamps -p1 $p +done + +%build +autoreconf -ivf + +%configure \ + --with-test-dir=/dev/shm \ + --with-db-path=%{dbpath} \ + --with-mcache-path=%{mcpath} \ + --with-pipe-path=%{pipepath} \ + --with-pubconf-path=%{pubconfpath} \ + --with-gpo-cache-path=%{gpocachepath} \ + --with-init-dir=%{_initrddir} \ + --with-krb5-rcache-dir=%{_localstatedir}/cache/krb5rcache \ + --enable-nsslibdir=%{_libdir} \ + --enable-pammoddir=%{_libdir}/security \ + --enable-nfsidmaplibdir=%{_libdir}/libnfsidmap \ + --disable-static \ + --disable-rpath \ + --with-sssd-user=sssd \ + --with-initscript=systemd \ + --with-syslog=journald \ + --enable-sss-default-nss-plugin \ + %{?with_cifs_utils_plugin_option} \ + --without-python3-bindings \ + --with-ad-gpo-default=permissive \ + %{?enable_polkit_rules_option} \ + %{?enable_systemtap_opt} \ + %{?with_kcm_option} + +make %{?_smp_mflags} all docs + +%check +export CK_TIMEOUT_MULTIPLIER=10 +make %{?_smp_mflags} check VERBOSE=yes +unset CK_TIMEOUT_MULTIPLIER + +%install + +make install DESTDIR=$RPM_BUILD_ROOT + +if [ ! -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version} ] +then + echo "Expected libwbclient version not found, please check if version has changed." + exit -1 +fi + +# Prepare language files +/usr/lib/rpm/find-lang.sh $RPM_BUILD_ROOT sssd + +# Copy default logrotate file +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d +install -m644 src/examples/logrotate $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/sssd + +# Make sure SSSD is able to run on read-only root +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rwtab.d +install -m644 src/examples/rwtab $RPM_BUILD_ROOT%{_sysconfdir}/rwtab.d/sssd + +%if (0%{?with_cifs_utils_plugin} == 1) +# Create directory for cifs-idmap alternative +# Otherwise this directory could not be owned by sssd-client +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cifs-utils +%endif + +# Remove .la files created by libtool +find $RPM_BUILD_ROOT -name "*.la" -exec rm -f {} \; + +# Suppress developer-only documentation +rm -Rf ${RPM_BUILD_ROOT}/%{_docdir}/%{name} + +# Older versions of rpmbuild can only handle one -f option +# So we need to append to the sssd*.lang file +for file in `ls $RPM_BUILD_ROOT/%{python_sitelib}/*.egg-info 2> /dev/null` +do + echo %{python_sitelib}/`basename $file` >> python_sssdconfig.lang +done + +touch sssd.lang +for subpackage in sssd_ldap sssd_krb5 sssd_ipa sssd_ad sssd_proxy sssd_tools \ + sssd_client sssd_dbus sssd_winbind_idmap \ + libsss_certmap sssd_kcm +do + touch $subpackage.lang +done + +for man in `find $RPM_BUILD_ROOT/%{_mandir}/??/man?/ -type f | sed -e "s#$RPM_BUILD_ROOT/%{_mandir}/##"` +do + lang=`echo $man | cut -c 1-2` + case `basename $man` in + sss_cache*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd.lang + ;; + sss_ssh*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd.lang + ;; + sss_*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_tools.lang + ;; + sssctl*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_tools.lang + ;; + sssd_krb5_*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_client.lang + ;; + pam_sss*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_client.lang + ;; + sssd-ldap*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_ldap.lang + ;; + sssd-krb5*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_krb5.lang + ;; + sssd-ipa*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_ipa.lang + ;; + sssd-ad*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_ad.lang + ;; + sssd-proxy*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_proxy.lang + ;; + sssd-ifp*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_dbus.lang + ;; + sssd-kcm*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_kcm.lang + ;; + idmap_sss*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_winbind_idmap.lang + ;; + sss-certmap*) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> libsss_certmap.lang + ;; + *) + echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd.lang + ;; + esac +done + +# Print these to the rpmbuild log +echo "sssd.lang:" +cat sssd.lang + +echo "python_sssdconfig.lang:" +cat python_sssdconfig.lang + +for subpackage in sssd_ldap sssd_krb5 sssd_ipa sssd_ad sssd_proxy sssd_tools \ + sssd_client sssd_dbus sssd_winbind_idmap \ + libsss_certmap sssd_kcm +do + echo "$subpackage.lang:" + cat $subpackage.lang +done + +%files +%defattr(-,root,root,-) +%license COPYING + +%files common -f sssd.lang +%defattr(-,root,root,-) +%license COPYING +%doc src/examples/sssd-example.conf +%{_sbindir}/sssd +%{_unitdir}/sssd.service +%{_unitdir}/sssd-autofs.socket +%{_unitdir}/sssd-autofs.service +%{_unitdir}/sssd-nss.socket +%{_unitdir}/sssd-nss.service +%{_unitdir}/sssd-pac.socket +%{_unitdir}/sssd-pac.service +%{_unitdir}/sssd-pam.socket +%{_unitdir}/sssd-pam-priv.socket +%{_unitdir}/sssd-pam.service +%{_unitdir}/sssd-ssh.socket +%{_unitdir}/sssd-ssh.service +%{_unitdir}/sssd-sudo.socket +%{_unitdir}/sssd-sudo.service +%{_unitdir}/sssd-secrets.socket +%{_unitdir}/sssd-secrets.service + +%dir %{_libexecdir}/%{servicename} +%{_libexecdir}/%{servicename}/sssd_be +%{_libexecdir}/%{servicename}/sssd_nss +%{_libexecdir}/%{servicename}/sssd_pam +%{_libexecdir}/%{servicename}/sssd_autofs +%{_libexecdir}/%{servicename}/sssd_secrets +%{_libexecdir}/%{servicename}/sssd_ssh +%{_libexecdir}/%{servicename}/sssd_sudo +%{_libexecdir}/%{servicename}/p11_child +%{_libexecdir}/%{servicename}/sssd_check_socket_activated_responders + +%dir %{_libdir}/%{name} +# The files provider is intentionally packaged in -common +%{_libdir}/%{name}/libsss_files.so +%{_libdir}/%{name}/libsss_simple.so + +#Internal shared libraries +%{_libdir}/%{name}/libsss_child.so +%{_libdir}/%{name}/libsss_crypt.so +%{_libdir}/%{name}/libsss_cert.so +%{_libdir}/%{name}/libsss_debug.so +%{_libdir}/%{name}/libsss_krb5_common.so +%{_libdir}/%{name}/libsss_ldap_common.so +%{_libdir}/%{name}/libsss_util.so +%{_libdir}/%{name}/libsss_semanage.so + +# 3rd party application libraries +%{_libdir}/libnfsidmap/sss.so + +%{ldb_modulesdir}/memberof.so +%{_bindir}/sss_ssh_authorizedkeys +%{_bindir}/sss_ssh_knownhostsproxy +%{_sbindir}/sss_cache +%{_libexecdir}/%{servicename}/sss_signal + +%dir %{sssdstatedir} +%dir %{_localstatedir}/cache/krb5rcache +%attr(700,sssd,sssd) %dir %{dbpath} +%attr(755,sssd,sssd) %dir %{mcpath} +%attr(700,root,root) %dir %{secdbpath} +%ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd +%ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group +%ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups +%attr(755,sssd,sssd) %dir %{pipepath} +%attr(750,sssd,root) %dir %{pipepath}/private +%attr(755,sssd,sssd) %dir %{pubconfpath} +%attr(755,sssd,sssd) %dir %{gpocachepath} +%attr(750,sssd,sssd) %dir %{_var}/log/%{name} +%attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd +%attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd/conf.d +%ghost %attr(0600,sssd,sssd) %config(noreplace) %{_sysconfdir}/sssd/sssd.conf +%attr(755,root,root) %dir %{_sysconfdir}/systemd/system/sssd.service.d +%config(noreplace) %{_sysconfdir}/systemd/system/sssd.service.d/journal.conf +%dir %{_sysconfdir}/logrotate.d +%config(noreplace) %{_sysconfdir}/logrotate.d/sssd +%dir %{_sysconfdir}/rwtab.d +%config(noreplace) %{_sysconfdir}/rwtab.d/sssd +%dir %{_datadir}/sssd +%{_sysconfdir}/pam.d/sssd-shadowutils +%{_libdir}/%{name}/conf/sssd.conf + +%{_datadir}/sssd/cfg_rules.ini +%{_datadir}/sssd/sssd.api.conf +%{_datadir}/sssd/sssd.api.d +%{_mandir}/man1/sss_ssh_authorizedkeys.1* +%{_mandir}/man1/sss_ssh_knownhostsproxy.1* +%{_mandir}/man5/sssd.conf.5* +%{_mandir}/man5/sssd-files.5* +%{_mandir}/man5/sssd-simple.5* +%{_mandir}/man5/sssd-sudo.5* +%{_mandir}/man5/sssd-secrets.5* +%{_mandir}/man5/sss_rpcidmapd.5* +%{_mandir}/man8/sssd.8* +%{_mandir}/man8/sss_cache.8* +%if (0%{?enable_systemtap} == 1) +%dir %{_datadir}/sssd/systemtap +%{_datadir}/sssd/systemtap/id_perf.stp +%{_datadir}/sssd/systemtap/nested_group_perf.stp +%dir %{_datadir}/systemtap +%dir %{_datadir}/systemtap/tapset +%{_datadir}/systemtap/tapset/sssd.stp +%{_datadir}/systemtap/tapset/sssd_functions.stp +%endif + +%if (0%{?install_pcscd_polkit_rule} == 1) +%files polkit-rules +%{_datadir}/polkit-1/rules.d/* +%endif + +%files ldap -f sssd_ldap.lang +%defattr(-,root,root,-) +%license COPYING +%{_libdir}/%{name}/libsss_ldap.so +%{_mandir}/man5/sssd-ldap.5* + +%files krb5-common +%defattr(-,root,root,-) +%license COPYING +%attr(755,sssd,sssd) %dir %{pubconfpath}/krb5.include.d +%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/ldap_child +%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/krb5_child + +%files krb5 -f sssd_krb5.lang +%defattr(-,root,root,-) +%license COPYING +%{_libdir}/%{name}/libsss_krb5.so +%{_mandir}/man5/sssd-krb5.5* + +%files common-pac +%defattr(-,root,root,-) +%license COPYING +%{_libexecdir}/%{servicename}/sssd_pac + +%files ipa -f sssd_ipa.lang +%defattr(-,root,root,-) +%license COPYING +%attr(700,sssd,sssd) %dir %{keytabdir} +%{_libdir}/%{name}/libsss_ipa.so +%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/selinux_child +%{_mandir}/man5/sssd-ipa.5* + +%files ad -f sssd_ad.lang +%defattr(-,root,root,-) +%license COPYING +%{_libdir}/%{name}/libsss_ad.so +%{_libexecdir}/%{servicename}/gpo_child +%{_mandir}/man5/sssd-ad.5* + +%files proxy +%defattr(-,root,root,-) +%license COPYING +%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/proxy_child +%{_libdir}/%{name}/libsss_proxy.so + +%files dbus -f sssd_dbus.lang +%defattr(-,root,root,-) +%license COPYING +%{_libexecdir}/%{servicename}/sssd_ifp +%{_mandir}/man5/sssd-ifp.5* +%{_unitdir}/sssd-ifp.service +# InfoPipe DBus plumbing +%{_sysconfdir}/dbus-1/system.d/org.freedesktop.sssd.infopipe.conf +%{_datadir}/dbus-1/system-services/org.freedesktop.sssd.infopipe.service + +%files -n libsss_simpleifp +%defattr(-,root,root,-) +%{_libdir}/libsss_simpleifp.so.* + +%files -n libsss_simpleifp-devel +%defattr(-,root,root,-) +%doc sss_simpleifp_doc/html +%{_includedir}/sss_sifp.h +%{_includedir}/sss_sifp_dbus.h +%{_libdir}/libsss_simpleifp.so +%{_libdir}/pkgconfig/sss_simpleifp.pc + +%files client -f sssd_client.lang +%defattr(-,root,root,-) +%license src/sss_client/COPYING src/sss_client/COPYING.LESSER +%{_libdir}/libnss_sss.so.2 +%{_libdir}/security/pam_sss.so +%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so +%{_libdir}/krb5/plugins/authdata/sssd_pac_plugin.so +%if (0%{?with_cifs_utils_plugin} == 1) +%dir %{_libdir}/cifs-utils +%{_libdir}/cifs-utils/cifs_idmap_sss.so +%dir %{_sysconfdir}/cifs-utils +%ghost %{_sysconfdir}/cifs-utils/idmap-plugin +%endif +%if (0%{?with_krb5_localauth_plugin} == 1) +%dir %{_libdir}/%{name} +%dir %{_libdir}/%{name}/modules +%{_libdir}/%{name}/modules/sssd_krb5_localauth_plugin.so +%endif +%{_mandir}/man8/pam_sss.8* +%{_mandir}/man8/sssd_krb5_locator_plugin.8* + +%files -n libsss_sudo +%defattr(-,root,root,-) +%license src/sss_client/COPYING +%{_libdir}/libsss_sudo.so* + +%files -n libsss_autofs +%defattr(-,root,root,-) +%license src/sss_client/COPYING src/sss_client/COPYING.LESSER +%dir %{_libdir}/%{name}/modules +%{_libdir}/%{name}/modules/libsss_autofs.so + +%files tools -f sssd_tools.lang +%defattr(-,root,root,-) +%license COPYING +%{_sbindir}/sss_useradd +%{_sbindir}/sss_userdel +%{_sbindir}/sss_usermod +%{_sbindir}/sss_groupadd +%{_sbindir}/sss_groupdel +%{_sbindir}/sss_groupmod +%{_sbindir}/sss_groupshow +%{_sbindir}/sss_obfuscate +%{_sbindir}/sss_override +%{_sbindir}/sss_debuglevel +%{_sbindir}/sss_seed +%{_sbindir}/sssctl +%{_mandir}/man8/sss_groupadd.8* +%{_mandir}/man8/sss_groupdel.8* +%{_mandir}/man8/sss_groupmod.8* +%{_mandir}/man8/sss_groupshow.8* +%{_mandir}/man8/sss_useradd.8* +%{_mandir}/man8/sss_userdel.8* +%{_mandir}/man8/sss_usermod.8* +%{_mandir}/man8/sss_obfuscate.8* +%{_mandir}/man8/sss_override.8* +%{_mandir}/man8/sss_debuglevel.8* +%{_mandir}/man8/sss_seed.8* +%{_mandir}/man8/sssctl.8* + +%files -n python-sssdconfig -f python_sssdconfig.lang +%defattr(-,root,root,-) +%dir %{python_sitelib}/SSSDConfig +%{python_sitelib}/SSSDConfig/*.py* + +%files -n python-sss +%defattr(-,root,root,-) +%{python_sitearch}/pysss.so + +%files -n python-sss-murmur +%defattr(-,root,root,-) +%{python_sitearch}/pysss_murmur.so + +%files -n libsss_idmap +%defattr(-,root,root,-) +%license src/sss_client/COPYING src/sss_client/COPYING.LESSER +%{_libdir}/libsss_idmap.so.* + +%files -n libsss_idmap-devel +%defattr(-,root,root,-) +%doc idmap_doc/html +%{_includedir}/sss_idmap.h +%{_libdir}/libsss_idmap.so +%{_libdir}/pkgconfig/sss_idmap.pc + +%files -n libipa_hbac +%defattr(-,root,root,-) +%license src/sss_client/COPYING src/sss_client/COPYING.LESSER +%{_libdir}/libipa_hbac.so.* + +%files -n libipa_hbac-devel +%defattr(-,root,root,-) +%doc hbac_doc/html +%{_includedir}/ipa_hbac.h +%{_libdir}/libipa_hbac.so +%{_libdir}/pkgconfig/ipa_hbac.pc + +%files -n libsss_nss_idmap +%defattr(-,root,root,-) +%license src/sss_client/COPYING src/sss_client/COPYING.LESSER +%{_libdir}/libsss_nss_idmap.so.* + +%files -n libsss_nss_idmap-devel +%defattr(-,root,root,-) +%doc nss_idmap_doc/html +%{_includedir}/sss_nss_idmap.h +%{_libdir}/libsss_nss_idmap.so +%{_libdir}/pkgconfig/sss_nss_idmap.pc + +%files -n python-libsss_nss_idmap +%defattr(-,root,root,-) +%{python_sitearch}/pysss_nss_idmap.so + +%files -n python-libipa_hbac +%defattr(-,root,root,-) +%{python_sitearch}/pyhbac.so + +%files libwbclient +%defattr(-,root,root,-) +%dir %{_libdir}/%{name} +%dir %{_libdir}/%{name}/modules +%{_libdir}/%{name}/modules/libwbclient.so.* + +%files libwbclient-devel +%defattr(-,root,root,-) +%{_includedir}/wbclient_sssd.h +%{_libdir}/%{name}/modules/libwbclient.so +%{_libdir}/pkgconfig/wbclient_sssd.pc + +%files winbind-idmap -f sssd_winbind_idmap.lang +%dir %{_libdir}/samba/idmap +%{_libdir}/samba/idmap/sss.so +%{_mandir}/man8/idmap_sss.8* + +%files -n libsss_certmap -f libsss_certmap.lang +%defattr(-,root,root,-) +%license src/sss_client/COPYING src/sss_client/COPYING.LESSER +%{_libdir}/libsss_certmap.so.* +%{_mandir}/man5/sss-certmap.5* + +%files -n libsss_certmap-devel +%defattr(-,root,root,-) +%doc certmap_doc/html +%{_includedir}/sss_certmap.h +%{_libdir}/libsss_certmap.so +%{_libdir}/pkgconfig/sss_certmap.pc + +%pre ipa +getent group sssd >/dev/null || groupadd -r sssd +getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd + +%pre krb5-common +getent group sssd >/dev/null || groupadd -r sssd +getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd + +%if (0%{?with_kcm} == 1) +%files kcm -f sssd_kcm.lang +%{_libexecdir}/%{servicename}/sssd_kcm +%dir %{_sysconfdir}/krb5.conf.d +%config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache +%{_unitdir}/sssd-kcm.socket +%{_unitdir}/sssd-kcm.service +%{_mandir}/man8/sssd-kcm.8* +%endif + +%pre common +getent group sssd >/dev/null || groupadd -r sssd +getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd +/bin/systemctl is-active --quiet sssd.service && touch /var/tmp/sssd_is_running || : + +%post common +%systemd_post sssd.service +%systemd_post sssd-autofs.socket +%systemd_post sssd-nss.socket +%systemd_post sssd-pac.socket +%systemd_post sssd-pam.socket +%systemd_post sssd-pam-priv.socket +%systemd_post sssd-secrets.socket +%systemd_post sssd-ssh.socket +%systemd_post sssd-sudo.socket + +%preun common +%systemd_preun sssd.service +%systemd_preun sssd-autofs.socket +%systemd_preun sssd-nss.socket +%systemd_preun sssd-pac.socket +%systemd_preun sssd-pam.socket +%systemd_preun sssd-pam-priv.socket +%systemd_preun sssd-secrets.socket +%systemd_preun sssd-ssh.socket +%systemd_preun sssd-sudo.socket + +%postun common +%systemd_postun_with_restart sssd-autofs.socket +%systemd_postun_with_restart sssd-autofs.service +%systemd_postun_with_restart sssd-nss.socket +%systemd_postun_with_restart sssd-nss.service +%systemd_postun_with_restart sssd-pac.socket +%systemd_postun_with_restart sssd-pac.service +%systemd_postun_with_restart sssd-pam.socket +%systemd_postun_with_restart sssd-pam-priv.socket +%systemd_postun_with_restart sssd-pam.service +%systemd_postun_with_restart sssd-secrets.socket +%systemd_postun_with_restart sssd-secrets.service +%systemd_postun_with_restart sssd-ssh.socket +%systemd_postun_with_restart sssd-ssh.service +%systemd_postun_with_restart sssd-sudo.socket +%systemd_postun_with_restart sssd-sudo.service + +%post dbus +%systemd_post sssd-ifp.service + +%preun dbus +%systemd_preun sssd-ifp.service + +%postun dbus +%systemd_postun_with_restart sssd-ifp.service + +%if (0%{?with_kcm} == 1) +%post kcm +%systemd_post sssd-kcm.socket + +%preun kcm +%systemd_preun sssd-kcm.socket + +%postun kcm +%systemd_postun_with_restart sssd-kcm.socket +%systemd_postun_with_restart sssd-kcm.service +%endif + +%if (0%{?with_cifs_utils_plugin} == 1) +%post client +/sbin/ldconfig +/usr/sbin/alternatives --install /etc/cifs-utils/idmap-plugin cifs-idmap-plugin %{_libdir}/cifs-utils/cifs_idmap_sss.so 20 + +%preun client +if [ $1 -eq 0 ] ; then + /usr/sbin/alternatives --remove cifs-idmap-plugin %{_libdir}/cifs-utils/cifs_idmap_sss.so +fi +%else +%post client -p /sbin/ldconfig +%endif + +%postun client -p /sbin/ldconfig + +%post -n libsss_sudo -p /sbin/ldconfig + +%postun -n libsss_sudo -p /sbin/ldconfig + +%post -n libipa_hbac -p /sbin/ldconfig + +%postun -n libipa_hbac -p /sbin/ldconfig + +%post -n libsss_idmap -p /sbin/ldconfig + +%postun -n libsss_idmap -p /sbin/ldconfig + +%post -n libsss_nss_idmap -p /sbin/ldconfig + +%postun -n libsss_nss_idmap -p /sbin/ldconfig + +%post -n libsss_simpleifp -p /sbin/ldconfig + +%postun -n libsss_simpleifp -p /sbin/ldconfig + +%post -n libsss_certmap -p /sbin/ldconfig + +%postun -n libsss_certmap -p /sbin/ldconfig + +%post libwbclient +%{_sbindir}/update-alternatives \ + --install %{_libdir}/libwbclient.so.%{libwbc_alternatives_version} \ + libwbclient.so.%{libwbc_alternatives_version}%{libwbc_alternatives_suffix} \ + %{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version} 20 +/sbin/ldconfig + +%preun libwbclient +if [ $1 -eq 0 ]; then + %{_sbindir}/update-alternatives \ + --remove libwbclient.so.%{libwbc_alternatives_version}%{libwbc_alternatives_suffix} \ + %{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version} +fi +/sbin/ldconfig + +%post libwbclient-devel +%{_sbindir}/update-alternatives --install %{_libdir}/libwbclient.so \ + libwbclient.so%{libwbc_alternatives_suffix} \ + %{_libdir}/%{name}/modules/libwbclient.so 20 + +%preun libwbclient-devel +if [ $1 -eq 0 ]; then + %{_sbindir}/update-alternatives --remove \ + libwbclient.so%{libwbc_alternatives_suffix} \ + %{_libdir}/%{name}/modules/libwbclient.so +fi + +%posttrans common +%systemd_postun_with_restart sssd.service +# After changing order of sssd-common and *libwbclient, +# older version of sssd will restart sssd.service in postun scriptlet +# It failed due to missing alternative to libwbclient. Start it again. +/bin/systemctl is-active --quiet sssd.service || { + if [ -f /var/tmp/sssd_is_running ]; then + systemctl start sssd.service >/dev/null 2>&1; + rm -f /var/tmp/sssd_is_running; + fi +} + +%changelog +* Fri Nov 03 2017 Fabiano Fidêncio - 1.15.2-50.8 +- Resolves: rhbz#1508972 - Accessing IdM kerberos ticket fails while id + mapping is applied [rhel-7.4.z] +- Resolves: rhbz#1509177 - Race condition between refreshing the cr_domain + list and a request that is using the list can + cause a segfault is sssd_nss [rhel-7.4.z] + +* Fri Oct 27 2017 Fabiano Fidêncio - 1.15.2-50.7 +- Resolves: rhbz#1506142 - SSSD can crash due to ABI changes in + libldb >= 1.2.0 (1.1.30) [rhel-7.4.z] +- Resolves: rhbz#1506682 - sssd_client: add mutex protected call to the + PAC responder [rhel-7.4.z] +- Resolves: rhbz#1499658 - CVE-2017-12173 sssd: unsanitized input when + searching in local cache database [rhel-7.4.z] + +* Wed Sep 27 2017 Fabiano Fidêncio - 1.15.2-50.6 +- Add a patch that was missed in 1.15.2-50.4 +- Related: rhbz#1489290 - samba shares with sssd authentication broken + on 7.4 [rhel-7.4.z] + +* Thu Sep 21 2017 Fabiano Fidêncio - 1.15.2-50.5 +- Resolves: rhbz#1493916 - Issues with certificate mapping rules [rhel-7.4.z] + +* Mon Sep 11 2017 Jakub Hrozek - 1.15.2-50.4 +- Resolves: rhbz#1489290 - samba shares with sssd authentication broken + on 7.4 [rhel-7.4.z] + +* Fri Aug 18 2017 Fabiano Fidêncio - 1.15.2-50.3 +- Resolves: rhbz#1482927 - sssd_be is utilizing more CPU during sudoi + rules refresh [rhel-7.4.z] + +* Sun Aug 6 2017 Jakub Hrozek - 1.15.2-50.2 +- Resolves: rhbz#1478252 - Querying the AD domain for external domain's + ID can mark the AD domain offline [rhel-7.4.z] + +* Sun Aug 6 2017 Jakub Hrozek - 1.15.2-50.1 +- Resolves: rhbz#1478250 - Idle nss file descriptors should be closed + [rhel-7.4.z] + +* Wed Jun 21 2017 Jakub Hrozek - 1.15.2-50 +- Resolves: rhbz#1457926 - Wrong search base used when SSSD is directly + connected to AD child domain + +* Wed Jun 21 2017 Jakub Hrozek - 1.15.2-49 +- Resolves: rhbz#1450107 - SSSD doesn't handle conflicts between users + from trusted domains with the same name when + shortname user resolution is enabled + +* Fri Jun 16 2017 Jakub Hrozek - 1.15.2-48 +- Resolves: rhbz#1459846 - krb5: properly handle 'password expired' + information retured by the KDC during + PKINIT/Smartcard authentication + +* Thu Jun 15 2017 Jakub Hrozek - 1.15.2-47 +- Resolves: rhbz#1430415 - ldap_purge_cache_timeout in RHEL7.3 invalidate + most of the entries once the cleanup task kicks in + +* Thu Jun 15 2017 Jakub Hrozek - 1.15.2-46 +- Resolves: rhbz#1455254 - Make domain available as user attribute + +* Thu Jun 8 2017 Jakub Hrozek - 1.15.2-45 +- Resolves: rhbz#1449731 - IPA client cannot change AD Trusted User password + +* Thu Jun 8 2017 Jakub Hrozek - 1.15.2-44 +- Resolves: rhbz#1457927 - getent failed to fetch netgroup information + after changing default_domain_suffix to + ADdomin in /etc/sssd/sssd.conf + +* Mon Jun 5 2017 Jakub Hrozek - 1.15.2-43 +- Resolves: rhbz#1440132 - fiter_users and filter_groups stop working + properly in v 1.15 + +* Mon Jun 5 2017 Jakub Hrozek - 1.15.2-42 +- Resolves: rhbz#1449728 - LDAP to IPA migration doesn't work in master + +* Mon Jun 5 2017 Jakub Hrozek - 1.15.2-41 +- Resolves: rhbz#1445445 - Smart card login fails if same cert mapped to + IdM user and AD user + +* Mon Jun 5 2017 Jakub Hrozek - 1.15.2-40 +- Resolves: rhbz#1449729 - org.freedesktop.sssd.infopipe.GetUserGroups + does not resolve groups into names with AD + +* Thu Jun 1 2017 Jakub Hrozek - 1.15.2-39 +- Resolves: rhbz#1450094 - Properly support IPA's promptusername config + option + +* Thu Jun 1 2017 Jakub Hrozek - 1.15.2-38 +- Resolves: rhbz#1457644 - Segfault in access_provider = krb5 is set in + sssd.conf due to an off-by-one error when + constructing the child send buffer +- Resolves: rhbz#1456531 - Option name typos are not detected with validator + function of sssctl config-check command in domain + sections + +* Fri May 26 2017 Jakub Hrozek - 1.15.2-37 +- Resolves: rhbz#1428906 - sssd intermittently failing to resolve groups + for an AD user in IPA-AD trust environment. + +* Fri May 26 2017 Jakub Hrozek - 1.15.2-36 +- Resolves: rhbz#1389796 - Smartcard authentication with UPN as logon name + might fail +- Fix Coverity issues in patches for rhbz#1445445 + +* Wed May 24 2017 Jakub Hrozek - 1.15.2-35 +- Resolves: rhbz#1445445 - Smart card login fails if same cert mapped to + IdM user and AD user + +* Wed May 24 2017 Jakub Hrozek - 1.15.2-34 +- Resolves: rhbz#1446302 - crash in sssd-kcm due to a race-condition + between two concurrent requests + +* Tue May 23 2017 Jakub Hrozek - 1.15.2-33 +- Resolves: rhbz#1389796 - Smartcard authentication with UPN as logon name might fail + +* Tue May 23 2017 Jakub Hrozek - 1.15.2-32 +- Resolves: rhbz#1306707 - Need better debug message when krb5_child + returns an unhandled error, leading to a + System Error PAM code + +* Mon May 22 2017 Jakub Hrozek - 1.15.2-31 +- Resolves: rhbz#1446535 - Group resolution does not work in subdomain + without ad_server option + +* Wed May 17 2017 Sumit Bose - 1.15.2-30 +- Resolves: rhbz#1449726 - sss_nss_getlistbycert() does not return results from + multiple domains +- Resolves: rhbz#1447098 - sssd unable to search dbus for ipa user by + certificate +- Additional patch for rhbz#1440132 + +* Thu May 11 2017 Jakub Hrozek - 1.15.2-29 +- Reapply patch by Lukas Slebodnik to fix upgrade issues with libwbclient +- Resolves: rhbz#1439457 - SSSD does not start after upgrade from 7.3 to 7.4 +- Resolves: rhbz#1449107 - error: %pre(sssd-common-1.15.2-26.el7.x86_64) + scriptlet failed, exit status 3 + +* Thu May 11 2017 Jakub Hrozek - 1.15.2-28 +- Resolves: rhbz#1440132 - fiter_users and filter_groups stop working + properly in v 1.15 +- Also apply an additional patch for rhbz#1441545 + +* Thu May 4 2017 Jakub Hrozek - 1.15.2-25 +- Resolves: rhbz#1445445 - Smart card login fails if same cert mapped to + IdM user and AD user + +* Wed May 3 2017 Jakub Hrozek - 1.15.2-24 +- Resolves: rhbz#1434992 - Wrong pam return code for user from subdomain + with ad_access_filter + +* Wed May 3 2017 Lukas Slebodnik - 1.15.2-23 +- Resolves: rhbz#1430494 - expect sss_ssh_authorizedkeys and + sss_ssh_knownhostsproxy manuals to be packaged + into sssd-common package + +* Tue May 2 2017 Jakub Hrozek - 1.15.2-22 +- Resolves: rhbz#1427749 - SSSD in server mode iterates over all domains + for group-by-GID requests, causing unnecessary + searches + +* Tue May 2 2017 Jakub Hrozek - 1.15.2-21 +- Resolves: rhbz#1446139 - Infopipe method ListByCertificate does not + return the users with overrides + +* Tue May 2 2017 Jakub Hrozek - 1.15.2-20 +- Resolves: rhbz#1441545 - With multiple subdomain sections id command + output for user is not displayed for both domains + +* Tue May 2 2017 Jakub Hrozek - 1.15.2-19 +- Resolves: rhbz#1428866 - Using ad_enabled_domains configuration option + in sssd.conf causes nameservice lookups to fail. + +* Tue May 2 2017 Jakub Hrozek - 1.15.2-18 +- Remove an unused variable from the sssd-secrets responder +- Related: rhbz#1398701 - [sssd-secrets] https proxy talks plain http +- Improve two DEBUG messages in the client trust code to aid troubleshooting +- Fix standalone application domains +- Related: rhbz#1425891 - Support delivering non-POSIX users and groups + through the IFP and PAM interfaces + +* Wed Apr 26 2017 Jakub Hrozek - 1.15.2-17 +- Allow completely server-side unqualified name resolution if the domain order is set, + do not require any client-side changes +- Related: rhbz#1330196 - [RFE] Short name input format with SSSD for users from + all domains when domain autodiscovery is used or when + IPA client resolves trusted AD domain users + +* Mon Apr 24 2017 Jakub Hrozek - 1.15.2-16 +- Resolves: rhbz#1402532 - D-Bus interface of sssd is giving inappropriate + group information for trusted AD users + +* Thu Apr 13 2017 Jakub Hrozek - 1.15.2-15 +- Resolves: rhbz#1431858 - Wrong principal found with ad provider and long + host name + +* Wed Apr 12 2017 Jakub Hrozek - 1.15.2-14 +- Resolves: rhbz#1415167 - pam_acct_mgmt with pam_sss.so fails in + unprivileged container unless + selinux_provider = none is used + +* Wed Apr 12 2017 Jakub Hrozek - 1.15.2-13 +- Resolves: rhbz#1438388 - [abrt] [faf] sssd: unknown function(): + /usr/libexec/sssd/sssd_pam killed by 6 + +* Tue Apr 11 2017 Jakub Hrozek - 1.15.2-12 +- Resolves: rhbz#1432112 - sssctl config-check does not give any error + when default configuration file is not present + +* Tue Apr 11 2017 Jakub Hrozek - 1.15.2-11 +- Resolves: rhbz#1438374 - [abrt] [faf] sssd: vfprintf(): + /usr/libexec/sssd/sssd_be killed by 11 + +* Tue Apr 11 2017 Jakub Hrozek - 1.15.2-10 +- Resolves: rhbz#1427195 - sssd_nss consumes more memory until restarted + or machine swaps + +* Mon Apr 10 2017 Jakub Hrozek - 1.15.2-9 +- Resolves: rhbz#1414023 - Create troubleshooting tool to determine if a + failure is in SSSD or not when using layered + products like RH-SSO/CFME etc + +* Thu Mar 30 2017 Jakub Hrozek - 1.15.2-8 +- Resolves: rhbz#1398701 - [sssd-secrets] https proxy talks plain http + +* Thu Mar 30 2017 Jakub Hrozek - 1.15.2-7 +- Fix off-by-one error in the KCM responder +- Related: rhbz#1396012 - [RFE] KCM ccache daemon in SSSD + +* Thu Mar 30 2017 Jakub Hrozek - 1.15.2-6 +- Resolves: rhbz#1425891 - Support delivering non-POSIX users and groups + through the IFP and PAM interfaces + +* Wed Mar 29 2017 Jakub Hrozek - 1.15.2-5 +- Resolves: rhbz#1434991 - Issue processing ssh keys from certificates in + ssh respoder + +* Wed Mar 29 2017 Jakub Hrozek - 1.15.2-4 +- Resolves: rhbz#1330196 - [RFE] Short name input format with SSSD for + users from all domains when domain autodiscovery + is used or when IPA client resolves trusted AD + domain users +- Also backport some buildtime fixes for the KCM responder +- Related: rhbz#1396012 - [RFE] KCM ccache daemon in SSSD + +* Mon Mar 27 2017 Jakub Hrozek - 1.15.2-3 +- Resolves: rhbz#1396012 - [RFE] KCM ccache daemon in SSSD + +* Thu Mar 23 2017 Jakub Hrozek - 1.15.2-2 +- Resolves: rhbz#1340711 - [RFE] Use one smartcard and certificate for + authentication to distinct logon accounts + +* Wed Mar 15 2017 Jakub Hrozek - 1.15.2-1 +- Update to upstream 1.15.2 +- https://docs.pagure.org/SSSD.sssd/users/relnotes/notes_1_15_2.html +- Resolves: rhbz#1418728 - IPA - sudo does not handle associated conflict + entries +- Resolves: rhbz#1386748 - sssd doesn't update PTR records if A/PTR zones + are configured as non-secure and secure +- Resolves: rhbz#1214491 - [RFE] Make it possible to configure AD subdomain + in the SSSD server mode + +* Thu Mar 9 2017 Fabiano Fidêncio - 1.15.1-2 +- Drop "NOUPSTREAM: Bundle http-parser" patch + Related: rhbz#1393819 - New package: http-parser + +* Sat Mar 4 2017 Jakub Hrozek - 1.15.1-1 +- Update to upstream 1.15.1 +- https://docs.pagure.org/SSSD.sssd/users/relnotes/notes_1_15_1.html +- Resolves: rhbz#1327085 - Don't prompt for password if there is already + one on the stack +- Resolves: rhbz#1378722 - [RFE] Make GETSIDBYNAME and GETORIGBYNAME + request aware of UPNs and aliases +- Resolves: rhbz#1405075 - [RFE] Add PKINIT support to SSSD Kerberos provider +- Resolves: rhbz#1416526 - Need correction in sssd-krb5 man page +- Resolves: rhbz#1418752 - pam_sss crashes in do_pam_conversation if no + conversation function is provided by the + client app +- Resolves: rhbz#1419356 - Fails to accept any sudo rules if there are + two user entries in an ldap role with the same + sudo user +- Resolves: rhbz#1421622 - SSSD - Users/Groups are cached as mixed-case + resulting in users unable to sign in + +* Wed Feb 1 2017 Jakub Hrozek - 1.15.0-2 +- Fix several packaging issues, notably the p11_child is no longer setuid + and the libwbclient used a wrong version number in the symlink + +* Mon Jan 30 2017 Jakub Hrozek - 1.15.0-1 +- Update to upstream 1.15.0 +- Resolves: rhbz#1393824 - Rebase SSSD to version 1.15 +- Resolves: rhbz#1407960 - wbcLookupSid() fails in pdomain is NULL +- Resolves: rhbz#1406437 - sssctl netgroup-show Cannot allocate memory +- Resolves: rhbz#1400422 - Use-after free in resolver in case the fd is + writeable and readable at the same time +- Resolves: rhbz#1393085 - bz - ldap group names don't resolve after + upgrading sssd to 1.14.0 if ldap_nesting_level is set to 0 +- Resolves: rhbz#1392444 - sssd_be keeps crashing +- Resolves: rhbz#1392441 - sssd fails to start after upgrading to RHEL 7.3 +- Resolves: rhbz#1382602 - autofs map resolution doesn't work offline +- Resolves: rhbz#1380436 - sudo: ignore case on case insensitive domains +- Resolves: rhbz#1378251 - Typo In SSSD-AD Man Page +- Resolves: rhbz#1373427 - Clock skew makes SSSD return System Error +- Resolves: rhbz#1306707 - Need better handling of "Server not found in + Kerberos database" +- Resolves: rhbz#1297462 - Don't include 'enable_only=sssd' in the localauth + plugin config + +* Mon Nov 7 2016 Jakub Hrozek - 1.14.0-46 +- Resolves: rhbz#1382598 - IPA: Uninitialized variable during subdomain check + +* Mon Nov 7 2016 Jakub Hrozek - 1.14.0-45 +- Resolves: rhbz#1378911 - No supplementary groups are resolved for users + in nested OUs when domain stanza differs from AD + domain + +* Mon Nov 7 2016 Jakub Hrozek - 1.14.0-44 +- Resolves: rhbz#1372075 - AD provider: SSSD does not retrieve a domain-local + group with the AD provider when following AGGUDLP + group structure across domains + +* Tue Sep 20 2016 Jakub Hrozek - 1.14.0-43 +- Resolves: rhbz#1376831 - sssd-common is missing dependency on sssd-sudo + +* Fri Sep 16 2016 Jakub Hrozek - 1.14.0-42 +- Resolves: rhbz#1371631 - login using gdm calls for gdm-smartcard when + smartcard authentication is not enabled + +* Wed Sep 14 2016 Jakub Hrozek - 1.14.0-41 +- Resolves: rhbz#1373420 - sss_override fails to export + +* Wed Sep 14 2016 Jakub Hrozek - 1.14.0-40 +- Resolves: rhbz#1375299 - sss_groupshow fails with error "No such + group in local domain. Printing groups only + allowed in local domain" + +* Wed Sep 14 2016 Jakub Hrozek - 1.14.0-39 +- Resolves: rhbz#1375182 - SSSD goes offline when the LDAP server returns + sizelimit exceeded + +* Mon Sep 12 2016 Jakub Hrozek - 1.14.0-38 +- Resolves: rhbz#1372753 - Access denied for user when access_provider = + krb5 is set in sssd.conf + +* Mon Sep 12 2016 Jakub Hrozek - 1.14.0-37 +- Resolves: rhbz#1373444 - unable to create group in sssd cache +- Resolves: rhbz#1373577 - unable to add local user in sssd to a group in sssd + +* Wed Sep 7 2016 Jakub Hrozek - 1.14.0-36 +- Resolves: rhbz#1369118 - Don't enable the default shadowtils domain in RHEL + +* Mon Sep 5 2016 Jakub Hrozek - 1.14.0-35 +- Fix permissions for the private pipe directory +- Resolves: rhbz#1362716 - selinux avc denial for vsftp login as ipa user + +* Fri Sep 2 2016 Jakub Hrozek - 1.14.0-34 +- Resolves: rhbz#1371977 - resolving IPA nested user groups is broken in 1.14 + +* Fri Sep 2 2016 Jakub Hrozek - 1.14.0-33 +- Resolves: rhbz#1368496 - sssd is not able to authenticate with alias + +* Fri Sep 2 2016 Jakub Hrozek - 1.14.0-32 +- Resolves: rhbz#1371152 - SSSD qualifies principal twice in IPA-AD trust + if the principal attribute doesn't exist on the + AD side + +* Fri Aug 26 2016 Jakub Hrozek - 1.14.0-31 +- Apply forgotten patch +- Resolves: rhbz#1368496 - sssd is not able to authenticate with alias +- Resolves: rhbz#1366470 - sssd: throw away the timestamp cache if + re-initializing the persistent cache +- Fix deleting non-existent secret +- Related: rhbz#1311056 - Add a Secrets as a Service component + +* Fri Aug 26 2016 Jakub Hrozek - 1.14.0-30 +- Resolves: rhbz#1362716 - selinux avc denial for vsftp login as ipa user + +* Fri Aug 26 2016 Jakub Hrozek - 1.14.0-29 +- Resolves: rhbz#1368496 - sssd is not able to authenticate with alias + +* Fri Aug 26 2016 Jakub Hrozek - 1.14.0-28 +- Resolves: rhbz#1364033 - sssd exits if clock is adjusted backwards + after boot + +* Fri Aug 19 2016 Jakub Hrozek - 1.14.0-27 +- Resolves: rhbz#1362023 - SSSD fails to start when ldap_user_extra_attrs + contains mail + +* Fri Aug 19 2016 Jakub Hrozek - 1.14.0-26 +- Resolves: rhbz#1368324 - libsss_autofs.so is packaged in two packages + sssd-common and libsss_autofs + +* Fri Aug 19 2016 Jakub Hrozek - 1.14.0-25 +- Fix RPM scriptlet plumbing for the sssd-secrets responder +- Related: rhbz#1311056 - Add a Secrets as a Service component + +* Wed Aug 17 2016 Jakub Hrozek - 1.14.0-24 +- Add socket-activation plumbing for the sssd-secrets responder +- Related: rhbz#1311056 - Add a Secrets as a Service component + +* Wed Aug 17 2016 Jakub Hrozek - 1.14.0-23 +- Own the secrets directory +- Related: rhbz#1311056 - Add a Secrets as a Service component + +* Wed Aug 17 2016 Jakub Hrozek - 1.14.0-22 +- Resolves: rhbz#1268874 - Add an option to disable checking for trusted + domains in the subdomains provider + +* Tue Aug 16 2016 Jakub Hrozek - 1.14.0-21 +- Resolves: rhbz#1271280 - sssd stores and returns incorrect information + about empty netgroup (ldap-server: 389-ds) + +* Tue Aug 16 2016 Jakub Hrozek - 1.14.0-20 +- Resolves: rhbz#1290500 - [feat] command to manually list + fo_add_server_to_list information + +* Tue Aug 16 2016 Jakub Hrozek - 1.14.0-19 +- Add several small fixes related to the config API +- Related: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check) + +* Thu Aug 11 2016 Jakub Hrozek - 1.14.0-18 +- Resolves: rhbz#1349900 - gpo search errors out and gpo_cache file is + never created + +* Wed Aug 10 2016 Jakub Hrozek - 1.14.0-17 +- Fix regressions in the simple access provider +- Resolves: rhbz#1360806 - sssd does not start if sub-domain user is used + with simple access provider +- Apply a number of specfile patches to better match the upstream spefile +- Related: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3 + +* Wed Aug 10 2016 Jakub Hrozek - 1.14.0-16 +- Cherry-pick patches from upstream that fix several regressions +- Avoid checking local users in all cases +- Resolves: rhbz#1353951 - sssd_pam leaks file descriptors + +* Mon Aug 8 2016 Jakub Hrozek - 1.14.0-15 +- Resolves: rhbz#1364118 - [abrt] [faf] sssd: unknown function(): + /usr/libexec/sssd/sssd_nss killed by 11 +- Resolves: rhbz#1361563 - Wrong pam error code returned for password + change in offline mode + +* Fri Jul 29 2016 Jakub Hrozek - 1.14.0-14 +- Resolves: rhbz#1309745 - Support multiple principals for IPA users + +* Fri Jul 29 2016 Jakub Hrozek - 1.14.0-13 +- Resolves: rhbz#1304992 - Handle overriden name of members in the + memberUid attribute + +* Wed Jul 27 2016 Jakub Hrozek - 1.14.0-12 +- handle unresolvable sites more gracefully +- Resolves: rhbz#1346011 - sssd is looking at a server in the GC of a + subdomain, not the root domain. +- fix compilation warnings in unit tests + +* Wed Jul 27 2016 Jakub Hrozek - 1.14.0-11 +- fix capaths output +- Resolves: rhbz#1344940 - GSSAPI error causes failures for child domain + user logins across IPA - AD trust +- also fix Coverity issues in the secrets responder and suppress noisy + debug messages when setting the timestamp cache + +* Tue Jul 19 2016 Jakub Hrozek - 1.14.0-10 +- Resolves: rhbz#1356577 - sssctl: Time stamps without time zone information + +* Tue Jul 19 2016 Jakub Hrozek - 1.14.0-9 +- Resolves: rhbz#1354414 - New or modified ID-View User overrides are not + visible unless rm -f /var/lib/sss/db/*cache* + +* Mon Jul 18 2016 Jakub Hrozek - 1.14.0-8 +- Resolves: rhbz#1211631 - [RFE] Support of UPN for IdM trusted domains + +* Thu Jul 14 2016 Jakub Hrozek - 1.14.0-7 +- Resolves: rhbz#1350520 - [abrt] sssd-common: ipa_dyndns_update_send(): + sssd_be killed by SIGSEGV + +* Wed Jul 13 2016 Jakub Hrozek - 1.14.0-6 +- Resolves: rhbz#1349882 - sssd does not work under non-root user +- Also cherry-pick a few patches from upstream to fix config schema +- Related: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check) + +* Wed Jul 13 2016 Jakub Hrozek - 1.14.0-5 +- Sync a few minor patches from upstream +- Fix sssctl manpage +- Fix nss-tests unit test on big-endian machines +- Fix several issues in the config schema +- Related: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check) + +* Wed Jul 13 2016 Jakub Hrozek - 1.14.0-4 +- Bundle http-parser +- Resolves: rhbz#1311056 - Add a Secrets as a Service component + +* Tue Jul 12 2016 Jakub Hrozek - 1.14.0-3 +- Sync a few minor patches from upstream +- Fix a failover issue +- Resolves: rhbz#1334749 - sssd fails to mark a connection as bad on + searches that time out + +* Mon Jul 11 2016 Jakub Hrozek - 1.14.0-2 +- Explicitly BuildRequire newer ding-libs +- Resolves: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check) + +* Fri Jul 8 2016 Jakub Hrozek - 1.14.0-1 +- New upstream release 1.14.0 +- Resolves: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3 +- Resolves: rhbz#835492 - [RFE] SSSD admin tool request - force reload +- Resolves: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check) +- Resolves: rhbz#1278691 - Please fix rfc2307 autofs schema defaults +- Resolves: rhbz#1287209 - default_domain_suffix Appended to User Name +- Resolves: rhbz#1300663 - Improve sudo protocol to support configurations + with default_domain_suffix +- Resolves: rhbz#1312275 - Support authentication indicators from IPA + +* Thu Jun 30 2016 Jakub Hrozek - 1.14.0beta1-2 +- Resolves: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3 +- Resolves: rhbz#790113 - [RFE] "include" directive in sssd.conf +- Resolves: rhbz#874985 - [RFE] AD provider support for automount lookups +- Resolves: rhbz#879333 - [RFE] SSSD admin tool request - status overview +- Resolves: rhbz#1140022 - [RFE]Allow sssd to add a new option that would + specify which server to update DNS with +- Resolves: rhbz#1290380 - RFE: Improve SSSD performance in large + environments +- Resolves: rhbz#883886 - sssd: incorrect checks on length values during + packet decoding +- Resolves: rhbz#988207 - sssd does not detail which line in configuration + is invalid +- Resolves: rhbz#1007969 - sssd_cache does not remove have an option to + remove the sssd database +- Resolves: rhbz#1103249 - PAC responder needs much time to process large + group lists +- Resolves: rhbz#1118257 - Users in ipa groups, added to netgroups are + not resovable +- Resolves: rhbz#1269018 - Too much logging from sssd_be +- Resolves: rhbz#1293695 - sssd mixup nested group from AD trusted domains +- Resolves: rhbz#1308935 - After removing certificate from user in IPA + and even after sss_cache, FindByCertificate + still finds the user +- Resolves: rhbz#1315766 - SSSD PAM module does not support multiple + password prompts (e.g. Password + Token) with sudo +- Resolves: rhbz#1316164 - SSSD fails to process GPO from Active Directory +- Resolves: rhbz#1322458 - sssd_be[11010]: segfault at 0 ip 00007ff889ff61bb + sp 00007ffc7d66a3b0 error 4 in + libsss_ipa.so[7ff889fcf000+5d000] + +* Mon Jun 20 2016 Jakub Hrozek - 1.14.0alpha-1 +- Resolves: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3 +- The rebase includes fixes for the following bugzillas: +- Resolves: rhbz#789477 - [RFE] SUDO: Support the IPA schema +- Resolves: rhbz#1059972 - RFE: SSSD: Automatically assign new slices for + any AD domain +- Resolves: rhbz#1233200 - man sssd.conf should clarify details about + subdomain_inherit option. +- Resolves: rhbz#1238144 - Need better libhbac debuging added to sssd +- Resolves: rhbz#1265366 - sss_override segfaults when accidentally adding + --help flag to some commands +- Resolves: rhbz#1269512 - sss_override: memory violation +- Resolves: rhbz#1278566 - crash in sssd when non-Englsh locale is used + and pam_strerror prints non-ASCII characters +- Resolves: rhbz#1283686 - groups get deleted from the cache +- Resolves: rhbz#1290378 - Smart Cards: Certificate in the ID View +- Resolves: rhbz#1292238 - extreme memory usage in libnfsidmap sss.so + plug-in when resolving groups with many members +- Resolves: rhbz#1292456 - sssd_be AD segfaults on missing A record +- Resolves: rhbz#1294670 - Local users with local sudo rules causes + LDAP queries +- Resolves: rhbz#1296618 - Properly remove OriginalMemberOf attribute in + SSSD cache if user has no secondary groups anymore +- Resolves: rhbz#1299553 - Cannot retrieve users after upgrade from 1.12 + to 1.13 +- Resolves: rhbz#1302821 - Cannot start sssd after switching to non-root +- Resolves: rhbz#1310877 - [RFE] Support Automatic Renewing of Kerberos + Host Keytabs +- Resolves: rhbz#1313014 - sssd is not closing sockets properly +- Resolves: rhbz#1318996 - SSSD does not fail over to next GC +- Resolves: rhbz#1327270 - local overrides: issues with sub-domain users + and mixed case names +- Resolves: rhbz#1342547 - sssd-libwbclient: wbcSidsToUnixIds should not + fail on lookup errors + +* Tue May 24 2016 Jakub Hrozek - 1.13.0-50 +- Build the PAC plugin with krb5-1.14 +- Related: rhbz#1336688 - sssd tries to resolve global catalog servers + from AD forest sub-domains in AD-IPA trust setup + +* Tue May 24 2016 Jakub Hrozek - 1.13.0-49 +- Resolves: rhbz#1336688 - sssd tries to resolve global catalog servers + from AD forest sub-domains in AD-IPA trust setup + +* Tue May 24 2016 Jakub Hrozek - 1.13.0-48 +- Resolves: rhbz#1290853 - [sssd] Trusted (AD) user's info stays in sssd + cache for much more than expected. + +* Mon May 23 2016 Jakub Hrozek - 1.13.0-47 +- Resolves: rhbz#1336706 - sssd_nss memory usage keeps growing when trying + to retrieve non-existing netgroups + +* Tue May 17 2016 Jakub Hrozek - 1.13.0-46 +- Resolves: rhbz#1296902 - In IPA-AD trust environment access is granted + to AD user even if the user is disabled on AD. + +* Tue May 17 2016 Jakub Hrozek - 1.13.0-45 +- Resolves: rhbz#1334159 - IPA provider crashes if a netgroup from a + trusted domain is requested + +* Mon Apr 18 2016 Jakub Hrozek - 1.13.0-44 +- Resolves: rhbz#1308913 - sssd be memory leak in sssd's memberof plugin +- More patches from upstream related to the memory leak + +* Fri Apr 1 2016 Jakub Hrozek - 1.13.0-43 +- Resolves: rhbz#1308913 - sssd be memory leak in sssd's memberof plugin + +* Wed Feb 24 2016 Jakub Hrozek - 1.13.0-42 +- Resolves: rhbz#1300740 - [RFE] IPA: resolve external group memberships + of IPA groups during getgrnam and getgrgid + +* Tue Nov 24 2015 Jakub Hrozek - 1.13.0-41 +- Resolves: rhbz#1284814 - sssd: [sysdb_add_user] (0x0400): Error: 17 + +* Wed Oct 14 2015 Jakub Hrozek - 1.13.0-40 +- Resolves: rhbz#1270827 - local overrides: don't contact server with + overridden name/id + +* Wed Oct 7 2015 Jakub Hrozek - 1.13.0-39 +- Resolves: rhbz#1267837 - sssd_be crashed in ipa_srv_ad_acct_lookup_step + +* Wed Oct 7 2015 Jakub Hrozek - 1.13.0-38 +- Resolves: rhbz#1267176 - Memory leak / possible DoS with krb auth. + +* Wed Oct 7 2015 Jakub Hrozek - 1.13.0-37 +- Resolves: rhbz#1267836 - PAM responder crashed if user was not set + +* Wed Sep 30 2015 Jakub Hrozek - 1.13.0-36 +- Resolves: rhbz#1266107 - AD: Conditional jump or move depends on + uninitialised value + +* Wed Sep 23 2015 Jakub Hrozek - 1.13.0-35 +- Resolves: rhbz#1250135 - Detect re-established trusts in the IPA + subdomain code + +* Tue Sep 22 2015 Jakub Hrozek - 1.13.0-34 +- Fix a Coverity warning in dyndns code +- Resolves: rhbz#1261155 - nsupdate exits on first GSSAPI error instead + of processing other commands +* Tue Sep 22 2015 Jakub Hrozek - 1.13.0-33 +- Resolves: rhbz#1261155 - nsupdate exits on first GSSAPI error instead + of processing other commands + +* Tue Sep 22 2015 Jakub Hrozek - 1.13.0-32 +- Resolves: rhbz#1263735 - Could not resolve AD user from root domain + +* Tue Sep 22 2015 Jakub Hrozek - 1.13.0-31 +- Remove -d from sss_override manpage +- Related: rhbz#1259512 - sss_override : The local override user is not found + +* Tue Sep 22 2015 Jakub Hrozek - 1.13.0-30 +- Patches required for better handling of failover with one-way trusts +- Related: rhbz#1250135 - Detect re-established trusts in the IPA subdomain + code + +* Fri Sep 18 2015 Jakub Hrozek - 1.13.0-29 +- Resolves: rhbz#1263587 - sss_override --name doesn't work with RFC2307 + and ghost users + +* Fri Sep 18 2015 Jakub Hrozek - 1.13.0-28 +- Resolves: rhbz#1259512 - sss_override : The local override user is not found + +* Fri Sep 18 2015 Jakub Hrozek - 1.13.0-27 +- Resolves: rhbz#1260027 - sssd_be memory leak with sssd-ad in GPO code + +* Tue Sep 1 2015 Jakub Hrozek - 1.13.0-26 +- Resolves: rhbz#1256398 - sssd cannot resolve user names containing + backslash with ldap provider + +* Tue Aug 25 2015 Martin Kosek - 1.13.0-25 +- Resolves: rhbz#1254189 - sss_override contains an extra parameter --debug + but is not listed in the man page or in + the arguments help + +* Thu Aug 20 2015 Jakub Hrozek - 1.13.0-24 +- Resolves: rhbz#1254518 - Fix crash in nss responder + +* Thu Aug 20 2015 Jakub Hrozek - 1.13.0-23 +- Support import/export for local overrides +- Support FQDNs for local overrides +- Resolves: rhbz#1254184 - sss_override does not work correctly when + 'use_fully_qualified_names = True' + +* Tue Aug 18 2015 Jakub Hrozek - 1.13.0-22 +- Resolves: rhbz#1244950 - Add index for 'objectSIDString' and maybe to + other cache attributes + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-21 +- Resolves: rhbz#1250415 - sssd: p11_child hardening + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-20 +- Related: rhbz#1250135 - Detect re-established trusts in the IPA + subdomain code + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-19 +- Resolves: rhbz#1202724 - [RFE] Add a way to lookup users based on CAC + identity certificates + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-18 +- Resolves: rhbz#1232950 - [IPA/IdM] sudoOrder not honored as expected + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-17 +- Fix wildcard_limit=0 +- Resolves: rhbz#1206571 - [RFE] Expose D-BUS interface + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-16 +- Fix race condition in invalidating the memory cache +- Related: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups + +* Mon Aug 17 2015 Jakub Hrozek - 1.13.0-15 +- Resolves: rhbz#1249015 - KDC proxy not working with SSSD krb5_use_kdcinfo + enabled + +* Thu Aug 6 2015 Jakub Hrozek - 1.13.0-14 +- Bump release number +- Related: rhbz#1246489 - sss_obfuscate fails with "ImportError: No module + named pysss" + +* Thu Aug 6 2015 Lukas Slebodnik - 1.13.0-13 +- Fix missing dependency of sssd-tools +- Resolves: rhbz#1246489 - sss_obfuscate fails with "ImportError: No module + named pysss" + +* Wed Aug 5 2015 Jakub Hrozek - 1.13.0-12 +- More memory cache related fixes +- Related: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups + +* Tue Aug 4 2015 Jakub Hrozek - 1.13.0-11 +- Remove binary blob from SC patches as patch(1) can't handle those +- Related: rhbz#854396 - [RFE] Support for smart cards + +* Tue Aug 4 2015 Jakub Hrozek - 1.13.0-10 +- Resolves: rhbz#1244949 - getgrgid for user's UID on a trust client + prevents getpw* + +* Tue Aug 4 2015 Jakub Hrozek - 1.13.0-9 +- Fix memory cache integration tests +- Resolves: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups +- Resolves: rhbz#854396 - [RFE] Support for smart cards + +* Tue Jul 28 2015 Jakub Hrozek - 1.13.0-8 +- Remove OTP from PAM stack correctly +- Related: rhbz#1200873 - [RFE] Allow smart multi step prompting when + user logs in with password and token code from IPA +- Handle sssd-owned keytabs when sssd runs as root +- Related: rhbz#1205144 - RFE: Support one-way trusts for IPA + +* Mon Jul 27 2015 Jakub Hrozek - 1.13.0-7 +- Resolves: rhbz#1183747 - [FEAT] UID and GID mapping on individual clients + +* Fri Jul 24 2015 Jakub Hrozek - 1.13.0-6 +- Resolves: rhbz#1206565 - [RFE] Add dualstack and multihomed support +- Resolves: rhbz#1187146 - If v4 address exists, will not create nonexistant + v6 in ipa domain + +* Fri Jul 17 2015 Jakub Hrozek - 1.13.0-5 +- Resolves: rhbz#1242942 - well-known SID check is broken for NetBIOS prefixes + +* Fri Jul 17 2015 Jakub Hrozek - 1.13.0-4 +- Resolves: rhbz#1234722 - sssd ad provider fails to start in rhel7.2 + +* Thu Jul 16 2015 Jakub Hrozek - 1.13.0-3 +- Add support for InfoPipe wildcard requests +- Resolves: rhbz#1206571 - [RFE] Expose D-BUS interface + +* Mon Jul 6 2015 Jakub Hrozek - 1.13.0-2 +- Also package the initgr memcache +- Related: rhbz#1205554 - Rebase SSSD to 1.13.x + +* Mon Jul 6 2015 Jakub Hrozek - 1.13.0-1 +- Rebase to 1.13.0 upstream +- Related: rhbz#1205554 - Rebase SSSD to 1.13.x +- Resolves: rhbz#910187 - [RFE] authenticate against cache in SSSD +- Resolves: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups + +* Wed Jul 1 2015 Jakub Hrozek - 1.13.0.3alpha +- Don't default to SSSD user +- Related: rhbz#1205554 - Rebase SSSD to 1.13.x + +* Tue Jun 23 2015 Jakub Hrozek - 1.13.0.2alpha +- Related: rhbz#1205554 - Rebase SSSD to 1.13.x +- GPO default should be permissve + +* Mon Jun 22 2015 Jakub Hrozek - 1.13.0.1alpha +- Resolves: rhbz#1205554 - Rebase SSSD to 1.13.x +- Relax the libldb requirement +- Resolves: rhbz#1221992 - sssd_be segfault at 0 ip sp error 6 in + libtevent.so.0.9.21 +- Resolves: rhbz#1221839 - SSSD group enumeration inconsistent due to + binary SIDs +- Resolves: rhbz#1219285 - Unable to resolve group memberships for AD + users when using sssd-1.12.2-58.el7_1.6.x86_64 + client in combination with + ipa-server-3.0.0-42.el6.x86_64 with AD Trust +- Resolves: rhbz#1217559 - [RFE] Support GPOs from different domain controllers +- Resolves: rhbz#1217350 - ignore_group_members doesn't work for subdomains +- Resolves: rhbz#1217127 - Override for IPA users with login does not list + user all groups +- Resolves: rhbz#1216285 - autofs provider fails when default_domain_suffix + and use_fully_qualified_names set +- Resolves: rhbz#1214719 - Group resolution is inconsistent with group + overrides +- Resolves: rhbz#1214718 - Overridde with --login fails trusted adusers + group membership resolution +- Resolves: rhbz#1214716 - idoverridegroup for ipa group with --group-name + does not work +- Resolves: rhbz#1214337 - Overrides with --login work in second attempt +- Resolves: rhbz#1212489 - Disable the cleanup task by default +- Resolves: rhbz#1211830 - external users do not resolve with + "default_domain_suffix" set in IPA server sssd.conf +- Resolves: rhbz#1210854 - Only set the selinux context if the context + differs from the local one +- Resolves: rhbz#1209483 - When using id_provider=proxy with + auth_provider=ldap, it does not work as expected +- Resolves: rhbz#1209374 - Man sssd-ad(5) lists Group Policy Management + Editor naming for some policies but not for all +- Resolves: rhbz#1208507 - sysdb sudo search doesn't escape special characters +- Resolves: rhbz#1206571 - [RFE] Expose D-BUS interface +- Resolves: rhbz#1206566 - SSSD does not update Dynamic DNS records if + the IPA domain differs from machine hostname's + domain +- Resolves: rhbz#1206189 - [bug] sssd always appends default_domain_suffix + when checking for host keys +- Resolves: rhbz#1204203 - sssd crashes intermittently +- Resolves: rhbz#1203945 - [FJ7.0 Bug]: getgrent returns error because + sss is written in nsswitch.conf as default +- Resolves: rhbz#1203642 - GPO access control looks for computer object + in user's domain only +- Resolves: rhbz#1202245 - SSSD's HBAC processing is not permissive enough + with broken replication entries +- Resolves: rhbz#1201271 - sssd_nss segfaults if initgroups request is by + UPN and doesn't find anything +- Resolves: rhbz#1200873 - [RFE] Allow smart multi step prompting when + user logs in with password and token code from IPA +- Resolves: rhbz#1199541 - Read and use the TTL value when resolving a + SRV query +- Resolves: rhbz#1199533 - [RFE] Implement background refresh for users, + groups or other cache objects +- Resolves: rhbz#1199445 - Does sssd-ad use the most suitable attribute + for group name? +- Resolves: rhbz#1198477 - ccname_file_dummy is not unlinked on error +- Resolves: rhbz#1187103 - [RFE] User's home directories are not taken + from AD when there is an IPA trust with AD +- Resolves: rhbz#1185536 - In ipa-ad trust, with 'default_domain_suffix' set + to AD domain, IPA user are not able to log unless + use_fully_qualified_names is set +- Resolves: rhbz#1175760 - [RFE] Have OpenLDAP lock out ssh keys when + account naturally expires +- Resolves: rhbz#1163806 - [RFE]ad provider dns_discovery_domain option: + kerberos discovery is not using this option +- Resolves: rhbz#1205160 - Complain loudly if backend doesn't start due + to missing or invalid keytab + +* Wed Apr 22 2015 Jakub Hrozek - 1.12.2-61 +- Resolves: rhbz#1226119 - Properly handle AD's binary objectGUID + +* Wed Apr 22 2015 Jakub Hrozek - 1.12.2-60 +- Filter out domain-local groups during AD initgroups operation +- Related: rhbz#1201840 - SSSD downloads too much information when fetching + information about groups + +* Wed Apr 22 2015 Jakub Hrozek - 1.12.2-59 +- Resolves: rhbz#1201840 - SSSD downloads too much information when fetching + information about groups + +* Thu Mar 19 2015 Jakub Hrozek - 1.12.2-58.6 +- Initialize variable in the views code in one success and one failure path +- Resolves: rhbz#1202170 - sssd_be segfault on IPA(when auth with AD + trusted domain) client at + src/providers/ipa/ipa_s2n_exop.c:1605 + +* Tue Mar 17 2015 Jakub Hrozek - 1.12.2-58.5 +- Resolves: rhbz#1202170 - sssd_be segfault on IPA(when auth with AD + trusted domain) client at + src/providers/ipa/ipa_s2n_exop.c:1605 + +* Tue Mar 17 2015 Jakub Hrozek - 1.12.2-58.4 +- Handle case where there is no default and no rules +- Resolves: rhbz#1192314 - With empty ipaselinuxusermapdefault security + context on client is staff_u + +* Thu Mar 5 2015 Jakub Hrozek - 1.12.2-58.3 +- Set a pointer in ldap_child to NULL to avoid warnings +- Related: rhbz#1198759 - ccname_file_dummy is not unlinked on error + +* Thu Mar 5 2015 Jakub Hrozek - 1.12.2-58.2 +- Resolves: rhbz#1199143 - With empty ipaselinuxusermapdefault security + context on client is staff_u + +* Thu Mar 5 2015 Jakub Hrozek - 1.12.2-58.1 +- Resolves: rhbz#1198759 - ccname_file_dummy is not unlinked on error + +* Tue Feb 3 2015 Jakub Hrozek - 1.12.2-57 +- Run the restart in sssd-common posttrans +- Explicitly require libwbclient +- Resolves: rhbz#1187113 - sssd deamon was not running after RHEL 7.1 upgrade + +* Fri Jan 30 2015 Jakub Hrozek - 1.12.2-56 +- Resolves: rhbz#1187113 - sssd deamon was not running after RHEL 7.1 upgrade + +* Fri Jan 30 2015 Jakub Hrozek - 1.12.2-55 +- Fix endianess bug in fill_id() +- Related: rhbz#1109331 - [RFE] Allow SSSD to be used with smbd shares + +* Fri Jan 30 2015 Jakub Hrozek - 1.12.2-54 +- Resolves: rhbz#1168904 - gid is overridden by uid in default trust view + +* Fri Jan 30 2015 Jakub Hrozek - 1.12.2-53 +- Resolves: rhbz#1187192 - IPA initgroups don't work correctly in + non-default view + +* Tue Jan 27 2015 Jakub Hrozek - 1.12.2-52 +- Resolves: rhbz#1184982 - Need to set different umask in selinux_child + +* Tue Jan 27 2015 Jakub Hrozek - 1.12.2-51 +- Bump the release number +- Related: rhbz#1184140 - Users saved throug extop don't have the + originalMemberOf attribute + +* Tue Jan 27 2015 Jakub Hrozek - 1.12.2-50 +- Add a patch dependency +- Related: rhbz#1184140 - Users saved throug extop don't have the + originalMemberOf attribute + +* Tue Jan 27 2015 Jakub Hrozek - 1.12.2-49 +- Process ghost members only once +- Fix processing of universal groups with members from different domains +- Related: rhbz#1168904 - gid is overridden by uid in default trust view + +* Tue Jan 27 2015 Jakub Hrozek - 1.12.2-48 +- Related: rhbz#1184140 - Users saved throug extop don't have the + originalMemberOf attribute + +* Fri Jan 23 2015 Jakub Hrozek - 1.12.2-47 +- Resolves: rhbz#1185188 - Uncached SIDs cannot be resolved + +* Fri Jan 23 2015 Jakub Hrozek - 1.12.2-46 +- Handle GID override in MPG domains +- Handle views with mixed-case domains +- Related: rhbz#1168904 - gid is overridden by uid in default trust view + +* Wed Jan 21 2015 Jakub Hrozek - 1.12.2-45 +- Open socket to the PAC responder in krb5_child before dropping root +- Related: rhbz#1184140 - Users saved throug extop don't have the + originalMemberOf attribute + +* Tue Jan 20 2015 Jakub Hrozek - 1.12.2-44 +- Resolves: rhbz#1184140 - Users saved throug extop don't have the + originalMemberOf attribute + +* Mon Jan 19 2015 Jakub Hrozek - 1.12.2-43 +- Resolves: rhbz#1182183 - pam_sss(sshd:auth): authentication failure with + user from AD + +* Wed Jan 14 2015 Jakub Hrozek - 1.12.2-42 +- Resolves: rhbz#889206 - On clock skew sssd returns system error + +* Wed Jan 14 2015 Jakub Hrozek - 1.12.2-41 +- Related: rhbz#1168904 - gid is overridden by uid in default trust view + +* Tue Jan 13 2015 Jakub Hrozek - 1.12.2-40 +- Resolves: rhbz#1177140 - gpo_child fails if "log level" is enabled in smb.conf +- Related: rhbz#1168904 - gid is overridden by uid in default trust view + +* Fri Dec 19 2014 Sumit Bose - 1.12.2-39 +- Resolves: rhbz#1175408 - SSSD should not fail authentication when only allow + rules are used +- Resolves: rhbz#1175705 - sssd-libwbclient conflicts with Samba's and causes + crash in wbinfo + - in addition to the patch libwbclient.so is + filtered out of the Provides list of the package + +* Wed Dec 17 2014 Sumit Bose - 1.12.2-38 +- Resolves: rhbz#1171215 - Crash in function get_object_from_cache +- Resolves: rhbz#1171383 - getent fails for posix group with AD users after + login +- Resolves: rhbz#1171382 - getent of AD universal group fails after group users + login +- Resolves: rhbz#1170300 - Access is not rejected for disabled domain +- Resolves: rhbz#1162486 - Error processing external groups with + getgrnam/getgrgid in the server mode +- Resolves: rhbz#1168904 - gid is overridden by uid in default trust view + +* Wed Dec 17 2014 Sumit Bose - 1.12.2-37 +- Resolves: rhbz#1169459 - sssd-ad: The man page description to enable GPO HBAC + Policies are unclear +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Mon Dec 15 2014 Jakub Hrozek - 1.12.2-35 +- Rebuild to add several forgotten Patch entries +- Resolves: rhbz#1173482 - MAN: Document that only user names are checked + for pam_trusted_users +- Resolves: rhbz#1167324 - pam_sss domains option: User auth should fail + when domains= + +* Sun Dec 14 2014 Jakub Hrozek - 1.12.2-35 +- Remove Coverity warnings in krb5_child code +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Sat Dec 13 2014 Jakub Hrozek - 1.12.2-34 +- Resolves: rhbz#1173482 - MAN: Document that only user names are checked + for pam_trusted_users +- Resolves: rhbz#1167324 - pam_sss domains option: User auth should fail + when domains= + +* Sat Dec 13 2014 Jakub Hrozek - 1.12.2-33 +- Don't error out on chpass with OTPs +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Mon Dec 8 2014 Jakub Hrozek - 1.12.2-32 +- Resolves: rhbz#1124320 - [FJ7.0 Bug]: getgrent returns error because sss + is written in nsswitch.conf as default. + +* Mon Dec 8 2014 Jakub Hrozek - 1.12.2-31 +- Resolves: rhbz#1169739 - selinuxusermap rule does not apply to trusted + AD users +- Enable running unit tests without cmocka +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Wed Dec 3 2014 Jakub Hrozek - 1.12.2-30 +- krb5_child and ldap_child do not call Kerberos calls as root +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Wed Dec 3 2014 Jakub Hrozek - 1.12.2-29 +- Resolves: rhbz#1168735 - The Kerberos provider is not properly views-aware + +* Wed Nov 26 2014 Jakub Hrozek - 1.12.2-28 +- Fix typo in libwbclient-devel alternatives invocation +- Related: rhbz#1109331 - [RFE] Allow SSSD to be used with smbd shares + +* Wed Nov 26 2014 Jakub Hrozek - 1.12.2-27 +- Resolves: rhbz#1166727 - pam_sss domains option: Untrusted users from + the same domain are allowed to auth. + +* Tue Nov 25 2014 Jakub Hrozek - 1.12.2-26 +- Handle migrating clients between views +- Related: rhbz#891984 - [RFE] ID Views: Support migration from the sync + solution to the trust solution + +* Tue Nov 25 2014 Jakub Hrozek - 1.12.2-25 +- Use alternatives for libwbclient +- Related: rhbz#1109331 - [RFE] Allow SSSD to be used with smbd shares + +* Tue Nov 25 2014 Jakub Hrozek - 1.12.2-24 +- Resolves: rhbz#1165794 - sssd does not work with custom value of option + re_expression + +* Tue Nov 25 2014 Jakub Hrozek - 1.12.2-23 +- Add an option that describes where to put generated krb5 files to +- Related: rhbz#1135043 - [RFE] Implement localauth plugin for MIT krb5 1.12 + +* Tue Nov 25 2014 Jakub Hrozek - 1.12.2-22 +- Handle IPA group names returned from the extop plugin +- Related: rhbz#891984 - [RFE] ID Views: Support migration from the sync + solution to the trust solution + +* Tue Nov 25 2014 Jakub Hrozek - 1.12.2-21 +- Resolves: rhbz#1165792 - automount segfaults in sss_nss_check_header + +* Thu Nov 20 2014 Jakub Hrozek - 1.12.2-20 +- Resolves: rhbz#1163742 - "debug_timestamps = false" and "debug_microseconds + = true" do not work after enabling journald + with sssd. + +* Thu Nov 20 2014 Jakub Hrozek - 1.12.2-19 +- Resolves: rhbz#1153593 - Manpage description of case_sensitive=preserving + is incomplete + +* Thu Nov 20 2014 Jakub Hrozek - 1.12.2-18 +- Support views for IPA users +- Related: rhbz#891984 - [RFE] ID Views: Support migration from the sync + solution to the trust solution + +* Thu Nov 20 2014 Jakub Hrozek - 1.12.2-17 +- Update man page to clarify TGs should be disabled with a custom search base +- Related: rhbz#1161741 - TokenGroups for LDAP provider breaks in corner cases + +* Wed Nov 19 2014 Jakub Hrozek - 1.12.2-16 +- Use upstreamed patches for the rootless sssd +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Wed Nov 19 2014 Jakub Hrozek - 1.12.2-15 +- Resolves: rhbz#1153603 - Proxy Provider: Fails to lookup case sensitive + users and groups with case_sensitive=preserving + +* Wed Nov 19 2014 Jakub Hrozek - 1.12.2-14 +- Resolves: rhbz#1161741 - TokenGroups for LDAP provider breaks in corner cases + +* Wed Nov 19 2014 Jakub Hrozek - 1.12.2-13 +- Resolves: rhbz#1162480 - dereferencing failure against openldap server + +* Wed Nov 12 2014 Jakub Hrozek - 1.12.2-12 +- Move adding the user from pretrans to pre, copy adding the user to + sssd-krb5-common and sssd-ipa as well in order to work around yum + ordering issue +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Tue Nov 11 2014 Jakub Hrozek - 1.12.2-11 +- Resolves: rhbz#1113783 - sssd should run under unprivileged user + +* Fri Nov 7 2014 Jakub Hrozek - 1.12.2-10 +- Fix two regressions in the new selinux_child process +- Related: rhbz#1113783 - sssd should run under unprivileged user +- Resolves: rhbz#1132365 - Remove password from the PAM stack if OTP is used + +* Wed Nov 5 2014 Jakub Hrozek - 1.12.2-9 +- Include the ldap_child and selinux_child patches for rootless sssd +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Wed Nov 5 2014 Jakub Hrozek - 1.12.2-8 +- Support overriding SSH public keys with views +- Support extended attributes via the extop plugin +- Related: rhbz#1109756 - Rebase SSSD to 1.12 +- Resolves: rhbz#1137010 - disable midpoint refresh for netgroups if ptask + refresh is enabled + +* Thu Oct 30 2014 Jakub Hrozek - 1.12.2-7 +- Resolves: rhbz#1153518 - service lookups returned in lowercase with + case_sensitive=preserving +- Resolves: rhbz#1158809 - Enumeration shows only a single group multiple + times + +* Wed Oct 22 2014 Jakub Hrozek - 1.12.2-6 +- Include the responder and packaging patches for rootless sssd +- Related: rhbz#1113783 - sssd should run under unprivileged user + +* Wed Oct 22 2014 Jakub Hrozek - 1.12.2-5 +- Amend the sssd-ldap man page with info about lockout setup +- Related: rhbz#1109756 - Rebase SSSD to 1.12 +- Resolves: rhbz#1137014 - Shell fallback mechanism in SSSD +- Resolves: rhbz#790854 - 4 functions with reference leaks within sssd (src/python/pyhbac.c) + +* Wed Oct 22 2014 Jakub Hrozek - 1.12.2-4 +- Fix regressions caused by views patches when SSSD is connected to a + pre-4.0 IPA server +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Wed Oct 22 2014 Jakub Hrozek - 1.12.2-3 +- Add the low-level server changes for running as unprivileged user +- Package the libsss_semange library needed for SELinux label changes +- Related: rhbz#1113783 - sssd should run under unprivileged user +- Resolves: rhbz#1113784 - sssd should audit selinux user map changes + +* Wed Oct 22 2014 Jakub Hrozek - 1.12.2-2 +- Use libsemanage for SELinux label changes +- Resolves: rhbz#1113784 - sssd should audit selinux user map changes + +* Mon Oct 20 2014 Jakub Hrozek - 1.12.2-1 +- Rebase SSSD to 1.12.2 +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Thu Oct 09 2014 Jakub Hrozek - 1.12.1-2 +- Sync with upstream +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Thu Sep 11 2014 Jakub Hrozek - 1.12.1-1 +- Rebuild against ding-libs with fixed SONAME +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Tue Sep 9 2014 Jakub Hrozek - 1.12.1-1 +- Rebase SSSD to 1.12.1 +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Fri Sep 05 2014 Jakub Hrozek - 1.12.0-3 +- Require ldb 2.1.17 +- Related: rhbz#1133914 - Rebase libldb to version 1.1.17 or newer + +* Fri Aug 08 2014 Jakub Hrozek - 1.12.0-2 +- Fix fully qualified IFP lookups +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Thu Jul 24 2014 Jakub Hrozek - 1.12.0-1 +- Rebase SSSD to 1.12.0 +- Related: rhbz#1109756 - Rebase SSSD to 1.12 + +* Wed May 21 2014 Jakub Hrozek - 1.11.2-70 +- Squash in upstream review comments about the PAC patch +- Related: rhbz#1097286 - Expanding home directory fails when the request + comes from the PAC responder + +* Tue May 13 2014 Jakub Hrozek - 1.11.2-69 +- Backport a patch to allow krb5-utils-test to run as root +- Related: rhbz#1097286 - Expanding home directory fails when the request + comes from the PAC responder + +* Tue May 13 2014 Jakub Hrozek - 1.11.2-68 +- Resolves: rhbz#1097286 - Expanding home directory fails when the request + comes from the PAC responder + +* Tue May 13 2014 Jakub Hrozek - 1.11.2-67 +- Fix a DEBUG message, backport two related fixes +- Related: rhbz#1090653 - segfault in sssd_be when second domain tree + users are queried while joined to child domain + +* Tue May 13 2014 Jakub Hrozek - 1.11.2-66 +- Resolves: rhbz#1090653 - segfault in sssd_be when second domain tree + users are queried while joined to child domain + +* Wed Apr 02 2014 Jakub Hrozek - 1.11.2-65 +- Resolves: rhbz#1082191 - RHEL7 IPA selinuxusermap hbac rule not always + matching + +* Wed Apr 02 2014 Jakub Hrozek - 1.11.2-64 +- Resolves: rhbz#1077328 - other subdomains are unavailable when joined + to a subdomain in the ad forest + +* Wed Mar 26 2014 Sumit Bose - 1.11.2-63 +- Resolves: rhbz#1078877 - Valgrind: Invalid read of int while processing + netgroup + +* Wed Mar 26 2014 Sumit Bose - 1.11.2-62 +- Resolves: rhbz#1075092 - Password change w/ OTP generates error on success + +* Fri Mar 21 2014 Jakub Hrozek - 1.11.2-61 +- Resolves: rhbz#1078840 - Error during password change + +* Thu Mar 13 2014 Jakub Hrozek - 1.11.2-60 +- Resolves: rhbz#1075663 - SSSD should create the SELinux mapping file + with format expected by pam_selinux + +* Wed Mar 12 2014 Jakub Hrozek - 1.11.2-59 +- Related: rhbz#1075621 - Add another Kerberos error code to trigger IPA + password migration + +* Tue Mar 11 2014 Jakub Hrozek - 1.11.2-58 +- Related: rhbz#1073635 - IPA SELinux code looks for the host in the wrong + sysdb subdir when a trusted user logs in + +* Tue Mar 11 2014 Jakub Hrozek - 1.11.2-57 +- Related: rhbz#1066096 - not retrieving homedirs of AD users with + posix attributes + +* Mon Mar 10 2014 Jakub Hrozek - 1.11.2-56 +- Related: rhbz#1072995 - AD group inconsistency when using AD provider + in sssd-1.11-40 + +* Mon Mar 10 2014 Jakub Hrozek - 1.11.2-55 +- Resolves: rhbz#1073631 - sssd fails to handle expired passwords + when OTP is used + +* Tue Mar 04 2014 Jakub Hrozek - 1.11.2-54 +- Resolves: rhbz#1072067 - SSSD Does not cache SELinux map from FreeIPA + correctly + +* Tue Mar 04 2014 Jakub Hrozek - 1.11.2-53 +- Resolves: rhbz#1071903 - ipa-server-mode: Use lower-case user name + component in home dir path + +* Tue Mar 04 2014 Jakub Hrozek - 1.11.2-52 +- Resolves: rhbz#1068725 - Evaluate usage of sudo LDAP provider together + with the AD provider + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-51 +- Fix idmap documentation +- Bump idmap version info +- Related: rhbz#1067361 - Check IPA idranges before saving them to the cache + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-50 +- Pull some follow up man page fixes from upstream +- Related: rhbz#1060389 - Document that `sssd` cache needs to be cleared + manually, if ID mapping configuration changes +- Related: rhbz#1064908 - MAN: Remove misleading memberof example from + ldap_access_filter example + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-49 +- Resolves: rhbz#1060389 - Document that `sssd` cache needs to be cleared + manually, if ID mapping configuration changes + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-48 +- Resolves: rhbz#1064908 - MAN: Remove misleading memberof example from + ldap_access_filter example + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-47 +- Resolves: rhbz#1068723 - Setting int option to 0 yields the default value + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-46 +- Resolves: rhbz#1067361 - Check IPA idranges before saving them to the cache + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-45 +- Resolves: rhbz#1067476 - SSSD pam module accepts usernames with leading + spaces + +* Wed Feb 26 2014 Jakub Hrozek - 1.11.2-44 +- Resolves: rhbz#1033069 - Configuring two different provider types might + start two parallel enumeration tasks + +* Mon Feb 17 2014 Jakub Hrozek - 1.11.2-43 +- Resolves: rhbz#1068640 - 'IPA: Don't call tevent_req_post outside _send' + should be added to RHEL7 + +* Mon Feb 17 2014 Jakub Hrozek - 1.11.2-42 +- Resolves: rhbz#1063977 - SSSD needs to enable FAST by default + +* Mon Feb 17 2014 Jakub Hrozek - 1.11.2-41 +- Resolves: rhbz#1064582 - sss_cache does not reset the SYSDB_INITGR_EXPIRE + attribute when expiring users + +* Wed Feb 12 2014 Jakub Hrozek - 1.11.2-40 +- Resolves: rhbz#1033081 - Implement heuristics to detect if POSIX attributes + have been replicated to the Global Catalog or not + +* Wed Feb 12 2014 Jakub Hrozek - 1.11.2-39 +- Resolves: rhbz#872177 - [RFE] subdomain homedir template should be + configurable/use flatname by default + +* Wed Feb 12 2014 Jakub Hrozek - 1.11.2-38 +- Resolves: rhbz#1059753 - Warn with a user-friendly error message when + permissions on sssd.conf are incorrect + +* Wed Jan 29 2014 Jakub Hrozek - 1.11.2-37 +- Resolves: rhbz#1037653 - Enabling ldap_id_mapping doesn't exclude + uidNumber in filter + +* Wed Jan 29 2014 Jakub Hrozek - 1.11.2-36 +- Resolves: rhbz#1059253 - Man page states default_shell option supersedes + other shell options but in fact override_shell does. +- Use the right domain for AD site resolution +- Related: rhbz#743503 - [RFE] sssd should support DNS sites + +* Wed Jan 29 2014 Jakub Hrozek - 1.11.2-35 +- Resolves: rhbz#1028039 - AD Enumeration reads data from LDAP while + regular lookups connect to GC + +* Wed Jan 29 2014 Jakub Hrozek - 1.11.2-34 +- Resolves: rhbz#877438 - sudoNotBefore/sudoNotAfter not supported by sssd + sudoers plugin + +* Fri Jan 24 2014 Daniel Mach - 1.11.2-33 +- Mass rebuild 2014-01-24 + +* Fri Jan 24 2014 Jakub Hrozek - 1.11.2-32 +- Resolves: rhbz#1054639 - sssd_be aborts a request if it doesn't match + any configured idmap domain + +* Fri Jan 24 2014 Jakub Hrozek - 1.11.2-31 +- Resolves: rhbz#1054899 - explicitly suggest krb5_auth_timeout in a loud + DEBUG message in case Kerberos authentication + times out + +* Wed Jan 22 2014 Jakub Hrozek - 1.11.2-30 +- Resolves: rhbz#1037653 - Enabling ldap_id_mapping doesn't exclude + uidNumber in filter + +* Mon Jan 20 2014 Jakub Hrozek - 1.11.2-29 +- Resolves: rhbz#1051360 - [FJ7.0 Bug]: [REG] sssd_be crashes when + ldap_search_base cannot be parsed. +- Fix a typo in the man page +- Related: rhbz#1034920 - RHEL7 sssd not setting IPA AD trusted user homedir + +* Mon Jan 20 2014 Jakub Hrozek - 1.11.2-28 +- Resolves: rhbz#1054639 - sssd_be aborts a request if it doesn't match + any configured idmap domain +- Fix return value when searching for AD domain flat names +- Resolves: rhbz#1048102 - Access denied for users from gc domain when + using format DOMAIN\user + +* Wed Jan 15 2014 Jakub Hrozek - 1.11.2-27 +- Resolves: rhbz#1034920 - RHEL7 sssd not setting IPA AD trusted user homedir + +* Wed Jan 15 2014 Jakub Hrozek - 1.11.2-26 +- Resolves: rhbz#1048102 - Access denied for users from gc domain when + using format DOMAIN\user + +* Wed Jan 15 2014 Jakub Hrozek - 1.11.2-25 +- Resolves: rhbz#1053106 - sssd ad trusted sub domain do not inherit + fallbacks and overrides settings + +* Thu Jan 09 2014 Jakub Hrozek - 1.11.2-24 +- Resolves: rhbz#1051016 - FAST does not work in SSSD 1.11.2 in Fedora 20 + +* Thu Jan 09 2014 Jakub Hrozek - 1.11.2-23 +- Resolves: rhbz#1033133 - "System Error" when invalid ad_access_filter + is used + +* Thu Jan 09 2014 Jakub Hrozek - 1.11.2-22 +- Resolves: rhbz#1032983 - sssd_be crashes when ad_access_filter uses + FOREST keyword. +- Fix two memory leaks in the PAC responder (Related: rhbz#991065) + +* Wed Jan 08 2014 Jakub Hrozek - 1.11.2-21 +- Resolves: rhbz#1048184 - Group lookup does not return member with multiple + names after user lookup + +* Wed Jan 08 2014 Jakub Hrozek - 1.11.2-20 +- Resolves: rhbz#1049533 - Group membership lookup issue + +* Fri Dec 27 2013 Daniel Mach - 1.11.2-19 +- Mass rebuild 2013-12-27 + +* Thu Dec 19 2013 Jakub Hrozek - 1.11.2-18 +- Resolves: rhbz#894068 - sss_cache doesn't support subdomains + +* Thu Dec 19 2013 Jakub Hrozek - 1.11.2-17 +- Re-initialize subdomains after provider startup +- Related: rhbz#1038637 - If SSSD starts offline, subdomains list is + never read + +* Thu Dec 19 2013 Jakub Hrozek - 1.11.2-16 +- The AD provider is able to resolve group memberships for groups with + Global and Universal scope +- Related: rhbz#1033096 - tokenGroups do not work reliable with Global + Catalog + +* Wed Dec 18 2013 Jakub Hrozek - 1.11.2-15 +- Resolves: rhbz#1033096 - tokenGroups do not work reliable with Global + Catalog +- Resolves: rhbz#1030483 - Individual group search returned multiple + results in GC lookups + +* Wed Dec 18 2013 Jakub Hrozek - 1.11.2-14 +- Resolves: rhbz#1040969 - sssd_nss grows memory footprint when netgroups + are requested + +* Thu Dec 12 2013 Jakub Hrozek - 1.11.2-13 +- Resolves: rhbz#1023409 - Valgrind sssd "Syscall param + socketcall.sendto(msg) points to uninitialised + byte(s)" + +* Thu Dec 12 2013 Jakub Hrozek - 1.11.2-12 +- Resolves: rhbz#1037936 - sssd_be crashes occasionally + +* Thu Dec 12 2013 Jakub Hrozek - 1.11.2-11 +- Resolves: rhbz#1038637 - If SSSD starts offline, subdomains list is + never read + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-10 +- Resolves: rhbz#1029631 - sssd_be crashes on manually adding a cleartext + password to ldap_default_authtok + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-9 +- Resolves: rhbz#1036758 - SSSD: Allow for custom attributes in RDN when + using id_provider = proxy + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-8 +- Resolves: rhbz#1034050 - Errors in domain log when saving user to sysdb + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-7 +- Resolves: rhbz#1036157 - sssd can't retrieve auto.master when using the + "default_domain_suffix" option in + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-6 +- Resolves: rhbz#1028057 - Improve detection of the right domain when + processing group with members from several domains + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-5 +- Resolves: rhbz#1033084 - sssd_be segfaults if empty grop is resolved + using ad_matching_rule + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-4 +- Resolves: rhbz#1031562 - Incorrect mention of access_filter in sssd-ad + manpage + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-3 +- Resolves: rhbz#991549 - sssd fails to retrieve netgroups with multiple + CN attributes + +* Mon Dec 2 2013 Jakub Hrozek - 1.11.2-2 +- Skip netgroups that don't provide well-formed triplets +- Related: rhbz#991549 - sssd fails to retrieve netgroups with multiple + CN attributes + +* Wed Oct 30 2013 Jakub Hrozek - 1.11.2-1 +- New upstream release 1.11.2 +- Remove upstreamed patches +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.2 +- Resolves: rhbz#991065 + +* Fri Sep 27 2013 Jakub Hrozek - 1.11.1-2 +- Resolves: rhbz#1019882 - RHEL7 ipa ad trusted user lookups failed with + sssd_be crash +- Resolves: rhbz#1002597 - ad: unable to resolve membership when user is + from different domain than group + +* Fri Sep 27 2013 Jakub Hrozek - 1.11.1-1 +- New upstream release 1.11.1 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.1 +- Resolves: rhbz#991065 - Rebase SSSD to 1.11.0 + +* Thu Aug 29 2013 Jakub Hrozek - 1.11.0-1 +- New upstream release 1.11.0 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.0 +- Resolves: rhbz#991065 + +* Fri Aug 02 2013 Jakub Hrozek - 1.11.0.1beta2 +- New upstream release 1.11 beta 2 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.0beta2 +- Related: rhbz#991065 + +* Wed Jul 31 2013 Jakub Hrozek - 1.10.1-5 +- Resolves: #906427 - Do not use %%{_lib} in specfile for the nss and + pam libraries + +* Wed Jul 31 2013 Jakub Hrozek - 1.10.1-4 +- Resolves: #983587 - sss_debuglevel did not increase verbosity in + sssd_pac.log + +* Wed Jul 31 2013 Jakub Hrozek - 1.10.1-3 +- Resolves: #983580 - Netgroups should ignore the 'use_fully_qualified_names' + setting + +* Wed Jul 31 2013 Jakub Hrozek - 1.10.1-2 +- Apply several important fixes from upstream 1.10 branch +- Related: #966757 - SSSD failover doesn't work if the first DNS server + in resolv.conf is unavailable + +* Thu Jul 18 2013 Jakub Hrozek - 1.10.1-1 +- New upstream release 1.10.1 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.1 + +* Wed Jul 10 2013 Jakub Hrozek - 1.10.0-18 +- Remove libcmocka dependency + +* Mon Jul 08 2013 Jakub Hrozek - 1.10.0-17 +- sssd-tools should require sssd-common, not sssd + +* Tue Jul 02 2013 Stephen Gallagher - 1.10.0-16 +- Move sssd_pac to the sssd-ipa and sssd-ad subpackages +- Trim out RHEL5-specific macros since we don't build on RHEL 5 +- Trim out macros for Fedora older than F18 +- Update libldb requirement to 1.1.16 +- Trim RPM changelog down to the last year + +* Tue Jul 02 2013 Stephen Gallagher - 1.10.0-15 +- Move sssd_pac to the sssd-krb5 subpackage + +* Mon Jul 01 2013 Stephen Gallagher - 1.10.0-14 +- Fix Obsoletes: to account for dist tag +- Convert post and pre scripts to run on the sssd-common subpackage +- Remove old conversion from SYSV + +* Thu Jun 27 2013 Jakub Hrozek - 1.10.0-13 +- New upstream release 1.10 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0 + +* Mon Jun 17 2013 Dan Horák - 1.10.0-12.beta2 +- the cmocka toolkit exists only on selected arches + +* Sun Jun 16 2013 Jakub Hrozek - 1.10.0-11.beta2 +- Apply a number of patches from upstream to fix issues found post-beta, + in particular: + -- segfault with a high DEBUG level + -- Fix IPA password migration (upstream #1873) + -- Fix fail over when retrying SRV resolution (upstream #1886) + +* Thu Jun 13 2013 Jakub Hrozek - 1.10.0-10.beta2 +- Only BuildRequire libcmocka on Fedora + +* Thu Jun 13 2013 Jakub Hrozek - 1.10.0-9.beta2 +- Fix typo in Requires that prevented an upgrade (#973916) +- Use a hardcoded version in Conflicts, not less-than-current + +* Wed Jun 12 2013 Jakub Hrozek - 1.10.0-8.beta2 +- New upstream release 1.10 beta2 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0beta2 +- BuildRequire libcmocka-devel in order to run all upstream tests during build +- BuildRequire libnl3 instead of libnl1 +- No longer BuildRequire initscripts, we no longer use /sbin/service +- Remove explicit krb5-libs >= 1.10 requires; this platform doensn't carry any + older krb5-libs version + +* Thu Jun 06 2013 Jakub Hrozek - 1.10.0-7.beta1 +- Enable hardened build for RHEL7 + +* Fri May 24 2013 Jakub Hrozek - 1.10.0-6.beta1 +- Apply a couple of patches from upstream git that resolve crashes when + ID mapping object was not initialized properly but needed later + +* Tue May 14 2013 Jakub Hrozek - 1.10.0-5.beta1 +- Resolves: rhbz#961357 - Missing dyndns_update entry in sssd.conf during + realm join +- Resolves: rhbz#961278 - Login failure: Enterprise Principal enabled by + default for AD Provider +- Resolves: rhbz#961251 - sssd does not create user's krb5 ccache dir/file + parent directory when logging in + +* Tue May 7 2013 Jakub Hrozek - 1.10.0-4.beta1 +- Explicitly Require libini_config >= 1.0.0.1 to work around a SONAME bug + in ding-libs +- Fix SSH integration with fully-qualified domains +- Add the ability to dynamically discover the NetBIOS name + +* Fri May 3 2013 Jakub Hrozek - 1.10.0-3.beta1 +- New upstream release 1.10 beta1 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0beta1 + +* Wed Apr 17 2013 Jakub Hrozek - 1.10.0-2.alpha1 +- Add a patch to fix krb5 ccache creation issue with krb5 1.11 + +* Tue Apr 2 2013 Jakub Hrozek - 1.10.0-1.alpha1 +- New upstream release 1.10 alpha1 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0alpha1 + +* Fri Mar 01 2013 Stephen Gallagher - 1.9.4-9 +- Split internal helper libraries into a shared object +- Significantly reduce disk-space usage + +* Thu Feb 14 2013 Jakub Hrozek - 1.9.4-8 +- Fix the Kerberos password expiration warning (#912223) + +* Thu Feb 14 2013 Jakub Hrozek - 1.9.4-7 +- Do not write out dots in the domain-realm mapping file (#905650) + +* Mon Feb 11 2013 Jakub Hrozek - 1.9.4-6 +- Include upstream patch to build with krb5-1.11 + +* Thu Feb 07 2013 Jakub Hrozek - 1.9.4-5 +- Rebuild against new libldb + +* Mon Feb 04 2013 Jakub Hrozek - 1.9.4-4 +- Fix build with new automake versions + +* Wed Jan 30 2013 Jakub Hrozek - 1.9.4-3 +- Recreate Kerberos ccache directory if it's missing +- Resolves: rhbz#853558 - [sssd[krb5_child[PID]]]: Credential cache + directory /run/user/UID/ccdir does not exist + +* Tue Jan 29 2013 Jakub Hrozek - 1.9.4-2 +- Fix changelog dates to make F19 rpmbuild happy + +* Mon Jan 28 2013 Jakub Hrozek - 1.9.4-1 +- New upstream release 1.9.4 + +* Thu Dec 06 2012 Jakub Hrozek - 1.9.3-1 +- New upstream release 1.9.3 + +* Tue Oct 30 2012 Jakub Hrozek - 1.9.2-5 +- Resolve groups from AD correctly + +* Tue Oct 30 2012 Jakub Hrozek - 1.9.2-4 +- Check the validity of naming context + +* Thu Oct 18 2012 Jakub Hrozek - 1.9.2-3 +- Move the sss_cache tool to the main package + +* Sun Oct 14 2012 Jakub Hrozek - 1.9.2-2 +- Include the 1.9.2 tarball + +* Sun Oct 14 2012 Jakub Hrozek - 1.9.2-1 +- New upstream release 1.9.2 + +* Sun Oct 07 2012 Jakub Hrozek - 1.9.1-1 +- New upstream release 1.9.1 + +* Wed Oct 03 2012 Jakub Hrozek - 1.9.0-24 +- require the latest libldb + +* Tue Sep 25 2012 Jakub Hrozek - 1.9.0-24 +- Use mcpath insted of mcachepath macro to be consistent with + upsteam spec file + +* Tue Sep 25 2012 Jakub Hrozek - 1.9.0-23 +- New upstream release 1.9.0 + +* Fri Sep 14 2012 Jakub Hrozek - 1.9.0-22.rc1 +- New upstream release 1.9.0 rc1 + +* Thu Sep 06 2012 Jakub Hrozek - 1.9.0-21.beta7 +- New upstream release 1.9.0 beta7 +- obsoletes patches #1-#3 + +* Mon Sep 03 2012 Jakub Hrozek - 1.9.0-20.beta6 +- Rebuild against libldb 1.12 + +* Tue Aug 28 2012 Jakub Hrozek - 1.9.0-19.beta6 +- Rebuild against libldb 1.11 + +* Fri Aug 24 2012 Jakub Hrozek - 1.9.0-18.beta6 +- Change the default ccache location to DIR:/run/user/${UID}/krb5cc + and patch man page accordingly +- Resolves: rhbz#851304 + +* Mon Aug 20 2012 Jakub Hrozek - 1.9.0-17.beta6 +- Rebuild against libldb 1.10 + +* Fri Aug 17 2012 Jakub Hrozek - 1.9.0-16.beta6 +- Only create the SELinux login file if there are SELinux mappings on + the IPA server + +* Fri Aug 10 2012 Jakub Hrozek - 1.9.0-14.beta6 +- Don't discard HBAC rule processing result if SELinux is on + Resolves: rhbz#846792 (CVE-2012-3462) + +* Thu Aug 02 2012 Jakub Hrozek - 1.9.0-13.beta6 +- New upstream release 1.9.0 beta 6 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta6 +- A new option, override_shell was added. If this option is set, all users + managed by SSSD will have their shell set to its value. +- Fixes for the support for setting default SELinux user context from FreeIPA. +- Fixed a regression introduced in beta 5 that broke LDAP SASL binds +- The SSSD supports the concept of a Primary Server and a Back Up Server in + failover +- A new command-line tool sss_seed is available to help prime the cache with + a user record when deploying a new machine +- SSSD is now able to discover and save the domain-realm mappings + between an IPA server and a trusted Active Directory server. +- Packaging changes to fix ldconfig usage in subpackages (#843995) +- Rebuild against libldb 1.1.9 + +* Fri Jul 27 2012 Fedora Release Engineering - 1.9.0-13.beta5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Thu Jul 19 2012 Jakub Hrozek - 1.9.0-12.beta5 +- New upstream release 1.9.0 beta 5 +- Obsoletes the patch for missing DP_OPTION_TERMINATOR in AD provider options +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta5 +- Many fixes for the support for setting default SELinux user context from + FreeIPA, most notably fixed the specificity evaluation +- Fixed an incorrect default in the krb5_canonicalize option of the AD + provider which was preventing password change operation +- The shadowLastChange attribute value is now correctly updated with the + number of days since the Epoch, not seconds + +* Mon Jul 16 2012 Stephen Gallagher - 1.9.0-11.beta4 +- Fix broken ARM build +- Add missing DP_OPTION_TERMINATOR in AD provider options + +* Wed Jul 11 2012 Jakub Hrozek - 1.9.0-10.beta4 +- Own several directories create during make install (#839782) + +* Wed Jul 11 2012 Jakub Hrozek - 1.9.0-9.beta4 +- New upstream release 1.9.0 beta 4 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta4 +- Add a new AD provider to improve integration with Active Directory 2008 R2 + or later servers +- SUDO integration was completely rewritten. The new implementation works + with multiple domains and uses an improved refresh mechanism to download + only the necessary rules +- The IPA authentication provider now supports subdomains +- Fixed regression for setups that were setting default_tkt_enctypes + manually by reverting a previous workaround. + +* Mon Jun 25 2012 Stephen Gallagher - 1.9.0-8.beta3 +- New upstream release 1.9.0 beta 3 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta3 +- Add a new PAC responder for dealing with cross-realm Kerberos trusts +- Terminate idle connections to the NSS and PAM responders + +* Wed Jun 20 2012 Stephen Gallagher - 1.9.0-7.beta2 +- Switch unicode library from libunistring to Glib +- Drop unnecessary explicit Requires on keyutils +- Guarantee that versioned Requires include the correct architecture + +* Mon Jun 18 2012 Stephen Gallagher - 1.9.0-6.beta2 +- Fix accidental disabling of the DIR cache support + +* Fri Jun 15 2012 Stephen Gallagher - 1.9.0-5.beta2 +- New upstream release 1.9.0 beta 2 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta2 +- Add support for the Kerberos DIR cache for storing multiple TGTs + automatically +- Major performance enhancement when storing large groups in the cache +- Major performance enhancement when performing initgroups() against Active + Directory +- SSSDConfig data file default locations can now be set during configure for + easier packaging + +* Tue May 29 2012 Stephen Gallagher - 1.9.0-4.beta1 +- Fix regression in endianness patch + +* Tue May 29 2012 Stephen Gallagher - 1.9.0-3.beta1 +- Rebuild SSSD against ding-libs 0.3.0beta1 +- Fix endianness bug in service map protocol + +* Thu May 24 2012 Stephen Gallagher - 1.9.0-2.beta1 +- Fix several regressions since 1.5.x +- Ensure that the RPM creates the /var/lib/sss/mc directory +- Add support for Netscape password warning expiration control +- Rebuild against libldb 1.1.6 + +* Fri May 11 2012 Stephen Gallagher - 1.9.0-1.beta1 +- New upstream release 1.9.0 beta 1 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta1 +- Add native support for autofs to the IPA provider +- Support for ID-mapping when connecting to Active Directory +- Support for handling very large (> 1500 users) groups in Active Directory +- Support for sub-domains (will be used for dealing with trust relationships) +- Add a new fast in-memory cache to speed up lookups of cached data on + repeated requests + +* Thu May 03 2012 Stephen Gallagher - 1.8.3-11 +- New upstream release 1.8.3 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.3 +- Numerous manpage and translation updates +- LDAP: Handle situations where the RootDSE isn't available anonymously +- LDAP: Fix regression for users using non-standard LDAP attributes for user + information + +* Mon Apr 09 2012 Stephen Gallagher - 1.8.2-10 +- New upstream release 1.8.2 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.2 +- Several fixes to case-insensitive domain functions +- Fix for GSSAPI binds when the keytab contains unrelated principals +- Fixed several segfaults +- Workarounds added for LDAP servers with unreadable RootDSE +- SSH knownhostproxy will no longer enter an infinite loop preventing login +- The provided SYSV init script now starts SSSD earlier at startup and stops + it later during shutdown +- Assorted minor fixes for issues discovered by static analysis tools + +* Mon Mar 26 2012 Stephen Gallagher - 1.8.1-9 +- Don't duplicate libsss_autofs.so in two packages +- Set explicit package contents instead of globbing + +* Wed Mar 21 2012 Stephen Gallagher - 1.8.1-8 +- Fix uninitialized value bug causing crashes throughout the code +- Resolves: rhbz#804783 - [abrt] Segfault during LDAP 'services' lookup + +* Mon Mar 12 2012 Stephen Gallagher - 1.8.1-7 +- New upstream release 1.8.1 +- Resolve issue where we could enter an infinite loop trying to connect to an + auth server +- Fix serious issue with complex (3+ levels) nested groups +- Fix netgroup support for case-insensitivity and aliases +- Fix serious issue with lookup bundling resulting in requests never + completing +- IPA provider will now check the value of nsAccountLock during pam_acct_mgmt + in addition to pam_authenticate +- Fix several regressions in the proxy provider +- Resolves: rhbz#743133 - Performance regression with Kerberos authentication + against AD +- Resolves: rhbz#799031 - --debug option for sss_debuglevel doesn't work + +* Tue Feb 28 2012 Stephen Gallagher - 1.8.0-6 +- New upstream release 1.8.0 +- Support for the service map in NSS +- Support for setting default SELinux user context from FreeIPA +- Support for retrieving SSH user and host keys from LDAP (Experimental) +- Support for caching autofs LDAP requests (Experimental) +- Support for caching SUDO rules (Experimental) +- Include the IPA AutoFS provider +- Fixed several memory-corruption bugs +- Fixed a regression in group enumeration since 1.7.0 +- Fixed a regression in the proxy provider +- Resolves: rhbz#741981 - Separate Cache Timeouts for SSSD +- Resolves: rhbz#797968 - sssd_be: The requested tar get is not configured is + logged at each login +- Resolves: rhbz#754114 - [abrt] sssd-1.6.3-1.fc16: ping_check: Process + /usr/sbin/sssd was killed by signal 11 (SIGSEGV) +- Resolves: rhbz#743133 - Performance regression with Kerberos authentication + against AD +- Resolves: rhbz#773706 - SSSD fails during autodetection of search bases for + new LDAP features +- Resolves: rhbz#786957 - sssd and kerberos should change the default location for create the Credential Cashes to /run/usr/USERNAME/krb5cc + +* Wed Feb 22 2012 Stephen Gallagher - 1.8.0-5.beta3 +- Change default kerberos credential cache location to /run/user/ + +* Wed Feb 15 2012 Stephen Gallagher - 1.8.0-4.beta3 +- New upstream release 1.8.0 beta 3 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.0beta3 +- Fixed a regression in group enumeration since 1.7.0 +- Fixed several memory-corruption bugs +- Finalized the ABI for the autofs support +- Fixed a regression in the proxy provider + +* Fri Feb 10 2012 Petr Pisar - 1.8.0-3.beta2 +- Rebuild against PCRE 8.30 + +* Mon Feb 06 2012 Stephen Gallagher - 1.8.0-1.beta2 +- New upstream release +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.0beta2 +- Fix two minor manpage bugs +- Include the IPA AutoFS provider + +* Mon Feb 06 2012 Stephen Gallagher - 1.8.0-1.beta1 +- New upstream release +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.0beta1 +- Support for the service map in NSS +- Support for setting default SELinux user context from FreeIPA +- Support for retrieving SSH user and host keys from LDAP (Experimental) +- Support for caching autofs LDAP requests (Experimental) +- Support for caching SUDO rules (Experimental) + +* Wed Feb 01 2012 Stephen Gallagher - 1.7.0-5 +- Resolves: rhbz#773706 - SSSD fails during autodetection of search bases for + new LDAP features - fix netgroups and sudo as well + +* Wed Feb 01 2012 Stephen Gallagher - 1.7.0-4 +- Fixes a serious memory hierarchy bug causing unpredictable behavior in the + LDAP provider. + +* Wed Feb 01 2012 Stephen Gallagher - 1.7.0-3 +- Resolves: rhbz#773706 - SSSD fails during autodetection of search bases for + new LDAP features + +* Sat Jan 14 2012 Fedora Release Engineering - 1.7.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Dec 22 2011 Stephen Gallagher - 1.7.0-1 +- New upstream release 1.7.0 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.7.0 +- Support for case-insensitive domains +- Support for multiple search bases in the LDAP provider +- Support for the native FreeIPA netgroup implementation +- Reliability improvements to the process monitor +- New DEBUG facility with more consistent log levels +- New tool to change debug log levels without restarting SSSD +- SSSD will now disconnect from LDAP server when idle +- FreeIPA HBAC rules can choose to ignore srchost options for significant + performance gains +- Assorted performance improvements in the LDAP provider + +* Mon Dec 19 2011 Stephen Gallagher - 1.6.4-1 +- New upstream release 1.6.4 +- Rolls up previous patches applied to the 1.6.3 tarball +- Fixes a rare issue causing crashes in the failover logic +- Fixes an issue where SSSD would return the wrong PAM error code for users + that it does not recognize. + +* Wed Dec 07 2011 Stephen Gallagher - 1.6.3-5 +- Rebuild against libldb 1.1.4 + +* Tue Nov 29 2011 Stephen Gallagher - 1.6.3-4 +- Resolves: rhbz#753639 - sssd_nss crashes when passed invalid UTF-8 for the + username in getpwnam() +- Resolves: rhbz#758425 - LDAP failover not working if server refuses + connections + +* Thu Nov 24 2011 Jakub Hrozek - 1.6.3-3 +- Rebuild for libldb 1.1.3 + +* Thu Nov 10 2011 Stephen Gallagher - 1.6.3-2 +- Resolves: rhbz#752495 - Crash when apply settings + +* Fri Nov 04 2011 Stephen Gallagher - 1.6.3-1 +- New upstream release 1.6.3 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.6.3 +- Fixes a major cache performance issue introduced in 1.6.2 +- Fixes a potential infinite-loop with certain LDAP layouts + +* Wed Oct 26 2011 Fedora Release Engineering - 1.6.2-5 +- Rebuilt for glibc bug#747377 + +* Sun Oct 23 2011 Stephen Gallagher - 1.6.2-4 +- Change selinux policy requirement to Conflicts: with the old version, + rather than Requires: the supported version. + +* Fri Oct 21 2011 Stephen Gallagher - 1.6.2-3 +- Add explicit requirement on selinux-policy version to address new SBUS + symlinks. + +* Wed Oct 19 2011 Stephen Gallagher - 1.6.2-2 +- Remove %%files reference to sss_debuglevel copied from wrong upstreeam + spec file. + +* Tue Oct 18 2011 Stephen Gallagher - 1.6.2-1 +- Improved handling of users and groups with multi-valued name attributes + (aliases) +- Performance enhancements + Initgroups on RFC2307bis/FreeIPA + HBAC rule processing +- Improved process-hang detection and restarting +- Enabled the midpoint cache refresh by default (fewer cache misses on + commonly-used entries) +- Cleaned up the example configuration +- New tool to change debug level on the fly + +* Mon Aug 29 2011 Stephen Gallagher - 1.6.1-1 +- New upstream release 1.6.1 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.6.1 +- Fixes a serious issue with LDAP connections when the communication is + dropped (e.g. VPN disconnection, waking from sleep) +- SSSD is now less strict when dealing with users/groups with multiple names + when a definitive primary name cannot be determined +- The LDAP provider will no longer attempt to canonicalize by default when + using SASL. An option to re-enable this has been provided. +- Fixes for non-standard LDAP attribute names (e.g. those used by Active + Directory) +- Three HBAC regressions have been fixed. +- Fix for an infinite loop in the deref code + +* Wed Aug 03 2011 Stephen Gallagher - 1.6.0-2 +- Build with _hardened_build macro + +* Wed Aug 03 2011 Stephen Gallagher - 1.6.0-1 +- New upstream release 1.6.0 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.6.0 +- Add host access control support for LDAP (similar to pam_host_attr) +- Finer-grained control on principals used with Kerberos (such as for FAST or +- validation) +- Added a new tool sss_cache to allow selective expiring of cached entries +- Added support for LDAP DEREF and ASQ controls +- Added access control features for Novell Directory Server +- FreeIPA dynamic DNS update now checks first to see if an update is needed +- Complete rewrite of the HBAC library +- New libraries: libipa_hbac and libipa_hbac-python + +* Tue Jul 05 2011 Stephen Gallagher - 1.5.11-2 +- New upstream release 1.5.11 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.11 +- Fix a serious regression that prevented SSSD from working with ldaps:// URIs +- IPA Provider: Fix a bug with dynamic DNS that resulted in the wrong IPv6 +- address being saved to the AAAA record + +* Fri Jul 01 2011 Stephen Gallagher - 1.5.10-1 +- New upstream release 1.5.10 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.10 +- Fixed a regression introduced in 1.5.9 that could result in blocking calls +- to LDAP + +* Thu Jun 30 2011 Stephen Gallagher - 1.5.9-1 +- New upstream release 1.5.9 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.9 +- Support for overriding home directory, shell and primary GID locally +- Properly honor TTL values from SRV record lookups +- Support non-POSIX groups in nested group chains (for RFC2307bis LDAP +- servers) +- Properly escape IPv6 addresses in the failover code +- Do not crash if inotify fails (e.g. resource exhaustion) +- Don't add multiple TGT renewal callbacks (too many log messages) + +* Fri May 27 2011 Stephen Gallagher - 1.5.8-1 +- New upstream release 1.5.8 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.8 +- Support for the LDAP paging control +- Support for multiple DNS servers for name resolution +- Fixes for several group membership bugs +- Fixes for rare crash bugs + +* Mon May 23 2011 Stephen Gallagher - 1.5.7-3 +- Resolves: rhbz#706740 - Orphaned links on rc0.d-rc6.d +- Make sure to properly convert to systemd if upgrading from newer +- updates for Fedora 14 + +* Mon May 02 2011 Stephen Gallagher - 1.5.7-2 +- Fix segfault in TGT renewal + +* Fri Apr 29 2011 Stephen Gallagher - 1.5.7-1 +- Resolves: rhbz#700891 - CVE-2011-1758 sssd: automatic TGT renewal overwrites +- cached password with predicatable filename + +* Wed Apr 20 2011 Stephen Gallagher - 1.5.6.1-1 +- Re-add manpage translations + +* Wed Apr 20 2011 Stephen Gallagher - 1.5.6-1 +- New upstream release 1.5.6 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.6 +- Fixed a serious memory leak in the memberOf plugin +- Fixed a regression with the negative cache that caused it to be essentially +- nonfunctional +- Fixed an issue where the user's full name would sometimes be removed from +- the cache +- Fixed an issue with password changes in the kerberos provider not working +- with kpasswd + +* Wed Apr 20 2011 Stephen Gallagher - 1.5.5-5 +- Resolves: rhbz#697057 - kpasswd fails when using sssd and +- kadmin server != kdc server +- Upgrades from SysV should now maintain enabled/disabled status + +* Mon Apr 18 2011 Stephen Gallagher - 1.5.5-4 +- Fix %%postun + +* Thu Apr 14 2011 Stephen Gallagher - 1.5.5-3 +- Fix systemd conversion. Upgrades from SysV to systemd weren't properly +- enabling the systemd service. +- Fix a serious memory leak in the memberOf plugin +- Fix an issue where the user's full name would sometimes be removed +- from the cache + +* Tue Apr 12 2011 Stephen Gallagher - 1.5.5-2 +- Install systemd unit file instead of sysv init script + +* Tue Apr 12 2011 Stephen Gallagher - 1.5.5-1 +- New upstream release 1.5.5 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.5 +- Fixes for several crash bugs +- LDAP group lookups will no longer abort if there is a zero-length member +- attribute +- Add automatic fallback to 'cn' if the 'gecos' attribute does not exist + +* Thu Mar 24 2011 Stephen Gallagher - 1.5.4-1 +- New upstream release 1.5.4 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.4 +- Fixes for Active Directory when not all users and groups have POSIX attributes +- Fixes for handling users and groups that have name aliases (aliases are ignored) +- Fix group memberships after initgroups in the IPA provider + +* Thu Mar 17 2011 Stephen Gallagher - 1.5.3-2 +- Resolves: rhbz#683267 - sssd 1.5.1-9 breaks AD authentication + +* Fri Mar 11 2011 Stephen Gallagher - 1.5.3-1 +- New upstream release 1.5.3 +- Support for libldb >= 1.0.0 + +* Thu Mar 10 2011 Stephen Gallagher - 1.5.2-1 +- New upstream release 1.5.2 +- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.2 +- Fixes for support of FreeIPA v2 +- Fixes for failover if DNS entries change +- Improved sss_obfuscate tool with better interactive mode +- Fix several crash bugs +- Don't attempt to use START_TLS over SSL. Some LDAP servers can't handle this +- Delete users from the local cache if initgroups calls return 'no such user' +- (previously only worked for getpwnam/getpwuid) +- Use new Transifex.net translations +- Better support for automatic TGT renewal (now survives restart) +- Netgroup fixes + +* Sun Feb 27 2011 Simo Sorce - 1.5.1-9 +- Rebuild sssd against libldb 1.0.2 so the memberof module loads again. +- Related: rhbz#677425 + +* Mon Feb 21 2011 Stephen Gallagher - 1.5.1-8 +- Resolves: rhbz#677768 - name service caches names, so id command shows +- recently deleted users + +* Fri Feb 11 2011 Stephen Gallagher - 1.5.1-7 +- Ensure that SSSD builds against libldb-1.0.0 on F15 and later +- Remove .la for memberOf + +* Fri Feb 11 2011 Stephen Gallagher - 1.5.1-6 +- Fix memberOf install path + +* Fri Feb 11 2011 Stephen Gallagher - 1.5.1-5 +- Add support for libldb 1.0.0 + +* Wed Feb 09 2011 Fedora Release Engineering - 1.5.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Feb 01 2011 Stephen Gallagher - 1.5.1-3 +- Fix nested group member filter sanitization for RFC2307bis +- Put translated tool manpages into the sssd-tools subpackage + +* Thu Jan 27 2011 Stephen Gallagher - 1.5.1-2 +- Restore Requires: cyrus-sasl-gssapi as it is not auto-detected during +- rpmbuild + +* Thu Jan 27 2011 Stephen Gallagher - 1.5.1-1 +- New upstream release 1.5.1 +- Addresses CVE-2010-4341 - DoS in sssd PAM responder can prevent logins +- Vast performance improvements when enumerate = true +- All PAM actions will now perform a forced initgroups lookup instead of just +- a user information lookup +- This guarantees that all group information is available to other +- providers, such as the simple provider. +- For backwards-compatibility, DNS lookups will also fall back to trying the +- SSSD domain name as a DNS discovery domain. +- Support for more password expiration policies in LDAP +- 389 Directory Server +- FreeIPA +- ActiveDirectory +- Support for ldap_tls_{cert,key,cipher_suite} config options +-Assorted bugfixes + +* Tue Jan 11 2011 Stephen Gallagher - 1.5.0-2 +- CVE-2010-4341 - DoS in sssd PAM responder can prevent logins + +* Wed Dec 22 2010 Stephen Gallagher - 1.5.0-1 +- New upstream release 1.5.0 +- Fixed issues with LDAP search filters that needed to be escaped +- Add Kerberos FAST support on platforms that support it +- Reduced verbosity of PAM_TEXT_INFO messages for cached credentials +- Added a Kerberos access provider to honor .k5login +- Addressed several thread-safety issues in the sss_client code +- Improved support for delayed online Kerberos auth +- Significantly reduced time between connecting to the network/VPN and +- acquiring a TGT +- Added feature for automatic Kerberos ticket renewal +- Provides the kerberos ticket for long-lived processes or cron jobs +- even when the user logs out +- Added several new features to the LDAP access provider +- Support for 'shadow' access control +- Support for authorizedService access control +- Ability to mix-and-match LDAP access control features +- Added an option for a separate password-change LDAP server for those +- platforms where LDAP referrals are not supported +- Added support for manpage translations + + +* Thu Nov 18 2010 Stephen Gallagher - 1.4.1-3 +- Solve a shutdown race-condition that sometimes left processes running +- Resolves: rhbz#606887 - SSSD stops on upgrade + +* Tue Nov 16 2010 Stephen Gallagher - 1.4.1-2 +- Log startup errors to the syslog +- Allow cache cleanup to be disabled in sssd.conf + +* Mon Nov 01 2010 Stephen Gallagher - 1.4.1-1 +- New upstream release 1.4.1 +- Add support for netgroups to the proxy provider +- Fixes a minor bug with UIDs/GIDs >= 2^31 +- Fixes a segfault in the kerberos provider +- Fixes a segfault in the NSS responder if a data provider crashes +- Correctly use sdap_netgroup_search_base + +* Mon Oct 18 2010 Stephen Gallagher - 1.4.0-2 +- Fix incorrect tarball URL + +* Mon Oct 18 2010 Stephen Gallagher - 1.4.0-1 +- New upstream release 1.4.0 +- Added support for netgroups to the LDAP provider +- Performance improvements made to group processing of RFC2307 LDAP servers +- Fixed nested group issues with RFC2307bis LDAP servers without a memberOf plugin +- Build-system improvements to support Gentoo +- Split out several libraries into the ding-libs tarball +- Manpage reviewed and updated + +* Mon Oct 04 2010 Stephen Gallagher - 1.3.0-35 +- Fix pre and post script requirements + +* Mon Oct 04 2010 Stephen Gallagher - 1.3.0-34 +- Resolves: rhbz#606887 - sssd stops on upgrade + +* Fri Oct 01 2010 Stephen Gallagher - 1.3.0-33 +- Resolves: rhbz#626205 - Unable to unlock screen + +* Tue Sep 28 2010 Stephen Gallagher - 1.3.0-32 +- Resolves: rhbz#637955 - libini_config-devel needs libcollection-devel but +- doesn't require it + +* Thu Sep 16 2010 Stephen Gallagher - 1.3.0-31 +- Resolves: rhbz#632615 - the krb5 locator plugin isn't packaged for multilib + +* Tue Aug 24 2010 Stephen Gallagher - 1.3.0-30 +- Resolves: CVE-2010-2940 - sssd allows null password entry to authenticate +- against LDAP + +* Thu Jul 22 2010 David Malcolm - 1.2.91-21 +- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild + +* Fri Jul 09 2010 Stephen Gallagher - 1.2.91-20 +- New upstream version 1.2.91 (1.3.0rc1) +- Improved LDAP failover +- Synchronous sysdb API (provides performance enhancements) +- Better online reconnection detection + +* Mon Jun 21 2010 Stephen Gallagher - 1.2.1-15 +- New stable upstream version 1.2.1 +- Resolves: rhbz#595529 - spec file should eschew %%define in favor of +- %%global +- Resolves: rhbz#593644 - Empty list of simple_allow_users causes sssd service +- to fail while restart. +- Resolves: rhbz#599026 - Makefile typo causes SSSD not to use the kernel +- keyring +- Resolves: rhbz#599724 - sssd is broken on Rawhide + +* Mon May 24 2010 Stephen Gallagher - 1.2.0-12 +- New stable upstream version 1.2.0 +- Support ServiceGroups for FreeIPA v2 HBAC rules +- Fix long-standing issue with auth_provider = proxy +- Better logging for TLS issues in LDAP + +* Tue May 18 2010 Stephen Gallagher - 1.1.92-11 +- New LDAP access provider allows for filtering user access by LDAP attribute +- Reduced default timeout for detecting offline status with LDAP +- GSSAPI ticket lifetime made configurable +- Better offline->online transition support in Kerberos + +* Fri May 07 2010 Stephen Gallagher - 1.1.91-10 +- Release new upstream version 1.1.91 +- Enhancements when using SSSD with FreeIPA v2 +- Support for deferred kinit +- Support for DNS SRV records for failover + +* Fri Apr 02 2010 Simo Sorce - 1.1.1-3 +- Bump up release number to avoid library sub-packages version issues with + previous releases. + +* Thu Apr 01 2010 Stephen Gallagher - 1.1.1-1 +- New upstream release 1.1.1 +- Fixed the IPA provider (which was segfaulting at start) +- Fixed a bug in the SSSDConfig API causing some options to revert to +- their defaults +- This impacted the Authconfig UI +- Ensure that SASL binds to LDAP auto-retry when interrupted by a signal + +* Tue Mar 23 2010 Stephen Gallagher - 1.1.0-2 +- Release SSSD 1.1.0 final +- Fix two potential segfaults +- Fix memory leak in monitor +- Better error message for unusable confdb + +* Wed Mar 17 2010 Stephen Gallagher - 1.1.0-1.pre20100317git0ea7f19 +- Release candidate for SSSD 1.1 +- Add simple access provider +- Create subpackages for libcollection, libini_config, libdhash and librefarray +- Support IPv6 +- Support LDAP referrals +- Fix cache issues +- Better feedback from PAM when offline + +* Wed Feb 24 2010 Stephen Gallagehr - 1.0.5-2 +- Rebuild against new libtevent + +* Fri Feb 19 2010 Stephen Gallagher - 1.0.5-1 +- Fix licenses in sources and on RPMs + +* Mon Jan 25 2010 Stephen Gallagher - 1.0.4-1 +- Fix regression on 64-bit platforms + +* Fri Jan 22 2010 Stephen Gallagher - 1.0.3-1 +- Fixes link error on platforms that do not do implicit linking +- Fixes double-free segfault in PAM +- Fixes double-free error in async resolver +- Fixes support for TCP-based DNS lookups in async resolver +- Fixes memory alignment issues on ARM processors +- Manpage fixes + +* Thu Jan 14 2010 Stephen Gallagher - 1.0.2-1 +- Fixes a bug in the failover code that prevented the SSSD from detecting when it went back online +- Fixes a bug causing long (sometimes multiple-minute) waits for NSS requests +- Several segfault bugfixes + +* Mon Jan 11 2010 Stephen Gallagher - 1.0.1-1 +- Fix CVE-2010-0014 + +* Mon Dec 21 2009 Stephen Gallagher - 1.0.0-2 +- Patch SSSDConfig API to address +- https://bugzilla.redhat.com/show_bug.cgi?id=549482 + +* Fri Dec 18 2009 Stephen Gallagher - 1.0.0-1 +- New upstream stable release 1.0.0 + +* Fri Dec 11 2009 Stephen Gallagher - 0.99.1-1 +- New upstream bugfix release 0.99.1 + +* Mon Nov 30 2009 Stephen Gallagher - 0.99.0-1 +- New upstream release 0.99.0 + +* Tue Oct 27 2009 Stephen Gallagher - 0.7.1-1 +- Fix segfault in sssd_pam when cache_credentials was enabled +- Update the sample configuration +- Fix upgrade issues caused by data provider service removal + +* Mon Oct 26 2009 Stephen Gallagher - 0.7.0-2 +- Fix upgrade issues from old (pre-0.5.0) releases of SSSD + +* Fri Oct 23 2009 Stephen Gallagher - 0.7.0-1 +- New upstream release 0.7.0 + +* Thu Oct 15 2009 Stephen Gallagher - 0.6.1-2 +- Fix missing file permissions for sssd-clients + +* Tue Oct 13 2009 Stephen Gallagher - 0.6.1-1 +- Add SSSDConfig API +- Update polish translation for 0.6.0 +- Fix long timeout on ldap operation +- Make dp requests more robust + +* Tue Sep 29 2009 Stephen Gallagher - 0.6.0-1 +- Ensure that the configuration upgrade script always writes the config + file with 0600 permissions +- Eliminate an infinite loop in group enumerations + +* Mon Sep 28 2009 Sumit Bose - 0.6.0-0 +- New upstream release 0.6.0 + +* Mon Aug 24 2009 Simo Sorce - 0.5.0-0 +- New upstream release 0.5.0 + +* Wed Jul 29 2009 Jakub Hrozek - 0.4.1-4 +- Fix for CVE-2009-2410 - Native SSSD users with no password set could log in + without a password. (Patch by Stephen Gallagher) + +* Sun Jul 26 2009 Fedora Release Engineering - 0.4.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Mon Jun 22 2009 Simo Sorce - 0.4.1-2 +- Fix a couple of segfaults that may happen on reload + +* Thu Jun 11 2009 Simo Sorce - 0.4.1-1 +- add missing configure check that broke stopping the daemon +- also fix default config to add a missing required option + +* Mon Jun 8 2009 Simo Sorce - 0.4.1-0 +- latest upstream release. +- also add a patch that fixes debugging output (potential segfault) + +* Mon Apr 20 2009 Simo Sorce - 0.3.2-2 +- release out of the official 0.3.2 tarball + +* Mon Apr 20 2009 Jakub Hrozek - 0.3.2-1 +- bugfix release 0.3.2 +- includes previous release patches +- change permissions of the /etc/sssd/sssd.conf to 0600 + +* Tue Apr 14 2009 Simo Sorce - 0.3.1-2 +- Add last minute bug fixes, found in testing the package + +* Mon Apr 13 2009 Simo Sorce - 0.3.1-1 +- Version 0.3.1 +- includes previous release patches + +* Mon Apr 13 2009 Simo Sorce - 0.3.0-2 +- Try to fix build adding automake as an explicit BuildRequire +- Add also a couple of last minute patches from upstream + +* Mon Apr 13 2009 Simo Sorce - 0.3.0-1 +- Version 0.3.0 +- Provides file based configuration and lots of improvements + +* Tue Mar 10 2009 Simo Sorce - 0.2.1-1 +- Version 0.2.1 + +* Tue Mar 10 2009 Simo Sorce - 0.2.0-1 +- Version 0.2.0 + +* Sun Mar 08 2009 Jakub Hrozek - 0.1.0-5.20090309git691c9b3 +- package git snapshot + +* Fri Mar 06 2009 Jakub Hrozek - 0.1.0-4 +- fixed items found during review +- added initscript + +* Thu Mar 05 2009 Sumit Bose - 0.1.0-3 +- added sss_client + +* Mon Feb 23 2009 Jakub Hrozek - 0.1.0-2 +- Small cleanup and fixes in the spec file + +* Thu Feb 12 2009 Stephen Gallagher - 0.1.0-1 +- Initial release (based on version 0.1.0 upstream code)