From 396c651083ac132c2080892162459ed39d410971 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 22 Feb 2017 16:01:18 +0100 Subject: [PATCH] Cherry-pick patches from upstream that enable the files provider Required for: https://bugzilla.redhat.com/show_bug.cgi?id=1357418 - SSSD fast cache for local users --- ...-version-to-track-the-1.15.1-release.patch | 23 + ...-BUILD-Fix-linking-of-test_wbc_calls.patch | 40 + ...pres-implicit-fallthrough-from-gcc-7.patch | 201 ++ ...s-Suppress-warning-format-truncation.patch | 47 + ...-TOOLS-Fix-warning-format-truncation.patch | 49 + ...ix-warning-may-be-used-uninitialized.patch | 50 + 0007-SBUS-remove-unused-symbols.patch | 57 + ...BUS-use-sss_ptr_hash-for-opath-table.patch | 255 ++ ...BUS-use-sss_ptr_hash-for-nodes-table.patch | 165 ++ ...S-use-sss_ptr_hash-for-signals-table.patch | 168 ++ 0011-ldap_child-Fix-use-after-free.patch | 139 + ...VER-Improve-port-status-log-messages.patch | 41 + 0013-IFP-Update-ifp_iface_generated.c.patch | 44 + ...SYSTEMD-Update-journald-drop-in-file.patch | 30 + ...-CONFIG-Use-default-config-when-none.patch | 16 +- ...ip_entry-boolean-to-sudo-conversions.patch | 177 ++ 0017-TESTS-Add-to-IPA-DN-test.patch | 36 + 0018-LDAP-Better-logging-message.patch | 28 + ...ing-of-sysdb_try_to_find_expected_dn.patch | 592 ++++ ...T-create_multidom_test_ctx-extending.patch | 138 + ...for-sdap_search_initgr_user_in_batch.patch | 609 ++++ ...sh-fix-number-of-output-certificates.patch | 31 + 0023-ssh-do-not-create-again-fq-name.patch | 76 + ...nd-provide-default_domain-as-paramet.patch | 198 ++ ...ility-to-not-use-default-domain-suff.patch | 310 ++ ...e_req-search-user-by-name-with-attrs.patch | 87 + ...pi-to-create-ldb_result-from-message.patch | 99 + ...-cache_req-move-dp-request-to-plugin.patch | 1426 ++++++++++ 0029-cache_req-add-host-by-name-search.patch | 453 +++ ...write-ssh-responder-to-use-cache_req.patch | 2506 +++++++++++++++++ ...-to-match-forest-root-domain-not-the.patch | 76 + ...UILD-Fix-linking-of-test_sdap_initgr.patch | 44 + 0033-ssh-fix-typo.patch | 31 + ...-go-to-dp-first-when-looking-up-host.patch | 32 + ...sending-sd_notify-ready-into-a-new-f.patch | 74 + ...meout-if-using-local-provider-socket.patch | 51 + ...tore-lowercased-attribute-value-once.patch | 83 + ...dd-API-to-reset-all-users-and-groups.patch | 190 ++ ...sbus-interface-to-clear-memory-cache.patch | 193 ++ ...nterface-to-invalidate-memory-cache-.patch | 111 + ...domain-state-called-DOM_INCONSISTENT.patch | 56 + ...responder-sbus-interface-to-set-doma.patch | 561 ++++ ...-interface-to-reset-negatively-cache.patch | 259 ++ ...nal-DP-interface-to-set-domain-state.patch | 148 + ...interface-to-reset-negative-cache-fr.patch | 122 + ...interface-to-invalidate-memory-cache.patch | 77 + ...NDER-Use-the-NEED_CHECK_DOMAIN-macro.patch | 50 + ...e-the-files-provider-in-NEEDS_CHECK_.patch | 41 + ...SPONDER-Contact-inconsistent-domains.patch | 132 + 0050-UTIL-Add-a-generic-inotify-module.patch | 1312 +++++++++ ...-CONFDB-Re-enable-the-files-provider.patch | 42 + 0052-FILES-Add-the-files-provider.patch | 1329 +++++++++ ...The-files-provider-always-enumerates.patch | 48 + ...Make-pwfield-configurable-per-domain.patch | 147 + ...iles-domain-defaults-to-x-as-pwfield.patch | 38 + ...ent-the-pwfield-configuration-option.patch | 47 + ...r-fixtures-to-back-up-and-restore-a-.patch | 95 + ...per-module-with-shared-NSS-constants.patch | 186 ++ ...le-to-call-nss_sss-s-getpw-from-test.patch | 207 ++ ...le-to-call-nss_sss-s-getgr-from-test.patch | 128 + ...Add-files-provider-integration-tests.patch | 1002 +++++++ ...-Remove-checks-for-sssd.conf-changes.patch | 44 + ...common-inotify-code-to-watch-resolv..patch | 700 +++++ ...documentation-for-the-files-provider.patch | 155 + ...ES-Do-not-point-to-id_provider-local.patch | 38 + ...w-to-free-the-result-of-sbus_create_.patch | 40 + 0067-IPA_SUDO-Unused-value-fix.patch | 33 + 0068-intg-Fix-python3-issues.patch | 99 + ...ate-PTR-record-after-non-fatal-error.patch | 36 + ...S-Correct-debug-log-message-of-realm.patch | 43 + ...nd_map-make-sure-memory-can-be-freed.patch | 83 + ...te-check-name-member-before-using-it.patch | 79 + 0073-FILES-Fix-reallocation-logic.patch | 167 ++ ...-pam_sss-check-conversation-callback.patch | 47 + ...turn-an-error-in-case-we-fail-to-reg.patch | 44 + 0076-FILES-Remove-unnecessary-check.patch | 34 + ...tore-user-object-in-the-preq-context.patch | 80 + 0078-PAM-fix-memory-leak-in-pam_sss.patch | 72 + ...use-sentinel-error-code-in-PAM-tests.patch | 28 + 0080-utils-new-error-codes.patch | 44 + ...frontend-that-Smartcard-auth-is-not-.patch | 63 + ...nce-support-for-Smartcard-auth-blobs.patch | 775 +++++ ...rd-Smartcard-credentials-to-backends.patch | 265 ++ ...of-PKCS-11-module-and-key-id-to-pam_.patch | 550 ++++ ...hance-Smartcard-authentication-token.patch | 134 + ...KRB5-allow-pkinit-pre-authentication.patch | 520 ++++ 0087-TESTS-Remove-unused-import.patch | 28 + 0088-DOC-Deprecate-README-add-README.md.patch | 111 + ...n-implicit-files-domain-if-one-is-no.patch | 397 +++ ...-files-domain-for-all-integration-te.patch | 62 + ...t-the-files-domain-autoconfiguration.patch | 111 + ...FDB-Refactor-reading-the-config-file.patch | 247 ++ ...figuration-file-is-provided-create-a.patch | 151 + 0094-authtok-fix-tests-on-big-endian.patch | 45 + sssd.spec | 108 +- 95 files changed, 20427 insertions(+), 9 deletions(-) create mode 100644 0001-Updating-the-version-to-track-the-1.15.1-release.patch create mode 100644 0002-BUILD-Fix-linking-of-test_wbc_calls.patch create mode 100644 0003-Suppres-implicit-fallthrough-from-gcc-7.patch create mode 100644 0004-pam_sss-Suppress-warning-format-truncation.patch create mode 100644 0005-TOOLS-Fix-warning-format-truncation.patch create mode 100644 0006-sssctl-Fix-warning-may-be-used-uninitialized.patch create mode 100644 0007-SBUS-remove-unused-symbols.patch create mode 100644 0008-SBUS-use-sss_ptr_hash-for-opath-table.patch create mode 100644 0009-SBUS-use-sss_ptr_hash-for-nodes-table.patch create mode 100644 0010-SBUS-use-sss_ptr_hash-for-signals-table.patch create mode 100644 0011-ldap_child-Fix-use-after-free.patch create mode 100644 0012-FAILOVER-Improve-port-status-log-messages.patch create mode 100644 0013-IFP-Update-ifp_iface_generated.c.patch create mode 100644 0014-SYSTEMD-Update-journald-drop-in-file.patch rename 0501-Partially-revert-CONFIG-Use-default-config-when-none.patch => 0015-Partially-revert-CONFIG-Use-default-config-when-none.patch (89%) create mode 100644 0016-SUDO-Add-skip_entry-boolean-to-sudo-conversions.patch create mode 100644 0017-TESTS-Add-to-IPA-DN-test.patch create mode 100644 0018-LDAP-Better-logging-message.patch create mode 100644 0019-SYSDB-Removing-of-sysdb_try_to_find_expected_dn.patch create mode 100644 0020-TEST-create_multidom_test_ctx-extending.patch create mode 100644 0021-TESTS-Tests-for-sdap_search_initgr_user_in_batch.patch create mode 100644 0022-ssh-fix-number-of-output-certificates.patch create mode 100644 0023-ssh-do-not-create-again-fq-name.patch create mode 100644 0024-sss_parse_inp_send-provide-default_domain-as-paramet.patch create mode 100644 0025-cache_req-add-ability-to-not-use-default-domain-suff.patch create mode 100644 0026-cache_req-search-user-by-name-with-attrs.patch create mode 100644 0027-cache_req-add-api-to-create-ldb_result-from-message.patch create mode 100644 0028-cache_req-move-dp-request-to-plugin.patch create mode 100644 0029-cache_req-add-host-by-name-search.patch create mode 100644 0030-ssh-rewrite-ssh-responder-to-use-cache_req.patch create mode 100644 0031-AD-Use-ad_domain-to-match-forest-root-domain-not-the.patch create mode 100644 0032-BUILD-Fix-linking-of-test_sdap_initgr.patch create mode 100644 0033-ssh-fix-typo.patch create mode 100644 0034-cache_req-always-go-to-dp-first-when-looking-up-host.patch create mode 100644 0035-MONITOR-Wrap-up-sending-sd_notify-ready-into-a-new-f.patch create mode 100644 0036-MONITOR-Don-t-timeout-if-using-local-provider-socket.patch create mode 100644 0037-SUDO-Only-store-lowercased-attribute-value-once.patch create mode 100644 0038-NEGCACHE-Add-API-to-reset-all-users-and-groups.patch create mode 100644 0039-NSS-Add-sbus-interface-to-clear-memory-cache.patch create mode 100644 0040-NSS-Rename-the-interface-to-invalidate-memory-cache-.patch create mode 100644 0041-UTIL-Add-a-new-domain-state-called-DOM_INCONSISTENT.patch create mode 100644 0042-RESPONDER-Add-a-responder-sbus-interface-to-set-doma.patch create mode 100644 0043-RESPONDER-A-sbus-interface-to-reset-negatively-cache.patch create mode 100644 0044-DP-Add-internal-DP-interface-to-set-domain-state.patch create mode 100644 0045-DP-Add-internal-interface-to-reset-negative-cache-fr.patch create mode 100644 0046-DP-Add-internal-interface-to-invalidate-memory-cache.patch create mode 100644 0047-RESPONDER-Use-the-NEED_CHECK_DOMAIN-macro.patch create mode 100644 0048-RESPONDER-Include-the-files-provider-in-NEEDS_CHECK_.patch create mode 100644 0049-RESPONDER-Contact-inconsistent-domains.patch create mode 100644 0050-UTIL-Add-a-generic-inotify-module.patch create mode 100644 0051-CONFDB-Re-enable-the-files-provider.patch create mode 100644 0052-FILES-Add-the-files-provider.patch create mode 100644 0053-CONFDB-The-files-provider-always-enumerates.patch create mode 100644 0054-CONFDB-Make-pwfield-configurable-per-domain.patch create mode 100644 0055-CONFDB-The-files-domain-defaults-to-x-as-pwfield.patch create mode 100644 0056-MAN-Document-the-pwfield-configuration-option.patch create mode 100644 0057-TESTS-move-helper-fixtures-to-back-up-and-restore-a-.patch create mode 100644 0058-TESTS-add-a-helper-module-with-shared-NSS-constants.patch create mode 100644 0059-TESTS-Add-a-module-to-call-nss_sss-s-getpw-from-test.patch create mode 100644 0060-TESTS-Add-a-module-to-call-nss_sss-s-getgr-from-test.patch create mode 100644 0061-TESTS-Add-files-provider-integration-tests.patch create mode 100644 0062-MONITOR-Remove-checks-for-sssd.conf-changes.patch create mode 100644 0063-MONITOR-Use-the-common-inotify-code-to-watch-resolv..patch create mode 100644 0064-MAN-Add-documentation-for-the-files-provider.patch create mode 100644 0065-EXAMPLES-Do-not-point-to-id_provider-local.patch create mode 100644 0066-SBUS-Document-how-to-free-the-result-of-sbus_create_.patch create mode 100644 0067-IPA_SUDO-Unused-value-fix.patch create mode 100644 0068-intg-Fix-python3-issues.patch create mode 100644 0069-DYNDNS-Update-PTR-record-after-non-fatal-error.patch create mode 100644 0070-DYNDNS-Correct-debug-log-message-of-realm.patch create mode 100644 0071-sdap_extend_map-make-sure-memory-can-be-freed.patch create mode 100644 0072-check_duplicate-check-name-member-before-using-it.patch create mode 100644 0073-FILES-Fix-reallocation-logic.patch create mode 100644 0074-pam_sss-check-conversation-callback.patch create mode 100644 0075-MONITOR-Don-t-return-an-error-in-case-we-fail-to-reg.patch create mode 100644 0076-FILES-Remove-unnecessary-check.patch create mode 100644 0077-PAM-store-user-object-in-the-preq-context.patch create mode 100644 0078-PAM-fix-memory-leak-in-pam_sss.patch create mode 100644 0079-PAM-use-sentinel-error-code-in-PAM-tests.patch create mode 100644 0080-utils-new-error-codes.patch create mode 100644 0081-LDAP-proxy-tell-frontend-that-Smartcard-auth-is-not-.patch create mode 100644 0082-authtok-enhance-support-for-Smartcard-auth-blobs.patch create mode 100644 0083-PAM-forward-Smartcard-credentials-to-backends.patch create mode 100644 0084-p11-return-name-of-PKCS-11-module-and-key-id-to-pam_.patch create mode 100644 0085-pam-enhance-Smartcard-authentication-token.patch create mode 100644 0086-KRB5-allow-pkinit-pre-authentication.patch create mode 100644 0087-TESTS-Remove-unused-import.patch create mode 100644 0088-DOC-Deprecate-README-add-README.md.patch create mode 100644 0089-MONITOR-Enable-an-implicit-files-domain-if-one-is-no.patch create mode 100644 0090-TESTS-Enable-the-files-domain-for-all-integration-te.patch create mode 100644 0091-TESTS-Test-the-files-domain-autoconfiguration.patch create mode 100644 0092-CONFDB-Refactor-reading-the-config-file.patch create mode 100644 0093-CONFDB-If-no-configuration-file-is-provided-create-a.patch create mode 100644 0094-authtok-fix-tests-on-big-endian.patch diff --git a/0001-Updating-the-version-to-track-the-1.15.1-release.patch b/0001-Updating-the-version-to-track-the-1.15.1-release.patch new file mode 100644 index 0000000..3dbf43f --- /dev/null +++ b/0001-Updating-the-version-to-track-the-1.15.1-release.patch @@ -0,0 +1,23 @@ +From 33da7b13eaed678789b7ccba00e49065a8838e9a Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 25 Jan 2017 16:46:31 +0100 +Subject: [PATCH 01/79] Updating the version to track the 1.15.1 release + +--- + version.m4 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/version.m4 b/version.m4 +index 5ff77ba10a8e8a512057e4176377ba33713eb285..bec03afc6e4357e8f505978b0474888c2ab16a85 100644 +--- a/version.m4 ++++ b/version.m4 +@@ -1,5 +1,5 @@ + # Primary version number +-m4_define([VERSION_NUMBER], [1.15.0]) ++m4_define([VERSION_NUMBER], [1.15.1]) + + # If the PRERELEASE_VERSION_NUMBER is set, we'll append + # it to the release tag when creating an RPM or SRPM +-- +2.9.3 + diff --git a/0002-BUILD-Fix-linking-of-test_wbc_calls.patch b/0002-BUILD-Fix-linking-of-test_wbc_calls.patch new file mode 100644 index 0000000..c85141b --- /dev/null +++ b/0002-BUILD-Fix-linking-of-test_wbc_calls.patch @@ -0,0 +1,40 @@ +From c369b062182c746849196e495db467198039edf4 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Wed, 25 Jan 2017 16:12:02 +0100 +Subject: [PATCH 02/79] BUILD: Fix linking of test_wbc_calls +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Client code does not anymore depend on libpthread in master. +This is a reason why we didn't notice any linking failure +in master. But the test should be linked with CLIENT_LIBS. + + CCLD test_wbc_calls +/usr/bin/ld: src/sss_client/test_wbc_calls-common.o: undefined reference + to symbol 'pthread_mutexattr_setrobust@@GLIBC_2.12' +//lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing + from command line +collect2: error: ld returned 1 exit status +Makefile:12460: recipe for target 'test_wbc_calls' failed + +Reviewed-by: Fabiano Fidêncio +--- + Makefile.am | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Makefile.am b/Makefile.am +index 661e9447d56146cb756a23af3a1b0aa0fbf98fa4..674d328f52929cc2b20d1212af830c3777312bf1 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -2703,6 +2703,7 @@ test_wbc_calls_LDFLAGS = \ + -Wl,-wrap,sss_nss_getnamebysid \ + $(NULL) + test_wbc_calls_LDADD = \ ++ $(CLIENT_LIBS) \ + $(CMOCKA_LIBS) \ + $(POPT_LIBS) \ + $(TALLOC_LIBS) \ +-- +2.9.3 + diff --git a/0003-Suppres-implicit-fallthrough-from-gcc-7.patch b/0003-Suppres-implicit-fallthrough-from-gcc-7.patch new file mode 100644 index 0000000..b8deaa5 --- /dev/null +++ b/0003-Suppres-implicit-fallthrough-from-gcc-7.patch @@ -0,0 +1,201 @@ +From 2e505786d6d9d537f5b6631099862f6b93e2e687 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 30 Jan 2017 12:17:25 +0100 +Subject: [PATCH 03/79] Suppres implicit-fallthrough from gcc 7 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some kind of comments are recognized by gcc7 but they are ignored with +-Wimplicit-fallthrough=5 and only attributes disable the warning. + +Reviewed-by: Fabiano Fidêncio +--- + configure.ac | 24 ++++++++++++++++++++++++ + src/db/sysdb_ops.c | 1 + + src/providers/ad/ad_id.c | 1 + + src/providers/fail_over.c | 4 ++++ + src/providers/krb5/krb5_auth.c | 1 + + src/providers/ldap/sdap_idmap.c | 1 + + src/providers/proxy/proxy_id.c | 1 + + src/python/pyhbac.c | 1 + + src/responder/common/responder_dp.c | 1 + + src/util/murmurhash3.c | 3 +++ + 10 files changed, 38 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 291504652bf02e38c7edfd0cc4eefbe4ceaf09e6..d264abf3ebebbc1f3a96d1a450993e0933a5d789 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -418,6 +418,30 @@ if test x"$sss_cv_attribute_warn_unused_result" = xyes ; then + [whether compiler supports __attribute__((warn_unused_result))]) + fi + ++SAFE_CFLAGS=$CFLAGS ++CFLAGS="-Werror" ++AC_CACHE_CHECK( ++ [whether compiler supports __attribute__((fallthrough))], ++ [sss_cv_attribute_fallthrough], ++ [AC_COMPILE_IFELSE( ++ [AC_LANG_SOURCE( ++ [ __attribute__ ((fallthrough)); ]) ++ ],[ ++ sss_cv_attribute_fallthrough=yes ++ sss_cv_attribute_fallthrough_val="__attribute__ ((fallthrough))" ++ ],[ ++ sss_cv_attribute_fallthrough=no ++ sss_cv_attribute_fallthrough_val= ++ ]) ++ ]) ++CFLAGS=$SAFE_CFLAGS ++ ++AC_DEFINE_UNQUOTED( ++ [SSS_ATTRIBUTE_FALLTHROUGH], ++ [$sss_cv_attribute_fallthrough_val], ++ [__attribute__((fallthrough)) if supported]) ++ ++ + PKG_CHECK_MODULES([CHECK], [check >= 0.9.5], [have_check=1], [have_check=]) + if test x$have_check = x; then + AC_MSG_WARN([Without the 'CHECK' libraries, you will be unable to run all tests in the 'make check' suite]) +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 77e4c1a699eded07d2b266b08d2f4c177e6181a6..7f6c127d4fa3ef7655d5eb931210d0248352e159 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -116,6 +116,7 @@ static int sysdb_delete_cache_entry(struct ldb_context *ldb, + return EOK; + } + /* fall through */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "LDB Error: %s(%d)\nError Message: [%s]\n", + ldb_strerror(ret), ret, ldb_errstring(ldb)); +diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c +index 46a41a37b96bb7734f61226e72e75b56f9deccf1..8f26cb8744d2372c6180342c0d1bca025b16f52c 100644 +--- a/src/providers/ad/ad_id.c ++++ b/src/providers/ad/ad_id.c +@@ -337,6 +337,7 @@ static bool ad_account_can_shortcut(struct be_ctx *be_ctx, + goto done; + } + /* fall through */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + case BE_FILTER_SECID: + csid = sid == NULL ? filter_value : sid; + +diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c +index 77084098831a312bc8629513ccfc2a91165241ba..5d3c26d4a690769637f2fa4f41a76627cbdba77a 100644 +--- a/src/providers/fail_over.c ++++ b/src/providers/fail_over.c +@@ -1145,6 +1145,7 @@ fo_resolve_service_server(struct tevent_req *req) + state->server->common); + fo_set_server_status(state->server, SERVER_RESOLVING_NAME); + /* FALLTHROUGH */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + case SERVER_RESOLVING_NAME: + /* Name resolution is already under way. Just add ourselves into the + * waiting queue so we get notified after the operation is finished. */ +@@ -1284,6 +1285,7 @@ resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + * "server" might be invalid now if the SRV + * query collapsed + * */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + case SRV_NEUTRAL: /* Request SRV lookup */ + if (server != NULL && server != state->meta) { + /* A server created by expansion of meta server was marked as +@@ -1443,9 +1445,11 @@ resolve_srv_done(struct tevent_req *subreq) + break; + case ERR_SRV_NOT_FOUND: + /* fall through */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + case ERR_SRV_LOOKUP_ERROR: + fo_set_port_status(state->meta, PORT_NOT_WORKING); + /* fall through */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + default: + DEBUG(SSSDBG_OP_FAILURE, "Unable to resolve SRV [%d]: %s\n", + ret, sss_strerror(ret)); +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index bdd8e24111b077bfb91f19987d2ed289d803b334..0e685618ec2de1f923ffd9d78bf2a9d8816019e1 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -965,6 +965,7 @@ static void krb5_auth_done(struct tevent_req *subreq) + DEBUG(SSSDBG_CRIT_FAILURE, "krb5_delete_ccname failed.\n"); + } + /* FALLTHROUGH */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + + case ERR_CREDS_EXPIRED: + /* If the password is expired we can safely remove the ccache from the +diff --git a/src/providers/ldap/sdap_idmap.c b/src/providers/ldap/sdap_idmap.c +index b5dfc6cefe3ceed4971042a5326dd2b9c7f5eec8..0fda815224b5ce278e6fae4a5264f82cd1ea4a9d 100644 +--- a/src/providers/ldap/sdap_idmap.c ++++ b/src/providers/ldap/sdap_idmap.c +@@ -516,6 +516,7 @@ sdap_idmap_sid_to_unix(struct sdap_idmap_ctx *idmap_ctx, + "sssd-ad(5) for an explanation of how to resolve this issue.\n", + sid_str); + /* Fall through intentionally */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + default: + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not convert objectSID [%s] to a UNIX ID\n", +diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c +index 3d272897bda5622fa1e56e6b84448df7c3cefa2b..9b83f7a3cc942560186815b680e8b5f98508f18a 100644 +--- a/src/providers/proxy/proxy_id.c ++++ b/src/providers/proxy/proxy_id.c +@@ -1403,6 +1403,7 @@ static int get_initgr_groups_process(TALLOC_CTX *memctx, + "Assume the user is only member of its " + "primary group (%"SPRIgid")\n", pwd->pw_gid); + /* fall through */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + case NSS_STATUS_SUCCESS: + DEBUG(SSSDBG_CONF_SETTINGS, "User [%s] appears to be member of %lu " + "groups\n", pwd->pw_name, num_gids); +diff --git a/src/python/pyhbac.c b/src/python/pyhbac.c +index 09d308a0f3c932c4077dfdc92b3a46fe3238b69b..f7633ee02c5f113fad64c5ee41736d8f63a1914a 100644 +--- a/src/python/pyhbac.c ++++ b/src/python/pyhbac.c +@@ -1621,6 +1621,7 @@ py_hbac_evaluate(HbacRequest *self, PyObject *args) + goto fail; + } + /* FALLTHROUGH */ ++ SSS_ATTRIBUTE_FALLTHROUGH; + case HBAC_EVAL_DENY: + ret = PYNUMBER_FROMLONG(eres); + break; +diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c +index da67676675284db14fe7f6fcf8cb47e9f2baa7f9..11eb47ce1d41027f36998aba7b9fbca5fb4c7910 100644 +--- a/src/responder/common/responder_dp.c ++++ b/src/responder/common/responder_dp.c +@@ -221,6 +221,7 @@ static int sss_dp_get_reply(DBusPendingCall *pending, + 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: + /* + * Timeout or other error occurred or something +diff --git a/src/util/murmurhash3.c b/src/util/murmurhash3.c +index 03d10ff6ae360350dcc96e3e40ece0a0ce3d6b58..061e64e990aa4d91d4a300e116d2fb1193e33392 100644 +--- a/src/util/murmurhash3.c ++++ b/src/util/murmurhash3.c +@@ -90,14 +90,17 @@ uint32_t murmurhash3(const char *key, int len, uint32_t seed) + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; ++ SSS_ATTRIBUTE_FALLTHROUGH; + case 2: + k1 ^= tail[1] << 8; ++ SSS_ATTRIBUTE_FALLTHROUGH; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = rotl(k1, 15); + k1 *= c2; + h1 ^= k1; ++ break; + default: + break; + } +-- +2.9.3 + diff --git a/0004-pam_sss-Suppress-warning-format-truncation.patch b/0004-pam_sss-Suppress-warning-format-truncation.patch new file mode 100644 index 0000000..8bd71c4 --- /dev/null +++ b/0004-pam_sss-Suppress-warning-format-truncation.patch @@ -0,0 +1,47 @@ +From cbb0e683ff11d7800328da3991f3e75ef88f937f Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 30 Jan 2017 12:49:13 +0100 +Subject: [PATCH 04/79] pam_sss: Suppress warning format-truncation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +src/sss_client/pam_sss.c: In function ‘send_and_receive’: +src/sss_client/pam_sss.c:742:39: error: ‘%.*s’ directive output + between 0 and 18446744073709551615 bytes may cause result to exceed + ‘INT_MAX’ [-Werror=format-truncation=] + ret = snprintf(user_msg, bufsize, "%s%s%.*s", + ^~~~~~~~~~ +sssd/src/sss_client/pam_sss.c:742:39: note: assuming directive output + of 4294967295 bytes + +Reviewed-by: Fabiano Fidêncio +--- + 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 be697c7fcfb47a57b5b498c61f60fcf4bfbbd57f..b4175ae2c7fc1385a19f81045695bcd73d43f754 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -689,7 +689,7 @@ static int user_info_account_expired(pam_handle_t *pamh, size_t buflen, + ret = snprintf(user_msg, bufsize, "%s%s%.*s", + EXP_ACC_MSG, + msg_len > 0 ? SRV_MSG : "", +- msg_len, ++ (int)msg_len, + msg_len > 0 ? (char *)(buf + 2 * sizeof(uint32_t)) : "" ); + if (ret < 0 || ret > bufsize) { + D(("snprintf failed.")); +@@ -744,7 +744,7 @@ static int user_info_chpass_error(pam_handle_t *pamh, size_t buflen, + ret = snprintf(user_msg, bufsize, "%s%s%.*s", + _("Password change failed. "), + msg_len > 0 ? _("Server message: ") : "", +- msg_len, ++ (int)msg_len, + msg_len > 0 ? (char *)(buf + 2 * sizeof(uint32_t)) : "" ); + if (ret < 0 || ret > bufsize) { + D(("snprintf failed.")); +-- +2.9.3 + diff --git a/0005-TOOLS-Fix-warning-format-truncation.patch b/0005-TOOLS-Fix-warning-format-truncation.patch new file mode 100644 index 0000000..2d688d7 --- /dev/null +++ b/0005-TOOLS-Fix-warning-format-truncation.patch @@ -0,0 +1,49 @@ +From c587e9ae55c618c011bd4dde6a94fe5dc60fff01 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 30 Jan 2017 12:55:59 +0100 +Subject: [PATCH 05/79] TOOLS: Fix warning format-truncation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +src/tools/sss_groupshow.c: In function ‘print_group_info’: +src/tools/sss_groupshow.c:612:22: error: ‘%d’ directive output truncated + writing between 10 and 11 bytes into a region of size 7 [-Werror=format-truncation=] + snprintf(fmt, 8, "%%%ds", level*PADDING_SPACES); + ^~~~~~~ +src/tools/sss_groupshow.c:612:22: note: using the range + [-2147483648, 2147483647] for directive argument +src/tools/sss_groupshow.c:612:5: note: ‘snprintf’ output between 13 and 14 + bytes into a destination of size 8 + snprintf(fmt, 8, "%%%ds", level*PADDING_SPACES); + +Reviewed-by: Fabiano Fidêncio +--- + src/tools/sss_groupshow.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/tools/sss_groupshow.c b/src/tools/sss_groupshow.c +index 258d458b0d1a4cb56c8fb61060cb43a1c88c1ed0..ac4c3dc912db3d418c2eace8b5b1f3476768c875 100644 +--- a/src/tools/sss_groupshow.c ++++ b/src/tools/sss_groupshow.c +@@ -603,7 +603,7 @@ fail: + + /*==================The main program=================================== */ + +-static void print_group_info(struct group_info *g, int level) ++static void print_group_info(struct group_info *g, unsigned level) + { + int i; + char padding[512]; +@@ -634,7 +634,7 @@ static void print_group_info(struct group_info *g, int level) + printf(_("\n%1$sMember groups: "), padding); + } + +-static void print_recursive(struct group_info **group_members, int level) ++static void print_recursive(struct group_info **group_members, unsigned level) + { + int i; + +-- +2.9.3 + diff --git a/0006-sssctl-Fix-warning-may-be-used-uninitialized.patch b/0006-sssctl-Fix-warning-may-be-used-uninitialized.patch new file mode 100644 index 0000000..ecfe243 --- /dev/null +++ b/0006-sssctl-Fix-warning-may-be-used-uninitialized.patch @@ -0,0 +1,50 @@ +From bf0b4eb335ec1fb4fdd925f5cf80490ec8b8c24e Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Mon, 30 Jan 2017 14:36:56 +0100 +Subject: [PATCH 06/79] sssctl: Fix warning may be used uninitialized +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +gcc 7 probably does some new optimisations which might cause few +wariables to be uninitialized. + +src/tools/sssctl/sssctl_cache.c: In function ‘sssctl_print_object’: +src/tools/sssctl/sssctl_cache.c:523:13: error: ‘dom’ may be used uninitialized + in this function [-Werror=maybe-uninitialized] + ret = info[i].attr_fn(tmp_ctx, entry, dom, info[i].attr, &value); + ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +src/tools/sssctl/sssctl_cache.c:472:15: error: ‘entry’ may be used + uninitialized in this function [-Werror=maybe-uninitialized] + *_entry = talloc_steal(mem_ctx, entry); + ^~~~~~~~~~~~ +src/tools/sssctl/sssctl_cache.c:437:25: note: ‘entry’ was declared here + struct sysdb_attrs *entry; + ^~~~~ + +Another workaround would be to remove static modifier from function +sssctl_find_object which probably prevents some inlinig + optimisation. + +Reviewed-by: Fabiano Fidêncio +--- + src/tools/sssctl/sssctl_cache.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c +index 59c8cb473966d60848908fb8b9adcb7d769c8cd9..8f0fc281b73f38f408c1a2307192b3f207a97b5d 100644 +--- a/src/tools/sssctl/sssctl_cache.c ++++ b/src/tools/sssctl/sssctl_cache.c +@@ -434,8 +434,8 @@ static errno_t sssctl_fetch_object(TALLOC_CTX *mem_ctx, + struct sss_domain_info **_dom) + { + TALLOC_CTX *tmp_ctx; +- struct sysdb_attrs *entry; +- struct sss_domain_info *dom; ++ struct sysdb_attrs *entry = NULL; ++ struct sss_domain_info *dom = NULL; + const char **attrs; + char *sanitized; + errno_t ret; +-- +2.9.3 + diff --git a/0007-SBUS-remove-unused-symbols.patch b/0007-SBUS-remove-unused-symbols.patch new file mode 100644 index 0000000..7f86d2a --- /dev/null +++ b/0007-SBUS-remove-unused-symbols.patch @@ -0,0 +1,57 @@ +From bc898b360b9667195a7ae59537587c3ec696ac19 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 24 Jan 2017 12:36:04 +0100 +Subject: [PATCH 07/79] SBUS: remove unused symbols +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +--- + src/sbus/sssd_dbus.h | 2 -- + src/sbus/sssd_dbus_connection.c | 9 --------- + 2 files changed, 11 deletions(-) + +diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h +index 5a66f09d550533839b22465170950fdfdd71aa1e..c6cca7d4edf5014576f41ed146919427f8e3255f 100644 +--- a/src/sbus/sssd_dbus.h ++++ b/src/sbus/sssd_dbus.h +@@ -247,8 +247,6 @@ sbus_opath_get_object_name(TALLOC_CTX *mem_ctx, + const char *object_path, + const char *base_path); + +-bool sbus_conn_disconnecting(struct sbus_connection *conn); +- + /* max_retries < 0: retry forever + * max_retries = 0: never retry (why are you calling this function?) + * max_retries > 0: obvious +diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c +index 450cee9045de88fcf84b3ca379dd9f1bd5c98ef2..9def7084e5d496a5e6aa40ec0eafd2471a64659f 100644 +--- a/src/sbus/sssd_dbus_connection.c ++++ b/src/sbus/sssd_dbus_connection.c +@@ -27,9 +27,6 @@ + #include "sbus/sssd_dbus_private.h" + #include "sbus/sssd_dbus_meta.h" + +-/* Types */ +-struct dbus_ctx_list; +- + static int sbus_auto_reconnect(struct sbus_connection *conn); + + static void sbus_dispatch(struct tevent_context *ev, +@@ -501,12 +498,6 @@ void sbus_reconnect_init(struct sbus_connection *conn, + conn->reconnect_pvt = pvt; + } + +-bool sbus_conn_disconnecting(struct sbus_connection *conn) +-{ +- if (conn->disconnect == 1) return true; +- return false; +-} +- + int sss_dbus_conn_send(DBusConnection *dbus_conn, + DBusMessage *msg, + int timeout_ms, +-- +2.9.3 + diff --git a/0008-SBUS-use-sss_ptr_hash-for-opath-table.patch b/0008-SBUS-use-sss_ptr_hash-for-opath-table.patch new file mode 100644 index 0000000..4e2c3b5 --- /dev/null +++ b/0008-SBUS-use-sss_ptr_hash-for-opath-table.patch @@ -0,0 +1,255 @@ +From a3b2bc38263191f23eba2ad98470d8ecd016a60b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 24 Jan 2017 13:14:47 +0100 +Subject: [PATCH 08/79] SBUS: use sss_ptr_hash for opath table +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch reuses sss_ptr_hash module introduced in NSS patches in sbus code. + +Reviewed-by: Fabiano Fidêncio +--- + src/sbus/sssd_dbus_connection.c | 4 +- + src/sbus/sssd_dbus_interface.c | 94 +++++++++++------------------------------ + src/sbus/sssd_dbus_private.h | 5 +-- + 3 files changed, 28 insertions(+), 75 deletions(-) + +diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c +index 9def7084e5d496a5e6aa40ec0eafd2471a64659f..6ca039e8e2a919141bf951ed0203dc2c48b3eb55 100644 +--- a/src/sbus/sssd_dbus_connection.c ++++ b/src/sbus/sssd_dbus_connection.c +@@ -163,8 +163,8 @@ int sbus_init_connection(TALLOC_CTX *ctx, + conn->last_request_time = last_request_time; + conn->client_destructor_data = client_destructor_data; + +- ret = sbus_opath_hash_init(conn, conn, &conn->managed_paths); +- if (ret != EOK) { ++ conn->managed_paths = sbus_opath_hash_init(conn, conn); ++ if (conn->managed_paths == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create object paths hash table\n"); + talloc_free(conn); + return EIO; +diff --git a/src/sbus/sssd_dbus_interface.c b/src/sbus/sssd_dbus_interface.c +index 32e5b27e1f701898d96f5537b2bc72d491903b54..e8c8851231fab68024065a13c5f1e2642ba829e9 100644 +--- a/src/sbus/sssd_dbus_interface.c ++++ b/src/sbus/sssd_dbus_interface.c +@@ -23,6 +23,7 @@ + #include + + #include "util/util.h" ++#include "util/sss_ptr_hash.h" + #include "sbus/sssd_dbus.h" + #include "sbus/sssd_dbus_meta.h" + #include "sbus/sssd_dbus_private.h" +@@ -492,13 +493,11 @@ sbus_opath_hash_delete_cb(hash_entry_t *item, + dbus_connection_unregister_object_path(conn->dbus.conn, path); + } + +-errno_t ++hash_table_t * + sbus_opath_hash_init(TALLOC_CTX *mem_ctx, +- struct sbus_connection *conn, +- hash_table_t **_table) ++ struct sbus_connection *conn) + { +- return sss_hash_create_ex(mem_ctx, 10, _table, 0, 0, 0, 0, +- sbus_opath_hash_delete_cb, conn); ++ return sss_ptr_hash_create(mem_ctx, sbus_opath_hash_delete_cb, conn); + } + + static errno_t +@@ -511,11 +510,8 @@ sbus_opath_hash_add_iface(hash_table_t *table, + struct sbus_interface_list *list = NULL; + struct sbus_interface_list *item = NULL; + const char *iface_name = iface->vtable->meta->name; +- hash_key_t key; +- hash_value_t value; + bool path_known; + errno_t ret; +- int hret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +@@ -536,22 +532,14 @@ sbus_opath_hash_add_iface(hash_table_t *table, + + /* first lookup existing list in hash table */ + +- key.type = HASH_KEY_STRING; +- key.str = talloc_strdup(tmp_ctx, object_path); +- if (key.str == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- hret = hash_lookup(table, &key, &value); +- if (hret == HASH_SUCCESS) { ++ list = sss_ptr_hash_lookup(table, object_path, struct sbus_interface_list); ++ if (list != NULL) { + /* This object path has already some interface registered. We will + * check for existence of the interface currently being added and + * add it if missing. */ + + path_known = true; + +- list = talloc_get_type(value.ptr, struct sbus_interface_list); + if (sbus_iface_list_lookup(list, iface_name) != NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "Trying to register the same interface" + " twice: iface=%s, opath=%s\n", iface_name, object_path); +@@ -562,9 +550,6 @@ sbus_opath_hash_add_iface(hash_table_t *table, + DLIST_ADD_END(list, item, struct sbus_interface_list *); + ret = EOK; + goto done; +- } else if (hret != HASH_ERROR_KEY_NOT_FOUND) { +- ret = EIO; +- goto done; + } + + /* otherwise create new hash entry and new list */ +@@ -572,17 +557,8 @@ sbus_opath_hash_add_iface(hash_table_t *table, + path_known = false; + list = item; + +- value.type = HASH_VALUE_PTR; +- value.ptr = list; +- +- hret = hash_enter(table, &key, &value); +- if (hret != HASH_SUCCESS) { +- ret = EIO; +- goto done; +- } +- +- talloc_steal(table, key.str); +- ret = EOK; ++ ret = sss_ptr_hash_add(table, object_path, list, ++ struct sbus_interface_list); + + done: + if (ret == EOK) { +@@ -599,12 +575,7 @@ static bool + sbus_opath_hash_has_path(hash_table_t *table, + const char *object_path) + { +- hash_key_t key; +- +- key.type = HASH_KEY_STRING; +- key.str = discard_const(object_path); +- +- return hash_has_key(table, &key); ++ return sss_ptr_hash_has_key(table, object_path); + } + + /** +@@ -621,9 +592,6 @@ sbus_opath_hash_lookup_iface(hash_table_t *table, + struct sbus_interface_list *list = NULL; + struct sbus_interface *iface = NULL; + char *lookup_path = NULL; +- hash_key_t key; +- hash_value_t value; +- int hret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +@@ -636,21 +604,13 @@ sbus_opath_hash_lookup_iface(hash_table_t *table, + } + + while (lookup_path != NULL) { +- key.type = HASH_KEY_STRING; +- key.str = lookup_path; +- +- hret = hash_lookup(table, &key, &value); +- if (hret == HASH_SUCCESS) { +- list = talloc_get_type(value.ptr, struct sbus_interface_list); ++ list = sss_ptr_hash_lookup(table, lookup_path, ++ struct sbus_interface_list); ++ if (list != NULL) { + iface = sbus_iface_list_lookup(list, iface_name); + if (iface != NULL) { + goto done; + } +- } else if (hret != HASH_ERROR_KEY_NOT_FOUND) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to search hash table: hret=%d\n", hret); +- iface = NULL; +- goto done; + } + + /* we will not free lookup path since it is freed with tmp_ctx +@@ -674,13 +634,11 @@ sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx, + { + TALLOC_CTX *tmp_ctx = NULL; + TALLOC_CTX *list_ctx = NULL; +- struct sbus_interface_list *copy = NULL; +- struct sbus_interface_list *list = NULL; ++ struct sbus_interface_list *copy; ++ struct sbus_interface_list *output_list; ++ struct sbus_interface_list *table_list; + char *lookup_path = NULL; +- hash_key_t key; +- hash_value_t value; + errno_t ret; +- int hret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { +@@ -699,23 +657,19 @@ sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx, + goto done; + } + +- while (lookup_path != NULL) { +- key.type = HASH_KEY_STRING; +- key.str = lookup_path; ++ /* Initialize output_list. */ ++ output_list = NULL; + +- hret = hash_lookup(table, &key, &value); +- if (hret == HASH_SUCCESS) { +- ret = sbus_iface_list_copy(list_ctx, value.ptr, ©); ++ while (lookup_path != NULL) { ++ table_list = sss_ptr_hash_lookup(table, lookup_path, ++ struct sbus_interface_list); ++ if (table_list != NULL) { ++ ret = sbus_iface_list_copy(list_ctx, table_list, ©); + if (ret != EOK) { + goto done; + } + +- DLIST_CONCATENATE(list, copy, struct sbus_interface_list *); +- } else if (hret != HASH_ERROR_KEY_NOT_FOUND) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to search hash table: hret=%d\n", hret); +- ret = EIO; +- goto done; ++ DLIST_CONCATENATE(output_list, copy, struct sbus_interface_list *); + } + + /* we will not free lookup path since it is freed with tmp_ctx +@@ -724,7 +678,7 @@ sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx, + } + + talloc_steal(mem_ctx, list_ctx); +- *_list = list; ++ *_list = output_list; + ret = EOK; + + done: +diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h +index 8abca66b087d9ce1081889feda2ca1e1372514ad..c8913d0f0c522147aacf3214000ef9d4855fdb0c 100644 +--- a/src/sbus/sssd_dbus_private.h ++++ b/src/sbus/sssd_dbus_private.h +@@ -121,10 +121,9 @@ struct sbus_interface_list { + struct sbus_interface *interface; + }; + +-errno_t ++hash_table_t * + sbus_opath_hash_init(TALLOC_CTX *mem_ctx, +- struct sbus_connection *conn, +- hash_table_t **_table); ++ struct sbus_connection *conn); + + struct sbus_interface * + sbus_opath_hash_lookup_iface(hash_table_t *table, +-- +2.9.3 + diff --git a/0009-SBUS-use-sss_ptr_hash-for-nodes-table.patch b/0009-SBUS-use-sss_ptr_hash-for-nodes-table.patch new file mode 100644 index 0000000..bef5974 --- /dev/null +++ b/0009-SBUS-use-sss_ptr_hash-for-nodes-table.patch @@ -0,0 +1,165 @@ +From ea872f140a04419fba3f2b9722da74d7fd1ca1ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 24 Jan 2017 13:47:42 +0100 +Subject: [PATCH 09/79] SBUS: use sss_ptr_hash for nodes table +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch reuses sss_ptr_hash module introduced in NSS patches in sbus code. + +Reviewed-by: Fabiano Fidêncio +--- + src/sbus/sssd_dbus_connection.c | 4 +-- + src/sbus/sssd_dbus_interface.c | 72 +++++++---------------------------------- + src/sbus/sssd_dbus_private.h | 6 ++-- + 3 files changed, 16 insertions(+), 66 deletions(-) + +diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c +index 6ca039e8e2a919141bf951ed0203dc2c48b3eb55..5e493fb03e835d5f939a599efdc07f7ab2f9be28 100644 +--- a/src/sbus/sssd_dbus_connection.c ++++ b/src/sbus/sssd_dbus_connection.c +@@ -170,8 +170,8 @@ int sbus_init_connection(TALLOC_CTX *ctx, + return EIO; + } + +- ret = sbus_nodes_hash_init(conn, conn, &conn->nodes_fns); +- if (ret != EOK) { ++ conn->nodes_fns = sbus_nodes_hash_init(conn); ++ if (conn->nodes_fns == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create node functions hash table\n"); + talloc_free(conn); + return EIO; +diff --git a/src/sbus/sssd_dbus_interface.c b/src/sbus/sssd_dbus_interface.c +index e8c8851231fab68024065a13c5f1e2642ba829e9..1a11c6abcf23053e3b8c77f4d469d7c202a88eb8 100644 +--- a/src/sbus/sssd_dbus_interface.c ++++ b/src/sbus/sssd_dbus_interface.c +@@ -686,13 +686,10 @@ done: + return ret; + } + +-errno_t +-sbus_nodes_hash_init(TALLOC_CTX *mem_ctx, +- struct sbus_connection *conn, +- hash_table_t **_table) ++hash_table_t * ++sbus_nodes_hash_init(TALLOC_CTX *mem_ctx) + { +- return sss_hash_create_ex(mem_ctx, 10, _table, 0, 0, 0, 0, +- NULL, conn); ++ return sss_ptr_hash_create(mem_ctx, NULL, NULL); + } + + struct sbus_nodes_data { +@@ -706,57 +703,24 @@ sbus_nodes_hash_add(hash_table_t *table, + sbus_nodes_fn nodes_fn, + void *handler_data) + { +- TALLOC_CTX *tmp_ctx; + struct sbus_nodes_data *data; +- hash_key_t key; +- hash_value_t value; + errno_t ret; +- bool has_key; +- int hret; + +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- return ENOMEM; +- } +- +- key.type = HASH_KEY_STRING; +- key.str = talloc_strdup(tmp_ctx, object_path); +- if (key.str == NULL) { +- return ENOMEM; +- } +- +- has_key = hash_has_key(table, &key); +- if (has_key) { +- ret = EEXIST; +- goto done; +- } +- +- data = talloc_zero(tmp_ctx, struct sbus_nodes_data); ++ data = talloc_zero(table, struct sbus_nodes_data); + if (data == NULL) { +- ret = ENOMEM; +- goto done; ++ return ENOMEM; + } + + data->handler_data = handler_data; + data->nodes_fn = nodes_fn; + +- value.type = HASH_VALUE_PTR; +- value.ptr = data; +- +- hret = hash_enter(table, &key, &value); +- if (hret != HASH_SUCCESS) { +- ret = EIO; +- goto done; ++ ret = sss_ptr_hash_add(table, object_path, data, struct sbus_nodes_data); ++ if (ret != EOK) { ++ talloc_free(data); ++ return ret; + } + +- talloc_steal(table, key.str); +- talloc_steal(table, data); +- +- ret = EOK; +- +-done: +- talloc_free(tmp_ctx); +- return ret; ++ return EOK; + } + + const char ** +@@ -765,24 +729,12 @@ sbus_nodes_hash_lookup(TALLOC_CTX *mem_ctx, + const char *object_path) + { + struct sbus_nodes_data *data; +- hash_key_t key; +- hash_value_t value; +- int hret; + +- key.type = HASH_KEY_STRING; +- key.str = discard_const(object_path); +- +- hret = hash_lookup(table, &key, &value); +- if (hret == HASH_ERROR_KEY_NOT_FOUND) { +- return NULL; +- } else if (hret != HASH_SUCCESS) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to search hash table: hret=%d\n", hret); ++ data = sss_ptr_hash_lookup(table, object_path, struct sbus_nodes_data); ++ if (data == NULL) { + return NULL; + } + +- data = talloc_get_type(value.ptr, struct sbus_nodes_data); +- + return data->nodes_fn(mem_ctx, object_path, data->handler_data); + } + +diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h +index c8913d0f0c522147aacf3214000ef9d4855fdb0c..a5a2d47f4bfac99960fcca56aaa48077c36b96e4 100644 +--- a/src/sbus/sssd_dbus_private.h ++++ b/src/sbus/sssd_dbus_private.h +@@ -136,10 +136,8 @@ sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx, + const char *object_path, + struct sbus_interface_list **_list); + +-errno_t +-sbus_nodes_hash_init(TALLOC_CTX *mem_ctx, +- struct sbus_connection *conn, +- hash_table_t **_table); ++hash_table_t * ++sbus_nodes_hash_init(TALLOC_CTX *mem_ctx); + + const char ** + sbus_nodes_hash_lookup(TALLOC_CTX *mem_ctx, +-- +2.9.3 + diff --git a/0010-SBUS-use-sss_ptr_hash-for-signals-table.patch b/0010-SBUS-use-sss_ptr_hash-for-signals-table.patch new file mode 100644 index 0000000..0ff8b81 --- /dev/null +++ b/0010-SBUS-use-sss_ptr_hash-for-signals-table.patch @@ -0,0 +1,168 @@ +From b1afef0bc8d98c389a7f71307bee8ef9fc991ced Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 24 Jan 2017 14:02:51 +0100 +Subject: [PATCH 10/79] SBUS: use sss_ptr_hash for signals table +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch reuses sss_ptr_hash module introduced in NSS patches in sbus code. + +Reviewed-by: Fabiano Fidêncio +--- + src/sbus/sssd_dbus_connection.c | 4 +-- + src/sbus/sssd_dbus_private.h | 5 ++-- + src/sbus/sssd_dbus_signals.c | 58 ++++++++++------------------------------- + 3 files changed, 18 insertions(+), 49 deletions(-) + +diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c +index 5e493fb03e835d5f939a599efdc07f7ab2f9be28..de134f2f21bfb9697fcc8a42622817bc50b54f2a 100644 +--- a/src/sbus/sssd_dbus_connection.c ++++ b/src/sbus/sssd_dbus_connection.c +@@ -177,8 +177,8 @@ int sbus_init_connection(TALLOC_CTX *ctx, + return EIO; + } + +- ret = sbus_incoming_signal_hash_init(conn, &conn->incoming_signals); +- if (ret != EOK) { ++ conn->incoming_signals = sbus_incoming_signal_hash_init(conn); ++ if (conn->incoming_signals == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create incoming singals " + "hash table\n"); + talloc_free(conn); +diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h +index a5a2d47f4bfac99960fcca56aaa48077c36b96e4..a3d4bae166d5a4d17037b16094248d22de7e8f62 100644 +--- a/src/sbus/sssd_dbus_private.h ++++ b/src/sbus/sssd_dbus_private.h +@@ -180,9 +180,8 @@ sbus_signal_handler(DBusConnection *conn, + DBusMessage *message, + void *handler_data); + +-errno_t +-sbus_incoming_signal_hash_init(TALLOC_CTX *mem_ctx, +- hash_table_t **_table); ++hash_table_t * ++sbus_incoming_signal_hash_init(TALLOC_CTX *mem_ctx); + + void sbus_register_common_signals(struct sbus_connection *conn, void *pvt); + +diff --git a/src/sbus/sssd_dbus_signals.c b/src/sbus/sssd_dbus_signals.c +index 3f463e603a625cae8415fb17f5cd811ef0c10e15..be1c8527e5513bc258e7764239d9b16af083ac65 100644 +--- a/src/sbus/sssd_dbus_signals.c ++++ b/src/sbus/sssd_dbus_signals.c +@@ -23,6 +23,7 @@ + #include + + #include "util/util.h" ++#include "util/sss_ptr_hash.h" + #include "sbus/sssd_dbus.h" + #include "sbus/sssd_dbus_private.h" + +@@ -60,11 +61,10 @@ struct sbus_incoming_signal_data { + void *handler_data; + }; + +-errno_t +-sbus_incoming_signal_hash_init(TALLOC_CTX *mem_ctx, +- hash_table_t **_table) ++hash_table_t * ++sbus_incoming_signal_hash_init(TALLOC_CTX *mem_ctx) + { +- return sss_hash_create(mem_ctx, 10, _table); ++ return sss_ptr_hash_create(mem_ctx, NULL, NULL); + } + + static errno_t +@@ -76,30 +76,20 @@ sbus_incoming_signal_hash_add(hash_table_t *table, + { + TALLOC_CTX *tmp_ctx; + struct sbus_incoming_signal_data *data; +- hash_key_t key; +- hash_value_t value; ++ char *key; + errno_t ret; +- bool has_key; +- int hret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + +- key.type = HASH_KEY_STRING; +- key.str = talloc_asprintf(tmp_ctx, "%s.%s", iface, a_signal); +- if (key.str == NULL) { ++ key = talloc_asprintf(tmp_ctx, "%s.%s", iface, a_signal); ++ if (key == NULL) { + ret = ENOMEM; + goto done; + } + +- has_key = hash_has_key(table, &key); +- if (has_key) { +- ret = EEXIST; +- goto done; +- } +- + data = talloc_zero(tmp_ctx, struct sbus_incoming_signal_data); + if (data == NULL) { + ret = ENOMEM; +@@ -109,16 +99,11 @@ sbus_incoming_signal_hash_add(hash_table_t *table, + data->handler_data = handler_data; + data->handler_fn = handler_fn; + +- value.type = HASH_VALUE_PTR; +- value.ptr = data; +- +- hret = hash_enter(table, &key, &value); +- if (hret != HASH_SUCCESS) { +- ret = EIO; ++ ret = sss_ptr_hash_add(table, key, data, struct sbus_incoming_signal_data); ++ if (ret != EOK) { + goto done; + } + +- talloc_steal(table, key.str); + talloc_steal(table, data); + + ret = EOK; +@@ -134,31 +119,16 @@ sbus_incoming_signal_hash_lookup(hash_table_t *table, + const char *a_signal) + { + struct sbus_incoming_signal_data *data; +- hash_key_t key; +- hash_value_t value; +- int hret; ++ char *key; + +- key.type = HASH_KEY_STRING; +- key.str = talloc_asprintf(NULL, "%s.%s", iface, a_signal); +- if (key.str == NULL) { ++ key = talloc_asprintf(NULL, "%s.%s", iface, a_signal); ++ if (key == NULL) { + return NULL; + } + +- hret = hash_lookup(table, &key, &value); +- if (hret == HASH_ERROR_KEY_NOT_FOUND) { +- data = NULL; +- goto done; +- } else if (hret != HASH_SUCCESS) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to search hash table: hret=%d\n", hret); +- data = NULL; +- goto done; +- } ++ data = sss_ptr_hash_lookup(table, key, struct sbus_incoming_signal_data); ++ talloc_free(key); + +- data = talloc_get_type(value.ptr, struct sbus_incoming_signal_data); +- +-done: +- talloc_free(key.str); + return data; + } + +-- +2.9.3 + diff --git a/0011-ldap_child-Fix-use-after-free.patch b/0011-ldap_child-Fix-use-after-free.patch new file mode 100644 index 0000000..d22e453 --- /dev/null +++ b/0011-ldap_child-Fix-use-after-free.patch @@ -0,0 +1,139 @@ +From cb831fbbcb0dac8b6202037d4cd1a0d82db54f54 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 17 Jan 2017 10:17:24 +0100 +Subject: [PATCH 11/79] ldap_child: Fix use after free +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In case on any krb5 related error, we tried to send string +interpretation of krb5 error tb parrent in prepare_response. + +However, we cannot use global krb5 context (krb5_error_ctx) +because the context is every time released in done section of +ldap_child_get_tgt_sync. + +This patch rather return duplicated string to prevent use after free. + +Backtrace: + #0 __strchr_sse42 () at ../sysdeps/x86_64/multiarch/strchr.S:100 + 100 ../sysdeps/x86_64/multiarch/strchr.S: No such file or directory. + + Thread 1 (Thread 0x7fc96cad5880 (LWP 11201)): + #0 __strchr_sse42 () at ../sysdeps/x86_64/multiarch/strchr.S:100 + No locals. + #1 0x00007fc96be43725 in err_fmt_fmt (msg=0x7fc96d1cf8d0 "Cannot find KDC for requested realm", + code=-1765328230, + err_fmt=) at kerrs.c:152 + buf = {buftype = K5BUF_DYNAMIC, data = 0x7fc96d1cdb10, + space = 128, len = 0} + p = + s = 0xdededededededede
+ #2 krb5_get_error_message (ctx=, + code=code@entry=-1765328230) at kerrs.c:184 + std = 0x7fc96d1cf8d0 "Cannot find KDC for requested realm" + #3 0x00007fc96cb224e5 in sss_krb5_get_error_message (ctx=, + ec=ec@entry=-1765328230) at src/util/sss_krb5.c:424 + No locals. + #4 0x00007fc96cb1fbb0 in prepare_response (rsp=, + kerr=-1765328230, expire_time=0, + ccname=0x0, + mem_ctx=0x7fc96d1cb390) at src/providers/ldap/ldap_child.c:553 + ret = + r = 0x7fc96d1cd8b0 + krb5_msg = 0x0 + +Reviewed-by: Fabiano Fidêncio +--- + src/providers/ldap/ldap_child.c | 26 +++++++++++++++++--------- + 1 file changed, 17 insertions(+), 9 deletions(-) + +diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c +index ffcbc3985691b965c76a06805068118628adc198..3f88a28dcffc320ba66afccbdcee71432913b775 100644 +--- a/src/providers/ldap/ldap_child.c ++++ b/src/providers/ldap/ldap_child.c +@@ -276,7 +276,8 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx, + const char *keytab_name, + const krb5_deltat lifetime, + const char **ccname_out, +- time_t *expire_time_out) ++ time_t *expire_time_out, ++ char **_krb5_msg) + { + char *ccname; + char *ccname_dummy; +@@ -522,7 +523,14 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx, + *expire_time_out = my_creds.times.endtime - kdc_time_offset; + + done: +- if (krberr != 0) KRB5_SYSLOG(krberr); ++ if (krberr != 0) { ++ const char *krb5_msg; ++ ++ KRB5_SYSLOG(krberr); ++ krb5_msg = sss_krb5_get_error_message(context, krberr); ++ *_krb5_msg = talloc_strdup(memctx, krb5_msg); ++ sss_krb5_free_error_message(context, krb5_msg); ++ } + if (keytab) krb5_kt_close(context, keytab); + if (context) krb5_free_context(context); + talloc_free(tmp_ctx); +@@ -533,11 +541,11 @@ static int prepare_response(TALLOC_CTX *mem_ctx, + const char *ccname, + time_t expire_time, + krb5_error_code kerr, ++ char *krb5_msg, + struct response **rsp) + { + int ret; + struct response *r = NULL; +- const char *krb5_msg = NULL; + + r = talloc_zero(mem_ctx, struct response); + if (!r) return ENOMEM; +@@ -550,15 +558,13 @@ static int prepare_response(TALLOC_CTX *mem_ctx, + if (kerr == 0) { + ret = pack_buffer(r, EOK, kerr, ccname, expire_time); + } else { +- krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr); + if (krb5_msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "sss_krb5_get_error_message failed.\n"); ++ "Empty krb5 error message for non-zero kerr: %"PRIi32"\n", ++ kerr); + return ENOMEM; + } +- + ret = pack_buffer(r, EFAULT, kerr, krb5_msg, 0); +- sss_krb5_free_error_message(krb5_error_ctx, krb5_msg); + } + + if (ret != EOK) { +@@ -605,6 +611,7 @@ int main(int argc, const char *argv[]) + uint8_t *buf = NULL; + ssize_t len = 0; + const char *ccname = NULL; ++ char *krb5_msg = NULL; + time_t expire_time = 0; + struct input_buffer *ibuf = NULL; + struct response *resp = NULL; +@@ -721,13 +728,14 @@ int main(int argc, const char *argv[]) + kerr = ldap_child_get_tgt_sync(main_ctx, ibuf->context, + ibuf->realm_str, ibuf->princ_str, + ibuf->keytab_name, ibuf->lifetime, +- &ccname, &expire_time); ++ &ccname, &expire_time, &krb5_msg); + if (kerr != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "ldap_child_get_tgt_sync failed.\n"); + /* Do not return, must report failure */ + } + +- ret = prepare_response(main_ctx, ccname, expire_time, kerr, &resp); ++ ret = prepare_response(main_ctx, ccname, expire_time, kerr, krb5_msg, ++ &resp); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "prepare_response failed. [%d][%s].\n", + ret, strerror(ret)); +-- +2.9.3 + diff --git a/0012-FAILOVER-Improve-port-status-log-messages.patch b/0012-FAILOVER-Improve-port-status-log-messages.patch new file mode 100644 index 0000000..2e44ef6 --- /dev/null +++ b/0012-FAILOVER-Improve-port-status-log-messages.patch @@ -0,0 +1,41 @@ +From 1c7f9a676088ecee4c14df14b8688b391fb32a05 Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Mon, 19 Dec 2016 16:49:17 -0500 +Subject: [PATCH 12/79] FAILOVER: Improve port status log messages + +It should be more clear to administrators that when SSSD internal +port status is set as PORT_NOT_WORKING, this does not directly relate +to an assumed network port-related issue. + +Reviewed-by: Jakub Hrozek +--- + src/providers/fail_over.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c +index 5d3c26d4a690769637f2fa4f41a76627cbdba77a..168e59d6f4e9fc8abd827be21004daef2c6613f0 100644 +--- a/src/providers/fail_over.c ++++ b/src/providers/fail_over.c +@@ -376,12 +376,18 @@ get_port_status(struct fo_server *server) + "Port status of port %d for server '%s' is '%s'\n", server->port, + SERVER_NAME(server), str_port_status(server->port_status)); + ++ if (server->port_status == PORT_NOT_WORKING) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "SSSD is unable to complete the full " ++ "connection request, this internal status does not necessarily " ++ "indicate network port issues.\n"); ++ } ++ + timeout = server->service->ctx->opts->retry_timeout; + if (timeout != 0 && server->port_status == PORT_NOT_WORKING) { + gettimeofday(&tv, NULL); + if (STATUS_DIFF(server, tv) > timeout) { + DEBUG(SSSDBG_CONF_SETTINGS, +- "Reseting the status of port %d for server '%s'\n", ++ "Resetting the status of port %d for server '%s'\n", + server->port, SERVER_NAME(server)); + server->port_status = PORT_NEUTRAL; + server->last_status_change.tv_sec = tv.tv_sec; +-- +2.9.3 + diff --git a/0013-IFP-Update-ifp_iface_generated.c.patch b/0013-IFP-Update-ifp_iface_generated.c.patch new file mode 100644 index 0000000..83f4b2e --- /dev/null +++ b/0013-IFP-Update-ifp_iface_generated.c.patch @@ -0,0 +1,44 @@ +From 2ddcd5785f10de42bf03dfc36eca94dbc1fc1fb3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Mon, 6 Feb 2017 18:58:18 +0000 +Subject: [PATCH 13/79] IFP: Update ifp_iface_generated.c +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +These changes are leftovers from commit 78b4b7e. + +Signed-off-by: Fabiano Fidêncio +Reviewed-by: Pavel Březina +--- + src/responder/ifp/ifp_iface_generated.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c +index 90cd4ff9e3a4dff3e8d2e3d904bbf6bde6a748ae..d9df6c623b7f597b8ea9427a58488b340b1934ea 100644 +--- a/src/responder/ifp/ifp_iface_generated.c ++++ b/src/responder/ifp/ifp_iface_generated.c +@@ -263,11 +263,6 @@ const struct sbus_interface_meta iface_ifp_meta = { + sbus_invoke_get_all, /* GetAll invoker */ + }; + +-/* methods for org.freedesktop.sssd.infopipe.Components */ +-const struct sbus_method_meta iface_ifp_components__methods[] = { +- { NULL, } +-}; +- + /* property info for org.freedesktop.sssd.infopipe.Components */ + const struct sbus_property_meta iface_ifp_components__properties[] = { + { +@@ -321,7 +316,7 @@ const struct sbus_property_meta iface_ifp_components__properties[] = { + /* interface info for org.freedesktop.sssd.infopipe.Components */ + const struct sbus_interface_meta iface_ifp_components_meta = { + "org.freedesktop.sssd.infopipe.Components", /* name */ +- iface_ifp_components__methods, ++ NULL, /* no methods */ + NULL, /* no signals */ + iface_ifp_components__properties, + sbus_invoke_get_all, /* GetAll invoker */ +-- +2.9.3 + diff --git a/0014-SYSTEMD-Update-journald-drop-in-file.patch b/0014-SYSTEMD-Update-journald-drop-in-file.patch new file mode 100644 index 0000000..c890fed --- /dev/null +++ b/0014-SYSTEMD-Update-journald-drop-in-file.patch @@ -0,0 +1,30 @@ +From 7b4704a10958bb7d3390db9eff863875d2b643f7 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 7 Feb 2017 09:52:59 +0100 +Subject: [PATCH 14/79] SYSTEMD: Update journald drop-in file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We changed type forking into type notify as part of commit +d4063e9a21a4e203bee7e0a0144fa8cabb14cc46. +But we forgot to update template drop-in file for logging into journald. + +Reviewed-by: Fabiano Fidêncio +--- + src/sysv/systemd/journal.conf.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/sysv/systemd/journal.conf.in b/src/sysv/systemd/journal.conf.in +index d89325e0872881e3e8485102d9971871101098f3..9ce170b4893629792516aab41573adea1fb741f0 100644 +--- a/src/sysv/systemd/journal.conf.in ++++ b/src/sysv/systemd/journal.conf.in +@@ -4,4 +4,4 @@ + # run 'systemctl daemon-reload' and then restart the SSSD service + # for this to take effect + #ExecStart= +-#ExecStart=@sbindir@/sssd -D ++#ExecStart=@sbindir@/sssd -i +-- +2.9.3 + diff --git a/0501-Partially-revert-CONFIG-Use-default-config-when-none.patch b/0015-Partially-revert-CONFIG-Use-default-config-when-none.patch similarity index 89% rename from 0501-Partially-revert-CONFIG-Use-default-config-when-none.patch rename to 0015-Partially-revert-CONFIG-Use-default-config-when-none.patch index 40f0f43..b6198a7 100644 --- a/0501-Partially-revert-CONFIG-Use-default-config-when-none.patch +++ b/0015-Partially-revert-CONFIG-Use-default-config-when-none.patch @@ -1,13 +1,15 @@ -From 829aa39dffbe35f58b34159b962a2dd8de85fd30 Mon Sep 17 00:00:00 2001 +From c029f707d4847b01ff64bf3bb1fd46c0b5927cdb Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik Date: Mon, 12 Dec 2016 18:33:48 +0100 -Subject: [PATCH] Partially revert "CONFIG: Use default config when none +Subject: [PATCH 15/79] Partially revert "CONFIG: Use default config when none provided" This reverts part of commit 59744cff6edb106ae799b2321cb8731edadf409a. Removed is copying of default configuration into /etc/sssd/sssd.conf Sample configurations is still part of installation. + +Reviewed-by: Jakub Hrozek --- Makefile.am | 3 --- src/confdb/confdb.h | 1 - @@ -15,10 +17,10 @@ Sample configurations is still part of installation. 3 files changed, 4 insertions(+), 40 deletions(-) diff --git a/Makefile.am b/Makefile.am -index a15e68f682f6d8af301e11df8dcaef6d7f27e8c0..45d44146e737fc8460a2ed9ffc0171a6bb494b2b 100644 +index 674d328f52929cc2b20d1212af830c3777312bf1..6d21af8e8c455622d8c4c8b4e325789c4c1e34cb 100644 --- a/Makefile.am +++ b/Makefile.am -@@ -462,7 +462,6 @@ AM_CPPFLAGS = \ +@@ -473,7 +473,6 @@ AM_CPPFLAGS = \ -DSSSDDATADIR=\"$(sssddatadir)\" \ -DSSSD_LIBEXEC_PATH=\"$(sssdlibexecdir)\" \ -DSSSD_CONF_DIR=\"$(sssdconfdir)\" \ @@ -26,7 +28,7 @@ index a15e68f682f6d8af301e11df8dcaef6d7f27e8c0..45d44146e737fc8460a2ed9ffc0171a6 -DSSS_NSS_MCACHE_DIR=\"$(mcpath)\" \ -DSSS_NSS_SOCKET_NAME=\"$(pipepath)/nss\" \ -DSSS_PAM_SOCKET_NAME=\"$(pipepath)/pam\" \ -@@ -1232,8 +1231,6 @@ sssd_SOURCES = \ +@@ -1252,8 +1251,6 @@ sssd_SOURCES = \ src/confdb/confdb_setup.c \ src/monitor/monitor_iface_generated.c \ src/util/nscd.c \ @@ -36,7 +38,7 @@ index a15e68f682f6d8af301e11df8dcaef6d7f27e8c0..45d44146e737fc8460a2ed9ffc0171a6 sssd_LDADD = \ $(SSSD_LIBS) \ diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h -index 12beaabf8c949bd111abbe16cb98a205490fb08f..4813072bdafb5d6c9ec56a9ccaa5db6a1120112d 100644 +index 9055048865f008a2c3732551730c4a881cb9108c..dd6ac77f5a787b0434b56fccba49aa195b13297a 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -40,7 +40,6 @@ @@ -113,5 +115,5 @@ index d6feab9000d54d2c3761de6d8e990053ade7e85f..a71d9dd1202824b3c9a7e69f1d8fa905 ret = sss_ini_config_access_check(init_data); -- -2.11.0 +2.9.3 diff --git a/0016-SUDO-Add-skip_entry-boolean-to-sudo-conversions.patch b/0016-SUDO-Add-skip_entry-boolean-to-sudo-conversions.patch new file mode 100644 index 0000000..de81140 --- /dev/null +++ b/0016-SUDO-Add-skip_entry-boolean-to-sudo-conversions.patch @@ -0,0 +1,177 @@ +From d0aae3c1e87e2e51ab178b7b343261443094a974 Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Fri, 20 Jan 2017 15:43:34 -0500 +Subject: [PATCH 16/79] SUDO: Add skip_entry boolean to sudo conversions + +Add boolean to convert_attributes function and pass boolean as argument +to sudo conversion functions to add logic for skipping unexpected +entries like replication conflicts. + +Resolves: +https://fedorahosted.org/sssd/ticket/3288 + +Reviewed-by: Jakub Hrozek +--- + src/providers/ipa/ipa_sudo_conversion.c | 55 ++++++++++++++++++++++++--------- + 1 file changed, 41 insertions(+), 14 deletions(-) + +diff --git a/src/providers/ipa/ipa_sudo_conversion.c b/src/providers/ipa/ipa_sudo_conversion.c +index 9dbc8604df544ce0865a2e99facf92cfd697123b..05d863c20954c816e52d27fe4a5e1553776c6d41 100644 +--- a/src/providers/ipa/ipa_sudo_conversion.c ++++ b/src/providers/ipa/ipa_sudo_conversion.c +@@ -746,12 +746,15 @@ struct ipa_sudo_conv_result_ctx { + static const char * + convert_host(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value) ++ const char *value, ++ bool *skip_entry) + { + char *rdn; + const char *group; + errno_t ret; + ++ *skip_entry = false; ++ + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_HOST(conv->map_host)); + if (ret == EOK) { +@@ -765,7 +768,8 @@ convert_host(TALLOC_CTX *mem_ctx, + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_HOSTGROUP(conv->map_hostgroup)); + if (ret == ENOENT) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s: Skipping\n", value); ++ *skip_entry = true; + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", +@@ -782,12 +786,15 @@ convert_host(TALLOC_CTX *mem_ctx, + static const char * + convert_user(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value) ++ const char *value, ++ bool *skip_entry) + { + char *rdn; + const char *group; + errno_t ret; + ++ *skip_entry = false; ++ + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_USER(conv->map_user)); + if (ret == EOK) { +@@ -801,7 +808,8 @@ convert_user(TALLOC_CTX *mem_ctx, + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_GROUP(conv->map_group)); + if (ret == ENOENT) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s: Skipping\n", value); ++ *skip_entry = true; + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", +@@ -818,12 +826,15 @@ convert_user(TALLOC_CTX *mem_ctx, + static const char * + convert_user_fqdn(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value) ++ const char *value, ++ bool *skip_entry) + { + const char *shortname = NULL; + char *fqdn = NULL; + +- shortname = convert_user(mem_ctx, conv, value); ++ *skip_entry = false; ++ ++ shortname = convert_user(mem_ctx, conv, value, skip_entry); + if (shortname == NULL) { + return NULL; + } +@@ -836,15 +847,19 @@ convert_user_fqdn(TALLOC_CTX *mem_ctx, + static const char * + convert_group(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value) ++ const char *value, ++ bool *skip_entry) + { + char *rdn; + errno_t ret; + ++ *skip_entry = false; ++ + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_GROUP(conv->map_group)); + if (ret == ENOENT) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s: Skipping\n", value); ++ *skip_entry = true; + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", +@@ -858,7 +873,8 @@ convert_group(TALLOC_CTX *mem_ctx, + static const char * + convert_runasextusergroup(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value) ++ const char *value, ++ bool *skip_entry) + { + return talloc_asprintf(mem_ctx, "%%%s", value); + } +@@ -866,8 +882,12 @@ convert_runasextusergroup(TALLOC_CTX *mem_ctx, + static const char * + convert_cat(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value) ++ const char *value, ++ bool *skip_entry) + { ++ ++ *skip_entry = false; ++ + if (strcmp(value, "all") == 0) { + return talloc_strdup(mem_ctx, "ALL"); + } +@@ -885,12 +905,14 @@ convert_attributes(struct ipa_sudo_conv *conv, + const char *value; + errno_t ret; + int i, j; ++ bool skip_entry; + static struct { + const char *ipa; + const char *sudo; + const char *(*conv_fn)(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, +- const char *value); ++ const char *value, ++ bool *skip_entry); + } table[] = {{SYSDB_NAME, SYSDB_SUDO_CACHE_AT_CN , NULL}, + {SYSDB_IPA_SUDORULE_HOST, SYSDB_SUDO_CACHE_AT_HOST , convert_host}, + {SYSDB_IPA_SUDORULE_USER, SYSDB_SUDO_CACHE_AT_USER , convert_user_fqdn}, +@@ -931,10 +953,15 @@ convert_attributes(struct ipa_sudo_conv *conv, + + for (j = 0; values[j] != NULL; j++) { + if (table[i].conv_fn != NULL) { +- value = table[i].conv_fn(tmp_ctx, conv, values[j]); ++ value = table[i].conv_fn(tmp_ctx, conv, values[j], &skip_entry); + if (value == NULL) { +- ret = ENOMEM; +- goto done; ++ if (skip_entry) { ++ ret = ENOENT; ++ continue; ++ } else { ++ ret = ENOMEM; ++ goto done; ++ } + } + } else { + value = values[j]; +-- +2.9.3 + diff --git a/0017-TESTS-Add-to-IPA-DN-test.patch b/0017-TESTS-Add-to-IPA-DN-test.patch new file mode 100644 index 0000000..5b1c276 --- /dev/null +++ b/0017-TESTS-Add-to-IPA-DN-test.patch @@ -0,0 +1,36 @@ +From 1404f3aa541849d880cce591584ba1580014cb50 Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Wed, 25 Jan 2017 17:05:01 -0500 +Subject: [PATCH 17/79] TESTS: Add to IPA DN test + +Add test to ensure conflict entries return ENOENT + +Resolves: +https://fedorahosted.org/sssd/ticket/3288 + +Reviewed-by: Jakub Hrozek +--- + src/tests/cmocka/test_ipa_dn.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/tests/cmocka/test_ipa_dn.c b/src/tests/cmocka/test_ipa_dn.c +index a6e26ec31ff25519ad895ef934dac0e3a3dd83ae..ff951f28acbb8a567c3d27027a688386ff08b475 100644 +--- a/src/tests/cmocka/test_ipa_dn.c ++++ b/src/tests/cmocka/test_ipa_dn.c +@@ -169,6 +169,13 @@ static void ipa_get_rdn_test(void **state) + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1", &rdn, "cn", "attr1", "value1"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); ++ ++ ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, ++ "cn=rdn+nsuniqueid=9b1e3301-c32611e6-bdcae37a-ef905e7c," ++ "attr1=value1,attr2=value2,dc=example,dc=com", ++ &rdn, "cn", "attr1", "value1", "attr2", "value2"); ++ assert_int_equal(ret, ENOENT); ++ assert_null(rdn); + } + + int main(int argc, const char *argv[]) +-- +2.9.3 + diff --git a/0018-LDAP-Better-logging-message.patch b/0018-LDAP-Better-logging-message.patch new file mode 100644 index 0000000..d2a7b8b --- /dev/null +++ b/0018-LDAP-Better-logging-message.patch @@ -0,0 +1,28 @@ +From c3593f06da54315c88a08a46cfc0def366acad43 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20=C4=8Cech?= +Date: Thu, 19 Jan 2017 12:51:27 +0100 +Subject: [PATCH 18/79] LDAP: Better logging message + +Reviewed-by: Sumit Bose +Reviewed-by: Jakub Hrozek +--- + src/providers/ldap/sdap.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index dc7d5e0caf223c3ee3c43054aa44e796f1b37766..eb460d93bfb067e780868bc9f7bf4e6e0aa1b4a3 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -1691,7 +1691,8 @@ static bool sdap_object_in_domain(struct sdap_options *opts, + sdmatch = sdap_domain_get_by_dn(opts, original_dn); + if (sdmatch == NULL) { + DEBUG(SSSDBG_FUNC_DATA, +- "The group has no original DN, assuming our domain\n"); ++ "The original DN of the group cannot " ++ "be related to any search base\n"); + return true; + } + +-- +2.9.3 + diff --git a/0019-SYSDB-Removing-of-sysdb_try_to_find_expected_dn.patch b/0019-SYSDB-Removing-of-sysdb_try_to_find_expected_dn.patch new file mode 100644 index 0000000..ade1189 --- /dev/null +++ b/0019-SYSDB-Removing-of-sysdb_try_to_find_expected_dn.patch @@ -0,0 +1,592 @@ +From 3ee411625aee19afda7477bb10b52c3da378b6fb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20=C4=8Cech?= +Date: Wed, 4 Jan 2017 15:33:30 +0100 +Subject: [PATCH 19/79] SYSDB: Removing of sysdb_try_to_find_expected_dn() + +Currently in order to match multiple LDAP search results we +use two different functions - we have sysdb_try_to_find_expected_dn() +but also sdap_object_in_domain(). + +This patch removes sysdb_try_to_find_expected_dn() and add new +sdap_search_initgr_user_in_batch() based on sdap_object_in_domain(). +This function covers necessary logic. + +Resolves: +https://fedorahosted.org/sssd/ticket/3230 + +Reviewed-by: Sumit Bose +Reviewed-by: Jakub Hrozek +--- + src/db/sysdb.h | 6 - + src/db/sysdb_subdomains.c | 332 ----------------------------- + src/providers/ldap/sdap.c | 6 +- + src/providers/ldap/sdap.h | 4 + + src/providers/ldap/sdap_async_initgroups.c | 28 ++- + src/tests/cmocka/test_sysdb_subdomains.c | 104 --------- + 6 files changed, 30 insertions(+), 450 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 8a363d09066806c4e7836e4e0cd19ce645d14ee2..809ca359a32f85ef3afbad082665c7eaa9374830 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -1309,10 +1309,4 @@ errno_t sysdb_handle_original_uuid(const char *orig_name, + struct sysdb_attrs *dest_attrs, + const char *dest_name); + +-errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom, +- const char *domain_component_name, +- const char *ldap_search_base, +- struct sysdb_attrs **usr_attrs, +- size_t count, +- struct sysdb_attrs **exp_usr); + #endif /* __SYS_DB_H__ */ +diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c +index 780140484f6f023bc6e8c12266e3b81ff016ec10..1f43bfc12e73a9fc7f3b66c85b47f38d2c1a3c19 100644 +--- a/src/db/sysdb_subdomains.c ++++ b/src/db/sysdb_subdomains.c +@@ -1144,335 +1144,3 @@ done: + talloc_free(tmp_ctx); + return ret; + } +- +-static errno_t match_cn_users(TALLOC_CTX *tmp_ctx, +- struct sysdb_attrs **usr_attrs, +- size_t count, +- const char *dom_basedn, +- struct sysdb_attrs **_result) +-{ +- errno_t ret; +- const char *orig_dn; +- size_t dn_len; +- struct sysdb_attrs *result = NULL; +- const char *result_dn_str = NULL; +- char *cn_users_basedn; +- size_t cn_users_basedn_len; +- +- cn_users_basedn = talloc_asprintf(tmp_ctx, "%s%s", "cn=users,", dom_basedn); +- if (cn_users_basedn == NULL) { +- ret = ENOMEM; +- goto done; +- } +- cn_users_basedn_len = strlen(cn_users_basedn); +- DEBUG(SSSDBG_TRACE_ALL, "cn=users baseDN is [%s].\n", cn_users_basedn); +- +- for (size_t c = 0; c < count; c++) { +- ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); +- goto done; +- } +- dn_len = strlen(orig_dn); +- +- if (dn_len > cn_users_basedn_len +- && strcasecmp(orig_dn + (dn_len - cn_users_basedn_len), +- cn_users_basedn) == 0) { +- DEBUG(SSSDBG_TRACE_ALL, +- "Found matching dn [%s].\n", orig_dn); +- if (result != NULL) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Found 2 matching DN [%s] and [%s], expecting only 1.\n", +- result_dn_str, orig_dn); +- ret = EINVAL; +- goto done; +- } +- result = usr_attrs[c]; +- result_dn_str = orig_dn; +- } +- } +- +- ret = EOK; +-done: +- *_result = result; +- return ret; +-} +- +-static errno_t match_non_dc_comp(TALLOC_CTX *tmp_ctx, +- struct sss_domain_info *dom, +- struct sysdb_attrs **usr_attrs, +- size_t count, +- struct ldb_dn *ldb_basedn, +- const char *basedn, +- const char *domain_component_name, +- struct sysdb_attrs **_result) +-{ +- errno_t ret; +- const char *orig_dn; +- size_t orig_dn_len; +- size_t basedn_len; +- struct ldb_context *ldb_ctx; +- struct ldb_dn *ldb_orig_dn; +- int dn_comp_num; +- int basedn_comp_num; +- const char *component_name; +- struct sysdb_attrs *result = NULL; +- const char *result_dn_str = NULL; +- +- ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb); +- if (ldb_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n"); +- ret = EINVAL; +- goto done; +- } +- +- basedn_len = strlen(basedn); +- +- basedn_comp_num = ldb_dn_get_comp_num(ldb_basedn); +- basedn_comp_num++; +- +- for (size_t c = 0; c < count; c++) { +- ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n"); +- goto done; +- } +- orig_dn_len = strlen(orig_dn); +- +- if (orig_dn_len > basedn_len +- /* Does the user's original DN with the non-domain part +- * stripped match the domain base DN? +- */ +- && strcasecmp(orig_dn + (orig_dn_len - basedn_len), +- basedn) == 0) { +- ldb_orig_dn = ldb_dn_new(tmp_ctx, ldb_ctx, orig_dn); +- if (ldb_orig_dn == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed"); +- ret = ENOMEM; +- goto done; +- } +- +- dn_comp_num = ldb_dn_get_comp_num(ldb_orig_dn); +- if (dn_comp_num > basedn_comp_num) { +- component_name = ldb_dn_get_component_name(ldb_orig_dn, +- (dn_comp_num - basedn_comp_num)); +- DEBUG(SSSDBG_TRACE_ALL, "Comparing [%s] and [%s].\n", +- component_name, +- domain_component_name); +- /* If the component is NOT a DC component, then the entry +- * must come from our domain, perhaps from a child container. +- * If it matched the DC component, the entry was from a child +- * subdomain different from this one. +- */ +- if (component_name != NULL +- && strcasecmp(component_name, +- domain_component_name) != 0) { +- DEBUG(SSSDBG_TRACE_ALL, +- "Found matching dn [%s].\n", orig_dn); +- if (result != NULL) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Found 2 matching DN [%s] and [%s], " +- "expecting only 1.\n", result_dn_str, orig_dn); +- ret = EINVAL; +- goto done; +- } +- result = usr_attrs[c]; +- result_dn_str = orig_dn; +- } +- } +- } +- } +- +- ret = EOK; +- *_result = result; +-done: +- return ret; +-} +- +-static errno_t match_basedn(TALLOC_CTX *tmp_ctx, +- struct sss_domain_info *dom, +- struct sysdb_attrs **usr_attrs, +- size_t count, +- const char *dom_basedn, +- const char *domain_component_name, +- struct sysdb_attrs **_result) +-{ +- struct ldb_context *ldb_ctx; +- struct ldb_dn *ldb_dom_basedn; +- +- ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb); +- if (ldb_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n"); +- return EINVAL; +- } +- +- +- ldb_dom_basedn = ldb_dn_new(tmp_ctx, ldb_ctx, dom_basedn); +- if (ldb_dom_basedn == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n"); +- return ENOMEM; +- } +- +- return match_non_dc_comp(tmp_ctx, dom, +- usr_attrs, count, +- ldb_dom_basedn, dom_basedn, +- domain_component_name, +- _result); +-} +- +-static errno_t match_search_base(TALLOC_CTX *tmp_ctx, +- struct sss_domain_info *dom, +- const char *domain_component_name, +- const char *domain_search_base, +- struct sysdb_attrs **usr_attrs, +- size_t count, +- struct sysdb_attrs **_result) +-{ +- errno_t ret; +- bool ok; +- const char *search_base; +- struct ldb_context *ldb_ctx; +- struct sysdb_attrs *result = NULL; +- struct ldb_dn *ldb_search_base; +- int search_base_comp_num; +- int non_dc_comp_num; +- const char *component_name; +- +- ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb); +- if (ldb_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n"); +- ret = EINVAL; +- goto done; +- } +- +- ldb_search_base = ldb_dn_new(tmp_ctx, ldb_ctx, domain_search_base); +- if (ldb_search_base == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- +- /* strip non-DC components from the search base */ +- search_base_comp_num = ldb_dn_get_comp_num(ldb_search_base); +- for (non_dc_comp_num = 0; +- non_dc_comp_num < search_base_comp_num; +- non_dc_comp_num++) { +- +- component_name = ldb_dn_get_component_name(ldb_search_base, +- non_dc_comp_num); +- if (strcasecmp(domain_component_name, component_name) == 0) { +- break; +- } +- } +- +- if (non_dc_comp_num == search_base_comp_num) { +- /* The search base does not have any non-DC components, the search wouldn't +- * match anyway +- */ +- ret = EOK; +- *_result = NULL; +- goto done; +- } +- +- ok = ldb_dn_remove_child_components(ldb_search_base, non_dc_comp_num); +- if (!ok) { +- ret = EINVAL; +- goto done; +- } +- +- search_base = ldb_dn_get_linearized(ldb_search_base); +- if (search_base == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- ret = match_cn_users(tmp_ctx, usr_attrs, count, search_base, &result); +- if (ret != EOK) { +- goto done; +- } +- +- if (result == NULL) { +- ret = match_non_dc_comp(tmp_ctx, dom, +- usr_attrs, count, +- ldb_search_base, search_base, +- domain_component_name, +- &result); +- if (ret != EOK) { +- goto done; +- } +- } +- +- ret = EOK; +- *_result = result; +-done: +- return ret; +-} +- +-errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom, +- const char *domain_component_name, +- const char *domain_search_base, +- struct sysdb_attrs **usr_attrs, +- size_t count, +- struct sysdb_attrs **exp_usr) +-{ +- char *dom_basedn; +- int ret; +- TALLOC_CTX *tmp_ctx; +- struct sysdb_attrs *result = NULL; +- +- if (dom == NULL || domain_component_name == NULL +- || domain_search_base == NULL +- || usr_attrs == NULL || count == 0) { +- return EINVAL; +- } +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); +- return ENOMEM; +- } +- +- ret = domain_to_basedn(tmp_ctx, dom->name, &dom_basedn); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n"); +- ret = EINVAL; +- goto done; +- } +- +- ret = match_cn_users(tmp_ctx, usr_attrs, count, dom_basedn, &result); +- if (ret != EOK) { +- goto done; +- } +- +- if (result == NULL) { +- ret = match_basedn(tmp_ctx, dom, usr_attrs, +- count, dom_basedn, domain_component_name, +- &result); +- if (ret != EOK) { +- goto done; +- } +- } +- +- if (result == NULL) { +- ret = match_search_base(tmp_ctx, dom, domain_component_name, +- domain_search_base, usr_attrs, count, +- &result); +- if (ret != EOK) { +- goto done; +- } +- } +- +- if (result == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "No matching DN found.\n"); +- ret = ENOENT; +- goto done; +- } +- +- *exp_usr = result; +- +- ret = EOK; +-done: +- talloc_free(tmp_ctx); +- +- return ret; +-} +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index eb460d93bfb067e780868bc9f7bf4e6e0aa1b4a3..bfb7fc6d2a38debf56acae18b8e7eb7a08ccbd1b 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -1673,9 +1673,9 @@ char *sdap_make_oc_list(TALLOC_CTX *mem_ctx, struct sdap_attr_map *map) + } + } + +-static bool sdap_object_in_domain(struct sdap_options *opts, +- struct sysdb_attrs *obj, +- struct sss_domain_info *dom) ++bool sdap_object_in_domain(struct sdap_options *opts, ++ struct sysdb_attrs *obj, ++ struct sss_domain_info *dom) + { + errno_t ret; + const char *original_dn = NULL; +diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h +index e3cb8464ff40538e1e7f1ba853ed71d9a5cc3c98..6d4543ed48ce19f82252d34b6d0833a546a81bb9 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -616,4 +616,8 @@ size_t sdap_steal_objects_in_dom(struct sdap_options *opts, + size_t count, + bool filter); + ++bool sdap_object_in_domain(struct sdap_options *opts, ++ struct sysdb_attrs *obj, ++ struct sss_domain_info *dom); ++ + #endif /* _SDAP_H_ */ +diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c +index 2cd9c15b9e284592b3e132eb3d1f35b09a69046e..8c7a65bf36abf341e077cf9eac18a234d3a07c07 100644 +--- a/src/providers/ldap/sdap_async_initgroups.c ++++ b/src/providers/ldap/sdap_async_initgroups.c +@@ -23,6 +23,7 @@ + + #include "util/util.h" + #include "db/sysdb.h" ++#include "providers/ldap/sdap.h" + #include "providers/ldap/sdap_async_private.h" + #include "providers/ldap/ldap_common.h" + #include "providers/ldap/sdap_idmap.h" +@@ -2890,6 +2891,25 @@ static errno_t sdap_get_initgr_next_base(struct tevent_req *req) + return EOK; + } + ++static int sdap_search_initgr_user_in_batch(struct sdap_get_initgr_state *state, ++ struct sysdb_attrs **users, ++ size_t count) ++{ ++ int ret = EINVAL; ++ ++ for (size_t i = 0; i < count; i++) { ++ if (sdap_object_in_domain(state->opts, users[i], state->dom) == false) { ++ continue; ++ } ++ ++ state->orig_user = talloc_steal(state, users[i]); ++ ret = EOK; ++ break; ++ } ++ ++ return ret; ++} ++ + static void sdap_get_initgr_user(struct tevent_req *subreq) + { + struct tevent_req *req = tevent_req_callback_data(subreq, +@@ -2951,13 +2971,11 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) + * the first search base because all bases in a single domain would + * have the same DC= components + */ +- ret = sysdb_try_to_find_expected_dn(state->dom, "dc", +- state->sdom->search_bases[0]->basedn, +- usr_attrs, count, +- &state->orig_user); ++ ret = sdap_search_initgr_user_in_batch(state, usr_attrs, count); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, +- "try_to_find_expected_dn failed. No matching DN found.\n"); ++ "sdap_search_initgr_user_in_batch failed. " ++ "No matching DN found.\n"); + tevent_req_error(req, EINVAL); + return; + } +diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c +index 52056e0435d2793893f1a4e336f38acf7a70b2c0..52242e516ed0490e5094ccc1392908207e00359d 100644 +--- a/src/tests/cmocka/test_sysdb_subdomains.c ++++ b/src/tests/cmocka/test_sysdb_subdomains.c +@@ -515,107 +515,6 @@ static void test_sysdb_link_ad_multidom(void **state) + + } + +-static void test_try_to_find_expected_dn(void **state) +-{ +- int ret; +- struct sysdb_attrs *result; +- struct sysdb_attrs *usr_attrs[10] = { NULL }; +- struct sysdb_attrs *dom_usr_attrs[10] = { NULL }; +- struct sss_domain_info *dom; +- char *dom_basedn; +- struct subdom_test_ctx *test_ctx = +- talloc_get_type(*state, struct subdom_test_ctx); +- +- dom = find_domain_by_name(test_ctx->tctx->dom, +- "child2.test_sysdb_subdomains_2", true); +- assert_non_null(dom); +- +- ret = domain_to_basedn(test_ctx, dom->name, &dom_basedn); +- assert_int_equal(ret, EOK); +- +- usr_attrs[0] = sysdb_new_attrs(test_ctx); +- assert_non_null(usr_attrs[0]); +- +- ret = sysdb_attrs_add_string(usr_attrs[0], SYSDB_ORIG_DN, +- "uid=user,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2"); +- assert_int_equal(ret, EOK); +- +- ret = sysdb_try_to_find_expected_dn(NULL, NULL, NULL, NULL, 0, NULL); +- assert_int_equal(ret, EINVAL); +- +- ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, usr_attrs, 1, &result); +- assert_int_equal(ret, ENOENT); +- +- ret = sysdb_try_to_find_expected_dn(dom, "xy", dom_basedn, usr_attrs, 1, &result); +- assert_int_equal(ret, EOK); +- assert_ptr_equal(result, usr_attrs[0]); +- +- usr_attrs[1] = sysdb_new_attrs(test_ctx); +- assert_non_null(usr_attrs[1]); +- +- ret = sysdb_attrs_add_string(usr_attrs[1], SYSDB_ORIG_DN, +- "uid=user1,cn=abc,dc=child2,dc=test_sysdb_subdomains_2"); +- assert_int_equal(ret, EOK); +- +- usr_attrs[2] = sysdb_new_attrs(test_ctx); +- assert_non_null(usr_attrs[2]); +- +- ret = sysdb_attrs_add_string(usr_attrs[2], SYSDB_ORIG_DN, +- "uid=user2,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2"); +- assert_int_equal(ret, EOK); +- +- ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, usr_attrs, 3, &result); +- assert_int_equal(ret, EOK); +- assert_ptr_equal(result, usr_attrs[1]); +- +- ret = sysdb_try_to_find_expected_dn(dom, "xy", dom_basedn, usr_attrs, 3, &result); +- assert_int_equal(ret, EINVAL); +- +- /* Make sure cn=users match is preferred */ +- talloc_free(usr_attrs[2]); +- usr_attrs[2] = sysdb_new_attrs(test_ctx); +- assert_non_null(usr_attrs[2]); +- +- ret = sysdb_attrs_add_string(usr_attrs[2], SYSDB_ORIG_DN, +- "uid=user2,cn=abc,cn=users,dc=child2,dc=test_sysdb_subdomains_2"); +- assert_int_equal(ret, EOK); +- +- ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, usr_attrs, 3, &result); +- assert_int_equal(ret, EOK); +- assert_ptr_equal(result, usr_attrs[2]); +- +- /* test a case where the domain name does not match the basedn */ +- dom->name = discard_const("default"); +- dom_usr_attrs[0] = usr_attrs[0]; +- +- ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, dom_usr_attrs, 1, &result); +- assert_int_equal(ret, ENOENT); +- +- dom_usr_attrs[1] = usr_attrs[1]; +- dom_usr_attrs[2] = usr_attrs[2]; +- +- /* Make sure cn=users match is preferred */ +- ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, dom_usr_attrs, 3, &result); +- assert_int_equal(ret, EOK); +- assert_ptr_equal(result, dom_usr_attrs[2]); +- +- talloc_free(usr_attrs[2]); +- usr_attrs[2] = sysdb_new_attrs(test_ctx); +- assert_non_null(usr_attrs[2]); +- ret = sysdb_attrs_add_string(usr_attrs[2], SYSDB_ORIG_DN, +- "uid=user2,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2"); +- assert_int_equal(ret, EOK); +- +- dom_usr_attrs[2] = usr_attrs[2]; +- ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, dom_usr_attrs, 3, &result); +- assert_int_equal(ret, EOK); +- assert_ptr_equal(result, usr_attrs[1]); +- +- talloc_free(usr_attrs[0]); +- talloc_free(usr_attrs[1]); +- talloc_free(usr_attrs[2]); +-} +- + int main(int argc, const char *argv[]) + { + int rv; +@@ -649,9 +548,6 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_sysdb_link_ad_multidom, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), +- cmocka_unit_test_setup_teardown(test_try_to_find_expected_dn, +- test_sysdb_subdom_setup, +- test_sysdb_subdom_teardown), + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ +-- +2.9.3 + diff --git a/0020-TEST-create_multidom_test_ctx-extending.patch b/0020-TEST-create_multidom_test_ctx-extending.patch new file mode 100644 index 0000000..7e338b6 --- /dev/null +++ b/0020-TEST-create_multidom_test_ctx-extending.patch @@ -0,0 +1,138 @@ +From f1e3364a72eb75673d10cf8c97ba8f1d7a385405 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20=C4=8Cech?= +Date: Thu, 12 Jan 2017 13:16:10 +0100 +Subject: [PATCH 20/79] TEST: create_multidom_test_ctx() extending + +Function create_multidom_test_ctx() prepares test environment for +multidomains. This patch enables setting of different params for +each domain. + +Resolves: +https://fedorahosted.org/sssd/ticket/3230 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Sumit Bose +--- + src/tests/cmocka/test_ad_common.c | 5 +---- + src/tests/cmocka/test_sysdb_subdomains.c | 5 +---- + src/tests/cmocka/test_sysdb_ts_cache.c | 5 +---- + src/tests/common.h | 2 +- + src/tests/common_dom.c | 6 +++--- + 5 files changed, 7 insertions(+), 16 deletions(-) + +diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c +index 7ec292092e0de6a3edabfe6e7480f777e47a475d..ea9998951d1214ad41429cad38a28efcea11dcd0 100644 +--- a/src/tests/cmocka/test_ad_common.c ++++ b/src/tests/cmocka/test_ad_common.c +@@ -78,9 +78,6 @@ struct ad_sysdb_test_ctx { + static int test_ad_sysdb_setup(void **state) + { + struct ad_sysdb_test_ctx *test_ctx; +- struct sss_test_conf_param params[] = { +- { NULL, NULL }, /* Sentinel */ +- }; + + assert_true(leak_check_setup()); + +@@ -92,7 +89,7 @@ static int test_ad_sysdb_setup(void **state) + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, +- TEST_ID_PROVIDER, params); ++ TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + *state = test_ctx; +diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c +index 52242e516ed0490e5094ccc1392908207e00359d..49f44998a06740d1df70ac354ee741824acd8f50 100644 +--- a/src/tests/cmocka/test_sysdb_subdomains.c ++++ b/src/tests/cmocka/test_sysdb_subdomains.c +@@ -60,9 +60,6 @@ struct subdom_test_ctx { + static int test_sysdb_subdom_setup(void **state) + { + struct subdom_test_ctx *test_ctx; +- struct sss_test_conf_param params[] = { +- { NULL, NULL }, /* Sentinel */ +- }; + + assert_true(leak_check_setup()); + +@@ -74,7 +71,7 @@ static int test_sysdb_subdom_setup(void **state) + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, +- TEST_ID_PROVIDER, params); ++ TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + *state = test_ctx; +diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c +index e950f88631e4c78573bbb7290edfe94b5ced57cd..f5aab73f001e8fdece1f85de987d6711a459e6aa 100644 +--- a/src/tests/cmocka/test_sysdb_ts_cache.c ++++ b/src/tests/cmocka/test_sysdb_ts_cache.c +@@ -74,9 +74,6 @@ const char *domains[] = { TEST_DOM1_NAME, + static int test_sysdb_ts_setup(void **state) + { + struct sysdb_ts_test_ctx *test_ctx; +- struct sss_test_conf_param params[] = { +- { NULL, NULL }, /* Sentinel */ +- }; + + assert_true(leak_check_setup()); + +@@ -88,7 +85,7 @@ static int test_sysdb_ts_setup(void **state) + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, +- TEST_ID_PROVIDER, params); ++ TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + check_leaks_push(test_ctx); +diff --git a/src/tests/common.h b/src/tests/common.h +index b49cfea9b73d8b4b7b61c721912de9fd2c0ccf13..c06568d3820ab92ffd47b5c206c300842e8f8a39 100644 +--- a/src/tests/common.h ++++ b/src/tests/common.h +@@ -92,7 +92,7 @@ create_multidom_test_ctx(TALLOC_CTX *mem_ctx, + const char *cdb_file, + const char **domains, + const char *id_provider, +- struct sss_test_conf_param *params); ++ struct sss_test_conf_param **params); + + struct sss_test_ctx * + create_dom_test_ctx(TALLOC_CTX *mem_ctx, +diff --git a/src/tests/common_dom.c b/src/tests/common_dom.c +index f1a92cc99f3423d5d7ef10327013a5972940c792..def28d5101efe9990c963a4180d9fb2bd6f71b42 100644 +--- a/src/tests/common_dom.c ++++ b/src/tests/common_dom.c +@@ -231,7 +231,7 @@ create_multidom_test_ctx(TALLOC_CTX *mem_ctx, + const char *cdb_file, + const char **domains, + const char *id_provider, +- struct sss_test_conf_param *params) ++ struct sss_test_conf_param **params) + { + struct sss_domain_info *domain = NULL; + struct sss_test_ctx *test_ctx = NULL; +@@ -255,7 +255,7 @@ create_multidom_test_ctx(TALLOC_CTX *mem_ctx, + /* create confdb objects for the domains */ + for (i = 0; domains[i] != NULL; i++) { + ret = mock_confdb_domain(test_ctx, test_ctx->confdb, tests_path, +- domains[i], id_provider, params, ++ domains[i], id_provider, params != NULL ? params[i] : NULL, + (cdb_path == NULL ? &cdb_path : NULL)); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize confdb domain " +@@ -302,7 +302,7 @@ create_dom_test_ctx(TALLOC_CTX *mem_ctx, + const char *domains[] = {domain_name, NULL}; + + return create_multidom_test_ctx(mem_ctx, tests_path, confdb_path, domains, +- id_provider, params); ++ id_provider, ¶ms); + } + + void test_multidom_suite_cleanup(const char *tests_path, +-- +2.9.3 + diff --git a/0021-TESTS-Tests-for-sdap_search_initgr_user_in_batch.patch b/0021-TESTS-Tests-for-sdap_search_initgr_user_in_batch.patch new file mode 100644 index 0000000..a74e69f --- /dev/null +++ b/0021-TESTS-Tests-for-sdap_search_initgr_user_in_batch.patch @@ -0,0 +1,609 @@ +From 0b7ded15e53b3f31f1570c366f04bc41e5761929 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20=C4=8Cech?= +Date: Tue, 10 Jan 2017 14:01:45 +0100 +Subject: [PATCH 21/79] TESTS: Tests for sdap_search_initgr_user_in_batch + +This patch provides tests for core logic of +sdap_search_initgr_user_in_batch() function. This function replaces +old approach with sysdb_try_to_find_expected_dn() function. + +Resolves: +https://fedorahosted.org/sssd/ticket/3230 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Sumit Bose +--- + Makefile.am | 22 ++ + src/tests/cmocka/test_sdap_initgr.c | 540 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 562 insertions(+) + create mode 100644 src/tests/cmocka/test_sdap_initgr.c + +diff --git a/Makefile.am b/Makefile.am +index 6d21af8e8c455622d8c4c8b4e325789c4c1e34cb..9dd2060c6615b1c23ae8adb61886341bcdc49560 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -289,6 +289,7 @@ non_interactive_cmocka_based_tests += \ + ad_access_filter_tests \ + ad_gpo_tests \ + ad_common_tests \ ++ test_sdap_initgr \ + test_ad_subdom \ + test_ipa_subdom_server \ + $(NULL) +@@ -2862,6 +2863,27 @@ test_fo_srv_LDADD = \ + libsss_test_common.la \ + $(NULL) + ++test_sdap_initgr_SOURCES = \ ++ src/tests/cmocka/common_mock_sdap.c \ ++ src/tests/cmocka/common_mock_sysdb_objects.c \ ++ src/tests/cmocka/test_sdap_initgr.c \ ++ $(NULL) ++test_sdap_initgr_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NDR_NBT_CFLAGS) \ ++ $(NULL) ++test_sdap_initgr_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(POPT_LIBS) \ ++ $(TALLOC_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_ldap_common.la \ ++ libsss_ad_tests.la \ ++ libsss_idmap.la \ ++ libsss_test_common.la \ ++ libdlopen_test_providers.la \ ++ $(NULL) ++ + test_ad_subdom_SOURCES = \ + src/tests/cmocka/test_ad_subdomains.c \ + $(NULL) +diff --git a/src/tests/cmocka/test_sdap_initgr.c b/src/tests/cmocka/test_sdap_initgr.c +new file mode 100644 +index 0000000000000000000000000000000000000000..28c6ae33ef3dd2a343711b339554492c899dd7b5 +--- /dev/null ++++ b/src/tests/cmocka/test_sdap_initgr.c +@@ -0,0 +1,540 @@ ++/* ++ Authors: ++ Petr Čech ++ ++ 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 ++#include ++#include ++#include ++#include ++ ++#include "tests/cmocka/common_mock.h" ++#include "tests/cmocka/common_mock_sysdb_objects.h" ++#include "tests/cmocka/common_mock_sdap.h" ++#include "providers/ad/ad_common.h" ++ ++#include "providers/ad/ad_opts.c" ++#include "providers/ldap/sdap_async_initgroups.c" ++ ++/* Declarations from providers/ldap/sdap_async_initgroups.c */ ++struct sdap_get_initgr_state; ++static int sdap_search_initgr_user_in_batch(struct sdap_get_initgr_state *state, ++ struct sysdb_attrs **users, ++ size_t count); ++ ++#define TESTS_PATH "tp_" BASE_FILE_STEM ++#define TEST_CONF_DB "test_sdap_initgr_conf.ldb" ++#define TEST_ID_PROVIDER "ldap" ++ ++#define TEST_DOM1_NAME "domain.test.com" ++#define TEST_DOM2_NAME "subdom1.domain.test.com" ++#define TEST_DOM3_NAME "another_domain.test.com" ++ ++#define OBJECT_BASE_DN1 "dc=domain,dc=test,dc=com,cn=sysdb" ++#define OBJECT_BASE_DN2 "dc=subdom1,dc=domain,dc=test,dc=com,cn=sysdb" ++#define OBJECT_BASE_DN3 "dc=another_domain,dc=test,dc=com,cn=sysdb" ++ ++#define TEST_USER_1 "test_user_1" ++#define TEST_USER_2 "test_user_2" ++#define TEST_USER_3 "test_user_3" ++ ++const char *domains[] = { TEST_DOM1_NAME, ++ TEST_DOM2_NAME, ++ TEST_DOM3_NAME, ++ NULL }; ++ ++const char *object_bases[] = { OBJECT_BASE_DN1, ++ OBJECT_BASE_DN2, ++ OBJECT_BASE_DN3, ++ NULL }; ++ ++const char *test_users[] = { TEST_USER_1, ++ TEST_USER_2, ++ TEST_USER_3, ++ NULL }; ++ ++/* ====================== Utilities =============================== */ ++ ++struct test_sdap_initgr_ctx { ++ struct sss_test_ctx *tctx; ++}; ++ ++static struct passwd **get_users(TALLOC_CTX *ctx) ++{ ++ struct passwd **passwds = NULL; ++ char *homedir = NULL; ++ size_t user_count = 0; ++ ++ for (int i = 0; test_users[i] != NULL; i++) { ++ user_count++; ++ } ++ passwds = talloc_array(ctx, struct passwd *, user_count); ++ assert_non_null(passwds); ++ ++ for (int i = 0; i < user_count; i++) { ++ passwds[i] = talloc(passwds, struct passwd); ++ assert_non_null(passwds[i]); ++ ++ homedir = talloc_strdup_append(homedir, "/home/"); ++ homedir = talloc_strdup_append(homedir, test_users[i]); ++ ++ passwds[i]->pw_name = discard_const(test_users[i]); ++ passwds[i]->pw_uid = 567 + i; ++ passwds[i]->pw_gid = 890 + i; ++ passwds[i]->pw_dir = talloc_strdup(passwds[i], homedir); ++ passwds[i]->pw_gecos = discard_const(test_users[i]); ++ passwds[i]->pw_shell = discard_const("/bin/sh"); ++ passwds[i]->pw_passwd = discard_const("*"); ++ ++ talloc_zfree(homedir); ++ } ++ ++ return passwds; ++} ++ ++static struct sss_test_conf_param **get_params(TALLOC_CTX *ctx) ++{ ++ struct sss_test_conf_param **params; ++ char *user_base_dn = NULL; ++ char *group_base_dn = NULL; ++ size_t base_count = 0; ++ ++ for (int i = 0; object_bases[i] != NULL; i++) { ++ base_count++; ++ } ++ ++ params = talloc_array(ctx, struct sss_test_conf_param *, base_count + 1); ++ assert_non_null(params); ++ ++ for (int i = 0; i < base_count; i++) { ++ params[i] = talloc(params, struct sss_test_conf_param); ++ assert_non_null(params[i]); ++ ++ user_base_dn = talloc_strdup_append(user_base_dn, "cn=users,"); ++ user_base_dn = talloc_strdup_append(user_base_dn, object_bases[i]); ++ ++ group_base_dn = talloc_strdup_append(group_base_dn, "cn=groups,"); ++ group_base_dn = talloc_strdup_append(group_base_dn, object_bases[i]); ++ ++ params[i] = talloc_array(params[i], struct sss_test_conf_param, 5); ++ params[i][0].key = "ldap_schema"; ++ params[i][0].value = "rfc2307bis"; ++ params[i][1].key = "ldap_search_base"; ++ params[i][1].value = talloc_strdup(params[i], object_bases[i]); ++ params[i][2].key = "ldap_user_search_base"; ++ params[i][2].value = talloc_strdup(params[i], user_base_dn); ++ params[i][3].key = "ldap_group_search_base"; ++ params[i][3].value = talloc_strdup(params[i], group_base_dn); ++ params[i][4].key = NULL; ++ params[i][4].value = NULL; ++ ++ talloc_zfree(user_base_dn); ++ talloc_zfree(group_base_dn); ++ } ++ ++ return params; ++} ++ ++struct sss_domain_info *get_domain_info(struct sss_domain_info *domain, ++ const char *domain_name) ++{ ++ struct sss_domain_info *dom = domain; ++ ++ while(dom != NULL) { ++ if (strcmp(dom->name, domain_name) == 0) { ++ break; ++ } ++ dom = dom->next; ++ } ++ ++ return dom; ++} ++ ++struct sdap_get_initgr_state *prepare_state(struct test_sdap_initgr_ctx *ctx, ++ const char **domain_names) ++{ ++ struct sdap_get_initgr_state *state; ++ struct sss_domain_info *dom_info = NULL; ++ struct sss_domain_info *recent_dom_info = NULL; ++ ++ state = talloc_zero(ctx->tctx, struct sdap_get_initgr_state); ++ assert_non_null(state); ++ ++ for (int i=0; domain_names[i] != NULL; i++) { ++ dom_info = get_domain_info(ctx->tctx->dom, domain_names[i]); ++ assert_non_null(dom_info); ++ ++ if (i == 0) { ++ state->dom = dom_info; ++ recent_dom_info = state->dom; ++ } else { ++ recent_dom_info->next = dom_info; ++ recent_dom_info = recent_dom_info->next; ++ } ++ } ++ assert_non_null(state->dom); ++ assert_non_null(recent_dom_info); ++ recent_dom_info->next = NULL; ++ ++ state->opts = mock_sdap_options_ldap(state, state->dom, ++ ctx->tctx->confdb, ++ ctx->tctx->conf_dom_path); ++ assert_non_null(state->opts); ++ ++ return state; ++} ++ ++/* TODO: This function is copied from test_nss_srv.c ++ * It could be fine move both to one place, ++ * for example src/tests/common_sysdb.c ++ */ ++static errno_t store_user(TALLOC_CTX *ctx, ++ struct sss_domain_info *dom, ++ struct passwd *user, ++ struct sysdb_attrs *attrs, ++ time_t cache_update) ++{ ++ errno_t ret; ++ char *fqname; ++ ++ fqname = sss_create_internal_fqname(ctx, ++ user->pw_name, ++ dom->name); ++ if (fqname == NULL) { ++ return ENOMEM; ++ } ++ ++ /* Prime the cache with a valid user */ ++ ret = sysdb_store_user(dom, ++ fqname, ++ user->pw_passwd, ++ user->pw_uid, ++ user->pw_gid, ++ user->pw_gecos, ++ user->pw_dir, ++ user->pw_shell, ++ NULL, attrs, ++ NULL, 300, cache_update); ++ talloc_free(fqname); ++ ++ return ret; ++} ++ ++/* ====================== Setup =============================== */ ++ ++static int test_sdap_initgr_setup_one_domain(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ struct sss_test_conf_param **params; ++ ++ assert_true(leak_check_setup()); ++ ++ test_ctx = talloc_zero(global_talloc_context, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ params = get_params(test_ctx); ++ assert_non_null(params); ++ ++ test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, ++ TEST_CONF_DB, domains[0], ++ TEST_ID_PROVIDER, params[0]); ++ assert_non_null(test_ctx->tctx); ++ ++ check_leaks_push(test_ctx); ++ *state = test_ctx; ++ return 0; ++} ++ ++static int test_sdap_initgr_setup_multi_domains(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ struct sss_test_conf_param **params; ++ ++ assert_true(leak_check_setup()); ++ ++ test_ctx = talloc_zero(global_talloc_context, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ params = get_params(test_ctx); ++ assert_non_null(params); ++ ++ test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, ++ TEST_CONF_DB, domains, ++ TEST_ID_PROVIDER, params); ++ assert_non_null(test_ctx->tctx); ++ ++ check_leaks_push(test_ctx); ++ *state = test_ctx; ++ return 0; ++} ++ ++static int test_sdap_initgr_setup_other_multi_domains(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ struct sss_test_conf_param **params; ++ const char *domains_vith_other[] = { TEST_DOM1_NAME, ++ TEST_DOM3_NAME, ++ NULL }; ++ ++ assert_true(leak_check_setup()); ++ ++ test_ctx = talloc_zero(global_talloc_context, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ params = get_params(test_ctx); ++ assert_non_null(params); ++ ++ test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, ++ TEST_CONF_DB, domains_vith_other, ++ TEST_ID_PROVIDER, params); ++ assert_non_null(test_ctx->tctx); ++ ++ check_leaks_push(test_ctx); ++ *state = test_ctx; ++ return 0; ++} ++ ++static int test_sdap_initgr_teardown(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ ++ test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ assert_true(check_leaks_pop(test_ctx) == true); ++ talloc_free(test_ctx); ++ assert_true(leak_check_teardown()); ++ return 0; ++} ++ ++/* ====================== The tests =============================== */ ++ ++static void test_user_is_on_batch(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ struct sdap_get_initgr_state *initgr_state; ++ const char *domains_set[] = { domains[0], NULL }; ++ struct sss_domain_info *dom1_info = NULL; ++ struct sss_domain_info *dom2_info = NULL; ++ struct passwd **passwd_users; ++ struct sysdb_attrs **users; ++ const char *user_name; ++ errno_t ret; ++ ++ test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ dom1_info = get_domain_info(test_ctx->tctx->dom, domains[0]); ++ assert_non_null(dom1_info); ++ dom2_info = get_domain_info(test_ctx->tctx->dom, domains[1]); ++ assert_non_null(dom2_info); ++ ++ initgr_state = prepare_state(test_ctx, domains_set); ++ assert_non_null(initgr_state); ++ ++ passwd_users = get_users(test_ctx); ++ assert_non_null(passwd_users); ++ ++ ret = store_user(test_ctx, dom1_info, passwd_users[0], NULL, 0); ++ assert_int_equal(ret, 0); ++ ret = store_user(test_ctx, dom2_info, passwd_users[1], NULL, 0); ++ assert_int_equal(ret, 0); ++ ++ users = talloc_array(test_ctx, struct sysdb_attrs *, 2); ++ users[0] = mock_sysdb_user(users, object_bases[0], ++ passwd_users[0]->pw_uid, ++ passwd_users[0]->pw_name); ++ users[1] = mock_sysdb_user(users, object_bases[1], ++ passwd_users[1]->pw_uid, ++ passwd_users[1]->pw_name); ++ ++ ret = sdap_search_initgr_user_in_batch(initgr_state, users, 2); ++ assert_int_equal(ret, 0); ++ ++ ret = sysdb_attrs_get_string(initgr_state->orig_user, "name", &user_name); ++ assert_int_equal(ret, 0); ++ assert_string_equal(user_name, passwd_users[0]->pw_name); ++ ++ talloc_zfree(initgr_state); ++ talloc_zfree(passwd_users); ++ talloc_zfree(users); ++} ++ ++static void test_user_is_from_subdomain(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ struct sdap_get_initgr_state *initgr_state; ++ const char *domains_set[] = { domains[0], NULL }; ++ struct sss_domain_info *dom_info = NULL; ++ struct passwd **passwd_users; ++ struct sysdb_attrs **users; ++ const char *user_name; ++ errno_t ret; ++ ++ test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ dom_info = get_domain_info(test_ctx->tctx->dom, domains[0]); ++ assert_non_null(dom_info); ++ ++ initgr_state = prepare_state(test_ctx, domains_set); ++ assert_non_null(initgr_state); ++ ++ passwd_users = get_users(test_ctx); ++ assert_non_null(passwd_users); ++ ++ ret = store_user(test_ctx, dom_info, passwd_users[0], NULL, 0); ++ assert_int_equal(ret, 0); ++ ++ users = talloc_array(test_ctx, struct sysdb_attrs *, 1); ++ users[0] = mock_sysdb_user(users, object_bases[1], ++ passwd_users[1]->pw_uid, ++ passwd_users[1]->pw_name); ++ ++ const char *original_dn = NULL; ++ ret = sysdb_attrs_get_string(users[0], SYSDB_ORIG_DN, &original_dn); ++ ++ ret = sdap_search_initgr_user_in_batch(initgr_state, users, 1); ++ assert_int_equal(ret, 0); ++ ++ ret = sysdb_attrs_get_string(initgr_state->orig_user, "name", &user_name); ++ assert_int_equal(ret, 0); ++ assert_string_equal(user_name, passwd_users[1]->pw_name); ++ ++ talloc_zfree(initgr_state); ++ talloc_zfree(passwd_users); ++ talloc_zfree(users); ++} ++ ++static void test_user_is_from_another_domain(void **state) ++{ ++ struct test_sdap_initgr_ctx *test_ctx; ++ struct sdap_get_initgr_state *initgr_state; ++ const char *domains_set[] = { domains[0], domains[2], NULL }; ++ struct sss_domain_info *dom_info = NULL; ++ struct sss_domain_info *other_dom_info = NULL; ++ struct sdap_domain *other_sdom = NULL; ++ struct passwd **passwd_users; ++ struct sysdb_attrs **users; ++ errno_t ret; ++ ++ test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); ++ assert_non_null(test_ctx); ++ ++ dom_info = get_domain_info(test_ctx->tctx->dom, domains[0]); ++ assert_non_null(dom_info); ++ ++ initgr_state = prepare_state(test_ctx, domains_set); ++ assert_non_null(initgr_state); ++ ++ other_dom_info = get_domain_info(test_ctx->tctx->dom, domains[2]); ++ assert_non_null(other_dom_info); ++ ++ ret = sdap_domain_add(initgr_state->opts, other_dom_info, &other_sdom); ++ assert_int_equal(ret, EOK); ++ ++ talloc_zfree(other_sdom->search_bases); ++ other_sdom->search_bases = talloc_array(other_sdom, ++ struct sdap_search_base *, 2); ++ assert_non_null(other_sdom->search_bases); ++ other_sdom->search_bases[1] = NULL; ++ ++ ret = sdap_create_search_base(other_sdom, object_bases[2], ++ LDAP_SCOPE_SUBTREE, NULL, ++ &other_sdom->search_bases[0]); ++ assert_int_equal(ret, EOK); ++ ++ passwd_users = get_users(test_ctx); ++ assert_non_null(passwd_users); ++ ++ ret = store_user(test_ctx, dom_info, passwd_users[0], NULL, 0); ++ assert_int_equal(ret, 0); ++ ++ users = talloc_array(test_ctx, struct sysdb_attrs *, 1); ++ users[0] = mock_sysdb_user(users, object_bases[2], ++ passwd_users[2]->pw_uid, ++ passwd_users[2]->pw_name); ++ ++ ret = sdap_search_initgr_user_in_batch(initgr_state, users, 1); ++ assert_int_equal(ret, EINVAL); ++ ++ talloc_zfree(initgr_state); ++ talloc_zfree(passwd_users); ++ talloc_zfree(users); ++} ++ ++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_user_is_on_batch, ++ test_sdap_initgr_setup_multi_domains, ++ test_sdap_initgr_teardown), ++ cmocka_unit_test_setup_teardown(test_user_is_from_subdomain, ++ test_sdap_initgr_setup_one_domain, ++ test_sdap_initgr_teardown), ++ cmocka_unit_test_setup_teardown(test_user_is_from_another_domain, ++ test_sdap_initgr_setup_other_multi_domains, ++ test_sdap_initgr_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); ++ ++ /* 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(); ++ ++ test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); ++ test_dom_suite_setup(TESTS_PATH); ++ ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ if (rv == 0) { ++ test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); ++ } ++ ++ return rv; ++} +-- +2.9.3 + diff --git a/0022-ssh-fix-number-of-output-certificates.patch b/0022-ssh-fix-number-of-output-certificates.patch new file mode 100644 index 0000000..9af8340 --- /dev/null +++ b/0022-ssh-fix-number-of-output-certificates.patch @@ -0,0 +1,31 @@ +From d8c459feab7659a51c23c941fea486867c2b9dae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 17 Jan 2017 12:00:31 +0100 +Subject: [PATCH 22/79] ssh: fix number of output certificates + +SSH responder returned invalid number of certificates when +original ad pubkey attribute was not empty. Since we always +return all certificates to the client we should add number +of results to the output not override it. + +Reviewed-by: Jakub Hrozek +--- + src/responder/ssh/sshsrv_cmd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c +index 2e64893dfc2018727e6fc5fb80b47bd7eb1fac58..bd6270d0f1b62323ef7d140193351fb8585ce2ec 100644 +--- a/src/responder/ssh/sshsrv_cmd.c ++++ b/src/responder/ssh/sshsrv_cmd.c +@@ -1012,7 +1012,7 @@ ssh_cmd_build_reply(struct ssh_cmd_ctx *cmd_ctx) + el_orig = ldb_msg_find_element(cmd_ctx->result, + ORIGINALAD_PREFIX SYSDB_SSH_PUBKEY); + if (el_orig) { +- count = el_orig->num_values; ++ count += el_orig->num_values; + } + + if (DOM_HAS_VIEWS(cmd_ctx->domain)) { +-- +2.9.3 + diff --git a/0023-ssh-do-not-create-again-fq-name.patch b/0023-ssh-do-not-create-again-fq-name.patch new file mode 100644 index 0000000..d198f53 --- /dev/null +++ b/0023-ssh-do-not-create-again-fq-name.patch @@ -0,0 +1,76 @@ +From e33744e8cc82390153c94ace53c16f72365b9fd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 17 Jan 2017 11:58:06 +0100 +Subject: [PATCH 23/79] ssh: do not create again fq name + +We store fully qualified name in sysdb so there is no need to append +the domain part again which result in name@domain@domain string. +This field is not actually used in ssh client so it doesn't cause +any issue but we should stay correct here. + +Reviewed-by: Jakub Hrozek +--- + src/responder/ssh/sshsrv_cmd.c | 20 ++++++-------------- + 1 file changed, 6 insertions(+), 14 deletions(-) + +diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c +index bd6270d0f1b62323ef7d140193351fb8585ce2ec..195d5763e9c5f4f9ff2f2f5ac49cd856d9198e7a 100644 +--- a/src/responder/ssh/sshsrv_cmd.c ++++ b/src/responder/ssh/sshsrv_cmd.c +@@ -982,8 +982,7 @@ ssh_cmd_build_reply(struct ssh_cmd_ctx *cmd_ctx) + struct ldb_message_element *el_user_cert_keys = NULL; + uint32_t count = 0; + const char *name; +- char *fqname; +- uint32_t fqname_len; ++ uint32_t name_len; + TALLOC_CTX *tmp_ctx; + struct ssh_ctx *ssh_ctx; + struct cli_protocol *pctx; +@@ -1060,38 +1059,31 @@ ssh_cmd_build_reply(struct ssh_cmd_ctx *cmd_ctx) + goto done; + } + +- fqname = talloc_asprintf(cmd_ctx, "%s@%s", +- name, cmd_ctx->domain->name); +- if (!fqname) { +- ret = ENOMEM; +- goto done; +- } +- +- fqname_len = strlen(fqname)+1; ++ name_len = strlen(name) + 1; + + ret = decode_and_add_base64_data(cmd_ctx, el, false, ssh_ctx, +- fqname_len, fqname, &c); ++ name_len, name, &c); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); + goto done; + } + + ret = decode_and_add_base64_data(cmd_ctx, el_orig, false, ssh_ctx, +- fqname_len, fqname, &c); ++ name_len, name, &c); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); + goto done; + } + + ret = decode_and_add_base64_data(cmd_ctx, el_override, false, ssh_ctx, +- fqname_len, fqname, &c); ++ name_len, name, &c); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); + goto done; + } + + ret = decode_and_add_base64_data(cmd_ctx, el_user_cert_keys, true, ssh_ctx, +- fqname_len, fqname, &c); ++ name_len, name, &c); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); + goto done; +-- +2.9.3 + diff --git a/0024-sss_parse_inp_send-provide-default_domain-as-paramet.patch b/0024-sss_parse_inp_send-provide-default_domain-as-paramet.patch new file mode 100644 index 0000000..3b99750 --- /dev/null +++ b/0024-sss_parse_inp_send-provide-default_domain-as-paramet.patch @@ -0,0 +1,198 @@ +From 2b5704cd96a085b99d3b0d4f80f4414adc134750 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 3 Feb 2017 12:44:15 +0100 +Subject: [PATCH 24/79] sss_parse_inp_send: provide default_domain as parameter + +It is not always desirable to consider default_domain from configuration +but expect none instead. For example when we search host certificates. + +This is currently not used in this patch since host lookups parse +name directly with sss_parse_name but it will be used in the next +patch. + +Reviewed-by: Jakub Hrozek +--- + src/responder/common/cache_req/cache_req.c | 3 ++- + src/responder/common/responder.h | 5 ++++- + src/responder/common/responder_get_domains.c | 30 ++++++++++++++++++++++++---- + src/responder/ifp/ifpsrv_cmd.c | 2 +- + src/tests/cmocka/common_mock_resp_dp.c | 4 +++- + src/tests/cmocka/test_responder_common.c | 12 +++++++---- + 6 files changed, 44 insertions(+), 12 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index f546e6130a181f7b6d3fc1aca8ad0766e8a7f19d..e5026e1a869064fe81cc04e3b2bbd8c4cefec304 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -415,7 +415,8 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Parsing input name [%s]\n", cr->data->name.input); + +- subreq = sss_parse_inp_send(mem_ctx, cr->rctx, cr->data->name.input); ++ subreq = sss_parse_inp_send(mem_ctx, cr->rctx, cr->rctx->default_domain, ++ cr->data->name.input); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return ENOMEM; +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index d1fa532be3402214842da50e037f5f8d149631fb..c387c6ec326c612eef8798673c1c70c67efd5452 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -347,8 +347,11 @@ errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count, + uid_t *allowed_uids); + + struct tevent_req * +-sss_parse_inp_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx, ++sss_parse_inp_send(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *default_domain, + const char *rawinp); ++ + errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **_name, char **_domname); + +diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c +index cc7b99f30046569547a08f83e46cbbe9d6c19897..0f39d107dad6c458785b1b8d708e60d7c34e3901 100644 +--- a/src/responder/common/responder_get_domains.c ++++ b/src/responder/common/responder_get_domains.c +@@ -443,6 +443,7 @@ errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx, + + struct sss_parse_inp_state { + struct resp_ctx *rctx; ++ const char *default_domain; + const char *rawinp; + + char *name; +@@ -453,7 +454,9 @@ struct sss_parse_inp_state { + static void sss_parse_inp_done(struct tevent_req *subreq); + + struct tevent_req * +-sss_parse_inp_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx, ++sss_parse_inp_send(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *default_domain, + const char *rawinp) + { + errno_t ret; +@@ -465,16 +468,35 @@ sss_parse_inp_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx, + if (req == NULL) { + return NULL; + } +- state->rawinp = rawinp; ++ ++ if (rawinp == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Empty input!\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ + state->rctx = rctx; + ++ state->rawinp = talloc_strdup(state, rawinp); ++ if (state->rawinp == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ++ state->default_domain = talloc_strdup(state, default_domain); ++ if (default_domain != NULL && state->default_domain == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ + /* If the subdomains haven't been checked yet, we need to always + * attach to the post-startup subdomain request and only then parse + * the input. Otherwise, we might not be able to parse input with a + * flat domain name specifier */ + if (rctx->get_domains_last_call.tv_sec > 0) { + ret = sss_parse_name_for_domains(state, rctx->domains, +- rctx->default_domain, rawinp, ++ default_domain, rawinp, + &state->domname, &state->name); + if (ret == EOK) { + /* Was able to use cached domains */ +@@ -532,7 +554,7 @@ static void sss_parse_inp_done(struct tevent_req *subreq) + state->error = ERR_OK; + + ret = sss_parse_name_for_domains(state, state->rctx->domains, +- state->rctx->default_domain, ++ state->default_domain, + state->rawinp, + &state->domname, &state->name); + if (ret == EAGAIN && state->domname != NULL && state->name == NULL) { +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index 23f410a19ea985b4fcfcf34a770d37ea9a864e67..07edcddffa1091f8bbcf79a25962aadc791bb890 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -453,7 +453,7 @@ ifp_user_get_attr_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx, + state->ncache = ncache; + state->search_type = search_type; + +- subreq = sss_parse_inp_send(req, rctx, inp); ++ subreq = sss_parse_inp_send(req, rctx, rctx->default_domain, inp); + if (subreq == NULL) { + ret = ENOMEM; + goto done; +diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c +index f62606eb8a33b6417bbd32a7dccdbeaabd05818f..0b6870346c00954a3e2accf8f21625a14da8afb5 100644 +--- a/src/tests/cmocka/common_mock_resp_dp.c ++++ b/src/tests/cmocka/common_mock_resp_dp.c +@@ -80,7 +80,9 @@ void mock_account_recv_simple(void) + } + + struct tevent_req * +-sss_parse_inp_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx, ++sss_parse_inp_send(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ const char *default_domain, + const char *rawinp) + { + return test_req_succeed_send(mem_ctx, rctx->ev); +diff --git a/src/tests/cmocka/test_responder_common.c b/src/tests/cmocka/test_responder_common.c +index b25f8a8efcded664ed61be4d5a67b0f2e3adf327..fb7e4ee500570319999e6e85ee14a05cddea8de3 100644 +--- a/src/tests/cmocka/test_responder_common.c ++++ b/src/tests/cmocka/test_responder_common.c +@@ -192,7 +192,8 @@ void parse_inp_simple(void **state) + + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_REAL); + +- req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, NAME); ++ req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, ++ parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_simple_done, parse_inp_ctx); + +@@ -213,7 +214,8 @@ void parse_inp_call_dp(void **state) + /* The second one will succeed as the domains are up-to-date */ + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_REAL); + +- req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, NAME); ++ req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, ++ parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_simple_done, parse_inp_ctx); + +@@ -235,7 +237,8 @@ void parse_inp_call_attach(void **state) + * as the domains are up-to-date */ + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_REAL); + +- req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, NAME); ++ req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, ++ parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_simple_done, parse_inp_ctx); + +@@ -271,7 +274,8 @@ void parse_inp_call_neg(void **state) + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_parse_name_for_domains, EINVAL); + +- req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, NAME); ++ req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, ++ parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_neg_done, parse_inp_ctx); + +-- +2.9.3 + diff --git a/0025-cache_req-add-ability-to-not-use-default-domain-suff.patch b/0025-cache_req-add-ability-to-not-use-default-domain-suff.patch new file mode 100644 index 0000000..3d70df6 --- /dev/null +++ b/0025-cache_req-add-ability-to-not-use-default-domain-suff.patch @@ -0,0 +1,310 @@ +From ddfd1900b26c66a062457d4fcc1a48bafd3eadf6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 3 Feb 2017 13:04:23 +0100 +Subject: [PATCH 25/79] cache_req: add ability to not use default domain suffix + +This will be used in the next plugin "host by name" where +it is not desirable to use default domain suffix if set. + +Reviewed-by: Jakub Hrozek +--- + src/responder/common/cache_req/cache_req.c | 8 +++++++- + src/responder/common/cache_req/cache_req_plugin.h | 5 +++++ + src/responder/common/cache_req/plugins/cache_req_enum_groups.c | 1 + + src/responder/common/cache_req/plugins/cache_req_enum_svc.c | 1 + + src/responder/common/cache_req/plugins/cache_req_enum_users.c | 1 + + .../common/cache_req/plugins/cache_req_group_by_filter.c | 1 + + src/responder/common/cache_req/plugins/cache_req_group_by_id.c | 1 + + src/responder/common/cache_req/plugins/cache_req_group_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 + + src/responder/common/cache_req/plugins/cache_req_object_by_id.c | 1 + + src/responder/common/cache_req/plugins/cache_req_object_by_name.c | 1 + + src/responder/common/cache_req/plugins/cache_req_object_by_sid.c | 1 + + src/responder/common/cache_req/plugins/cache_req_svc_by_name.c | 1 + + src/responder/common/cache_req/plugins/cache_req_svc_by_port.c | 1 + + src/responder/common/cache_req/plugins/cache_req_user_by_cert.c | 1 + + src/responder/common/cache_req/plugins/cache_req_user_by_filter.c | 1 + + src/responder/common/cache_req/plugins/cache_req_user_by_id.c | 1 + + src/responder/common/cache_req/plugins/cache_req_user_by_name.c | 1 + + src/responder/common/cache_req/plugins/cache_req_user_by_upn.c | 1 + + 21 files changed, 31 insertions(+), 1 deletion(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index e5026e1a869064fe81cc04e3b2bbd8c4cefec304..aed8f1b225899a1c470407e259d2068ef62922b7 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -400,6 +400,7 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + const char *domain) + { + struct tevent_req *subreq; ++ const char *default_domain; + + if (cr->data->name.input == NULL) { + /* Input was not name, there is no need to process it further. */ +@@ -411,11 +412,16 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx, + return cache_req_set_name(cr, cr->data->name.input); + } + ++ default_domain = NULL; ++ if (!cr->plugin->ignore_default_domain) { ++ default_domain = cr->rctx->default_domain; ++ } ++ + /* Parse name since it may contain a domain name. */ + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, + "Parsing input name [%s]\n", cr->data->name.input); + +- subreq = sss_parse_inp_send(mem_ctx, cr->rctx, cr->rctx->default_domain, ++ subreq = sss_parse_inp_send(mem_ctx, cr->rctx, default_domain, + cr->data->name.input); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); +diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h +index e4d5eef91672a83e1ced47b394368a457acfbcb8..59ef8bad1697e094f729c53f33bda4f1d825cdff 100644 +--- a/src/responder/common/cache_req/cache_req_plugin.h ++++ b/src/responder/common/cache_req/cache_req_plugin.h +@@ -157,6 +157,11 @@ struct cache_req_plugin { + bool parse_name; + + /** ++ * True if default domain suffix should be ignored when parsing name. ++ */ ++ bool ignore_default_domain; ++ ++ /** + * True if we always contact data provider. + */ + bool bypass_cache; +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 de4bd968b18920cde0630dbd5142ce99d3b70a3e..2056dc2ccdadef98772402bde45aef8e043a0e76 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 +@@ -64,6 +64,7 @@ const struct cache_req_plugin cache_req_enum_groups = { + .dp_type = SSS_DP_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, +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 c83564fdce8abc237a3a4dbe7a88b4bc6c2baaff..e850212977bb26dc13b900f6e5908865fffa59b0 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 +@@ -65,6 +65,7 @@ const struct cache_req_plugin cache_req_enum_svc = { + .dp_type = SSS_DP_SERVICES, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, +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 c4eeed7463cca6ecd17fe8042d62f4b72da46e68..2adeddb6b4bea044371f168f5d39aecc1f06cc45 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 +@@ -64,6 +64,7 @@ const struct cache_req_plugin cache_req_enum_users = { + .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = true, +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 1619cf7bdd6ad7ef7c1ea71ef0dd8f24611c1a6e..bc42eb7db0830ba31649c2cbb9525dfd1f7b1fae 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 +@@ -119,6 +119,7 @@ const struct cache_req_plugin cache_req_group_by_filter = { + .dp_type = SSS_DP_WILDCARD_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = false, +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 293994fa1e22a23b7ff19c50050e5c6c25274b5d..e48588087eafde68a4a85c546cf08e90eb6c7605 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 +@@ -107,6 +107,7 @@ const struct cache_req_plugin cache_req_group_by_id = { + .dp_type = SSS_DP_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 c88dbd4566297da98d306e20deb7f7c64c7991a4..962b38866a1408bbdff556e20df5a69b0d4bbba0 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 +@@ -157,6 +157,7 @@ const struct cache_req_plugin cache_req_group_by_name = { + .dp_type = SSS_DP_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 9575ae70731875979f924dbf948222ed705fd923..d2f03cbea0780e4e0b88d56fcfbcf8903bcb3c85 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 +@@ -172,6 +172,7 @@ const struct cache_req_plugin cache_req_initgroups_by_name = { + .dp_type = SSS_DP_INITGROUPS, + .attr_expiration = SYSDB_INITGR_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, +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 7a0b96b19f487e046c32235e02ec0fdbc7baa211..9b2d07d4afa98cbfca4a62f944b744f01897a0ee 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 +@@ -108,6 +108,7 @@ const struct cache_req_plugin cache_req_initgroups_by_upn = { + .dp_type = SSS_DP_INITGROUPS, + .attr_expiration = SYSDB_INITGR_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, +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 15549adeff9e038387b21b6349b18683c14afe65..5b19edeb2952b83406ff20d001dd7d24449f69c9 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 +@@ -116,6 +116,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = { + .dp_type = SSS_DP_NETGR, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 b8ad3b5e76cbf52fb61e22aa872e51e7f51bbf29..3f47807616054c644e27e4c240ad7c4b752a563e 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 +@@ -99,6 +99,7 @@ const struct cache_req_plugin cache_req_object_by_id = { + .dp_type = SSS_DP_USER_AND_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 1ec906c7ad0c0f2d327667c697a96f2c2735d066..6829d0ec97c147aafda46b6eace25b97a28e626a 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 +@@ -192,6 +192,7 @@ const struct cache_req_plugin cache_req_object_by_name = { + .dp_type = SSS_DP_USER_AND_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 35cb74f61fab0c72dda68c8f95e30be9127f938f..6a6eb8e72c52c069935ca4e612e60f602c7b91bd 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 +@@ -109,6 +109,7 @@ const struct cache_req_plugin cache_req_object_by_sid = { + .dp_type = SSS_DP_SECID, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 4de27571c199baeeec1064f6d9b626fef08212c7..9562354ed3a453e3aec7264bb32dbd5273fb0927 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 +@@ -140,6 +140,7 @@ const struct cache_req_plugin cache_req_svc_by_name = { + .dp_type = SSS_DP_SERVICES, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, +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 1b17c71352678f7dfae830bea3ab3909fd62c564..55117492f6f8aa6a4e31c1e23862215255cdf660 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 +@@ -113,6 +113,7 @@ const struct cache_req_plugin cache_req_svc_by_port = { + .dp_type = SSS_DP_SERVICES, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = false, + .search_all_domains = false, +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 9a1bcc6aa1225c27362b11b9321994f65261d5cb..5203d3f94421715b711bcd1e01b7a42737b6fe41 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 +@@ -83,6 +83,7 @@ const struct cache_req_plugin cache_req_user_by_cert = { + .dp_type = SSS_DP_CERT, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 ee9f60bf682629acf3b2ec3d16a3ed075084480d..4c328a5d900e37de0f3396a8c2f1c937360ce081 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 +@@ -119,6 +119,7 @@ const struct cache_req_plugin cache_req_user_by_filter = { + .dp_type = SSS_DP_WILDCARD_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = true, + .only_one_result = false, + .search_all_domains = false, +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 d710986d1102af4422d29a9943c903f23bea8b9e..d794d248b1e9b11cd41210b8180823e3a2565847 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 +@@ -107,6 +107,7 @@ const struct cache_req_plugin cache_req_user_by_id = { + .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 46dd9434b34536b72b0966f53ab341c09542f16c..9ee7bef1cc904d25d156b3f64e039e47be58d1cc 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 +@@ -157,6 +157,7 @@ const struct cache_req_plugin cache_req_user_by_name = { + .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +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 9d1e703d623cd830c2ab6db6e835c4bec49f57e5..4c6e6bcd056392abb729d416d406f28c28cdaa77 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 +@@ -112,6 +112,7 @@ const struct cache_req_plugin cache_req_user_by_upn = { + .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, ++ .ignore_default_domain = false, + .bypass_cache = false, + .only_one_result = true, + .search_all_domains = false, +-- +2.9.3 + diff --git a/0026-cache_req-search-user-by-name-with-attrs.patch b/0026-cache_req-search-user-by-name-with-attrs.patch new file mode 100644 index 0000000..d5a2fe2 --- /dev/null +++ b/0026-cache_req-search-user-by-name-with-attrs.patch @@ -0,0 +1,87 @@ +From 7723e79f5a1fad4201360199037aea33f655bab6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 11 Jan 2017 11:36:50 +0100 +Subject: [PATCH 26/79] cache_req: search user by name with attrs + +Sometime is is desirable to aquire more attribute from user object +than SYSDB_PW_ATTRS set. such as user's public key. + +Reviewed-by: Jakub Hrozek +--- + src/responder/common/cache_req/cache_req.h | 13 +++++++++ + .../cache_req/plugins/cache_req_user_by_name.c | 31 ++++++++++++++++++++-- + 2 files changed, 42 insertions(+), 2 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h +index 7700091078c96698a5aaf12cf0d50f259cd186d8..2740c21ee0e390c64d94fedd6ab2cb7483cfe302 100644 +--- a/src/responder/common/cache_req/cache_req.h ++++ b/src/responder/common/cache_req/cache_req.h +@@ -186,6 +186,19 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx, + cache_req_single_domain_recv(mem_ctx, req, _result) + + struct tevent_req * ++cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct resp_ctx *rctx, ++ struct sss_nc_ctx *ncache, ++ int cache_refresh_percent, ++ const char *domain, ++ const char *name, ++ const char **attrs); ++ ++#define cache_req_user_by_name_attrs_recv(mem_ctx, req, _result) \ ++ cache_req_single_domain_recv(mem_ctx, req, _result) ++ ++struct tevent_req * + cache_req_user_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, +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 9ee7bef1cc904d25d156b3f64e039e47be58d1cc..3f343870c7e7c28ac72f4e94272c6dee281b963c 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 +@@ -105,8 +105,13 @@ cache_req_user_by_name_lookup(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_result **_result) + { +- return sysdb_getpwnam_with_views(mem_ctx, domain, data->name.lookup, +- _result); ++ if (data->attrs == NULL) { ++ return sysdb_getpwnam_with_views(mem_ctx, domain, data->name.lookup, ++ _result); ++ } ++ ++ return sysdb_get_user_attr_with_views(mem_ctx, domain, data->name.lookup, ++ data->attrs, _result); + } + + static errno_t +@@ -196,3 +201,25 @@ 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); + } ++ ++struct tevent_req * ++cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct resp_ctx *rctx, ++ struct sss_nc_ctx *ncache, ++ int cache_refresh_percent, ++ const char *domain, ++ const char *name, ++ const char **attrs) ++{ ++ struct cache_req_data *data; ++ ++ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_USER_BY_NAME, ++ name, attrs); ++ if (data == NULL) { ++ return NULL; ++ } ++ ++ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, ++ cache_refresh_percent, domain, data); ++} +-- +2.9.3 + diff --git a/0027-cache_req-add-api-to-create-ldb_result-from-message.patch b/0027-cache_req-add-api-to-create-ldb_result-from-message.patch new file mode 100644 index 0000000..96bacb3 --- /dev/null +++ b/0027-cache_req-add-api-to-create-ldb_result-from-message.patch @@ -0,0 +1,99 @@ +From 9492b3b26ac0b1898f836094074a9d8b38916e13 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 17 Jan 2017 14:11:32 +0100 +Subject: [PATCH 27/79] cache_req: add api to create ldb_result from message + +Some sysdb methods doesn't return ldb_result as output but return +ldb_message instead. Changing sysdb to be consistent is too big +so I added this helper function that will wrap resulting message +into ldb_result. + +Reviewed-by: Jakub Hrozek +--- + src/responder/common/cache_req/cache_req.c | 47 ++++++++++++++++++------------ + 1 file changed, 28 insertions(+), 19 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index aed8f1b225899a1c470407e259d2068ef62922b7..31c220b3a66db815100b10a4f2e04388c13eaf78 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -78,7 +78,6 @@ static errno_t cache_req_set_plugin(struct cache_req *cr, + } + + cr->reqname = plugin->name; +- cr->dp_type = plugin->dp_type; + cr->plugin = plugin; + + CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, "Setting \"%s\" plugin\n", +@@ -820,16 +819,11 @@ cache_req_create_result(TALLOC_CTX *mem_ctx, + return result; + } + +-struct cache_req_result * +-cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx, +- struct sss_domain_info *domain, +- struct ldb_message *ldb_msg, +- const char *lookup_name, +- const char *well_known_domain) ++struct ldb_result * ++cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx, ++ struct ldb_message *ldb_msg) + { +- struct cache_req_result *result; + struct ldb_result *ldb_result; +- errno_t ret; + + if (ldb_msg == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n"); +@@ -847,23 +841,38 @@ cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx, + ldb_result->count = 1; + ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *, 2); + if (ldb_result->msgs == NULL) { +- ret = ENOMEM; +- goto done; ++ talloc_free(ldb_result); ++ return NULL; + } + + ldb_result->msgs[0] = talloc_steal(ldb_result->msgs, ldb_msg); + ++ return ldb_result; ++} ++ ++struct cache_req_result * ++cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *domain, ++ struct ldb_message *ldb_msg, ++ const char *lookup_name, ++ const char *well_known_domain) ++{ ++ struct cache_req_result *result; ++ struct ldb_result *ldb_result; ++ ++ if (ldb_msg == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n"); ++ return NULL; ++ } ++ ++ ldb_result = cache_req_create_ldb_result_from_msg(mem_ctx, ldb_msg); ++ if (ldb_result == NULL) { ++ return NULL; ++ } ++ + result = cache_req_create_result(mem_ctx, domain, ldb_result, + lookup_name, well_known_domain); + if (result == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- ret = EOK; +- +-done: +- if (ret != EOK) { + talloc_free(ldb_result); + return NULL; + } +-- +2.9.3 + diff --git a/0028-cache_req-move-dp-request-to-plugin.patch b/0028-cache_req-move-dp-request-to-plugin.patch new file mode 100644 index 0000000..f48b8e6 --- /dev/null +++ b/0028-cache_req-move-dp-request-to-plugin.patch @@ -0,0 +1,1426 @@ +From 4df7aec645f87342f3a5146062abcb15f71f4fd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 17 Jan 2017 14:11:58 +0100 +Subject: [PATCH 28/79] cache_req: move dp request to plugin + +This will allow to use cache req even for object that do not use +account request such as hosts. + +Reviewed-by: Jakub Hrozek +--- + src/responder/common/cache_req/cache_req_plugin.h | 39 ++++---- + src/responder/common/cache_req/cache_req_private.h | 13 ++- + src/responder/common/cache_req/cache_req_search.c | 106 ++++----------------- + .../common/cache_req/plugins/cache_req_common.c | 40 ++++++++ + .../cache_req/plugins/cache_req_enum_groups.c | 24 ++--- + .../common/cache_req/plugins/cache_req_enum_svc.c | 24 ++--- + .../cache_req/plugins/cache_req_enum_users.c | 24 ++--- + .../cache_req/plugins/cache_req_group_by_filter.c | 25 +++-- + .../cache_req/plugins/cache_req_group_by_id.c | 26 ++++- + .../cache_req/plugins/cache_req_group_by_name.c | 26 ++++- + .../plugins/cache_req_initgroups_by_name.c | 26 ++++- + .../plugins/cache_req_initgroups_by_upn.c | 25 +++-- + .../cache_req/plugins/cache_req_netgroup_by_name.c | 25 +++-- + .../cache_req/plugins/cache_req_object_by_id.c | 25 +++-- + .../cache_req/plugins/cache_req_object_by_name.c | 25 +++-- + .../cache_req/plugins/cache_req_object_by_sid.c | 24 ++--- + .../cache_req/plugins/cache_req_svc_by_name.c | 25 +++-- + .../cache_req/plugins/cache_req_svc_by_port.c | 25 +++-- + .../cache_req/plugins/cache_req_user_by_cert.c | 24 ++--- + .../cache_req/plugins/cache_req_user_by_filter.c | 25 +++-- + .../cache_req/plugins/cache_req_user_by_id.c | 26 ++++- + .../cache_req/plugins/cache_req_user_by_name.c | 26 ++++- + .../cache_req/plugins/cache_req_user_by_upn.c | 25 +++-- + src/tests/cmocka/common_mock_resp_dp.c | 31 +++++- + src/tests/cmocka/test_responder_cache_req.c | 28 +++--- + 25 files changed, 400 insertions(+), 332 deletions(-) + +diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h +index 59ef8bad1697e094f729c53f33bda4f1d825cdff..61e346dacfe0d180fb2aae354bc7867093276ab0 100644 +--- a/src/responder/common/cache_req/cache_req_plugin.h ++++ b/src/responder/common/cache_req/cache_req_plugin.h +@@ -117,18 +117,29 @@ typedef errno_t + struct ldb_result **_result); + + /** +- * Return parameters for Data Provider request. ++ * Send Data Provider request. + * +- * @return EOK If everything went fine. +- * @return Other errno code in case of an error. ++ * @return Tevent request on success. ++ * @return NULL on error. + */ +-typedef errno_t +-(*cache_req_dpreq_params_fn)(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag); ++typedef struct tevent_req * ++(*cache_req_dp_send_fn)(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result); ++ ++/** ++ * Process result of Data Provider request. ++ * ++ * Do not free subreq! It will be freed in the caller. ++ * ++ * @return True if data provider request succeeded. ++ * @return False if there was an error. ++ */ ++typedef bool ++(*cache_req_dp_recv_fn)(struct tevent_req *subreq, ++ struct cache_req *cr); + + struct cache_req_plugin { + /** +@@ -137,11 +148,6 @@ struct cache_req_plugin { + const char *name; + + /** +- * Data provider request type. +- */ +- enum sss_dp_acct_type dp_type; +- +- /** + * Expiration timestamp attribute name. + */ + const char *attr_expiration; +@@ -202,7 +208,8 @@ struct cache_req_plugin { + cache_req_ncache_check_fn ncache_check_fn; + cache_req_ncache_add_fn ncache_add_fn; + cache_req_lookup_fn lookup_fn; +- cache_req_dpreq_params_fn dpreq_params_fn; ++ cache_req_dp_send_fn dp_send_fn; ++ cache_req_dp_recv_fn dp_recv_fn; + }; + + extern const struct cache_req_plugin cache_req_user_by_name; +diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h +index e79fe869b0d62fe949701911c8fe3a2e062ccd31..b544b739e92552189f806f4675ff28689b91ce66 100644 +--- a/src/responder/common/cache_req/cache_req_private.h ++++ b/src/responder/common/cache_req/cache_req_private.h +@@ -38,11 +38,6 @@ struct cache_req { + struct sss_nc_ctx *ncache; + int midpoint; + +- /* Data Provider request type resolved from @type. +- * FIXME: This is currently needed for data provider calls. We should +- * refactor responder_dp.c to get rid of this member. */ +- enum sss_dp_acct_type dp_type; +- + /* Domain related informations. */ + struct sss_domain_info *domain; + +@@ -116,6 +111,10 @@ cache_req_create_result(TALLOC_CTX *mem_ctx, + const char *lookup_name, + const char *well_known_domain); + ++struct ldb_result * ++cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx, ++ struct ldb_message *ldb_msg); ++ + struct cache_req_result * + cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, +@@ -132,4 +131,8 @@ cache_req_well_known_sid_result(TALLOC_CTX *mem_ctx, + const char *sid, + const char *name); + ++bool ++cache_req_common_dp_recv(struct tevent_req *subreq, ++ struct cache_req *cr); ++ + #endif /* _CACHE_REQ_PRIVATE_H_ */ +diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c +index aabff389a72aaee4638bc9d34a5ad35de58b4923..eed82cf262fad50ec1fe1649f48be664d750b590 100644 +--- a/src/responder/common/cache_req/cache_req_search.c ++++ b/src/responder/common/cache_req/cache_req_search.c +@@ -138,70 +138,6 @@ done: + return ret; + } + +-static errno_t cache_req_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) +-{ +- errno_t ret; +- +- if (cr->plugin->dpreq_params_fn == NULL) { +- CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, +- "Bug: No dpreq params function specified\n"); +- return ERR_INTERNAL; +- } +- +- +- CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, +- "Creating DP request parameters\n"); +- +- ret = cr->plugin->dpreq_params_fn(mem_ctx, cr, result, _string, _id, _flag); +- if (ret != EOK) { +- CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, +- "Unable to create DP request parameters [%d]: %s\n", +- ret, sss_strerror(ret)); +- return ret; +- } +- +- return EOK; +-} +- +-static bool cache_req_search_process_dp(TALLOC_CTX *mem_ctx, +- struct tevent_req *subreq, +- struct cache_req *cr) +-{ +- char *err_msg; +- dbus_uint16_t err_maj; +- dbus_uint32_t err_min; +- errno_t ret; +- +- ret = sss_dp_get_account_recv(mem_ctx, subreq, &err_maj, &err_min, &err_msg); +- talloc_zfree(subreq); +- if (ret != EOK) { +- CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, +- "Could not get account info [%d]: %s\n", +- ret, sss_strerror(ret)); +- CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, +- "Due to an error we will return cached data\n"); +- +- return false; +- } +- +- if (err_maj) { +- CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, +- "Data Provider Error: %u, %u, %s\n", +- (unsigned int)err_maj, (unsigned int)err_min, err_msg); +- CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, +- "Due to an error we will return cached data\n"); +- +- return false; +- } +- +- return true; +-} +- + static enum cache_object_status + cache_req_expiration_status(struct cache_req *cr, + struct ldb_result *result) +@@ -316,19 +252,10 @@ static errno_t cache_req_search_dp(struct tevent_req *req, + { + struct cache_req_search_state *state; + struct tevent_req *subreq; +- const char *extra_flag; +- const char *search_str; +- uint32_t search_id; + errno_t ret; + + state = tevent_req_data(req, struct cache_req_search_state); + +- ret = cache_req_dpreq_params(state, state->cr, state->result, +- &search_str, &search_id, &extra_flag); +- if (ret != EOK) { +- return ret; +- } +- + switch (status) { + case CACHE_OBJECT_MIDPOINT: + /* Out of band update. The calling function will return the cached +@@ -339,10 +266,10 @@ static errno_t cache_req_search_dp(struct tevent_req *req, + "Performing midpoint cache update of [%s]\n", + state->cr->debugobj); + +- subreq = sss_dp_get_account_send(state->cr->rctx, state->cr->rctx, +- state->cr->domain, true, +- state->cr->dp_type, +- search_str, search_id, extra_flag); ++ subreq = state->cr->plugin->dp_send_fn(state->cr, state->cr, ++ state->cr->data, ++ state->cr->domain, ++ state->result); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory sending out-of-band " + "data provider request\n"); +@@ -351,31 +278,37 @@ static errno_t cache_req_search_dp(struct tevent_req *req, + tevent_req_set_callback(subreq, cache_req_search_oob_done, req); + } + +- return EOK; ++ ret = EOK; ++ break; + case CACHE_OBJECT_EXPIRED: + case CACHE_OBJECT_MISSING: + CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, + "Looking up [%s] in data provider\n", + state->cr->debugobj); + +- subreq = sss_dp_get_account_send(state, state->cr->rctx, +- state->cr->domain, true, +- state->cr->dp_type, +- search_str, search_id, extra_flag); ++ subreq = state->cr->plugin->dp_send_fn(state->cr, state->cr, ++ state->cr->data, ++ state->cr->domain, ++ state->result); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Out of memory sending data provider request\n"); +- return ENOMEM; ++ ret = ENOMEM; ++ break; + } + + tevent_req_set_callback(subreq, cache_req_search_done, req); +- return EAGAIN; ++ ret = EAGAIN; ++ break; + default: + /* error */ + CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr, + "Unexpected status [%d]\n", status); +- return ret; ++ ret = ERR_INTERNAL; ++ break; + } ++ ++ return ret; + } + + static void cache_req_search_oob_done(struct tevent_req *subreq) +@@ -395,7 +328,8 @@ static void cache_req_search_done(struct tevent_req *subreq) + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct cache_req_search_state); + +- state->dp_success = cache_req_search_process_dp(state, subreq, state->cr); ++ state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr); ++ talloc_zfree(subreq); + + /* Get result from cache again. */ + ret = cache_req_search_cache(state, state->cr, &state->result); +diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c +index 4c0f358faeba1641905fe2a5487b0e49389d2331..b80f310feeebbdbc824db441ff5313632585d3fb 100644 +--- a/src/responder/common/cache_req/plugins/cache_req_common.c ++++ b/src/responder/common/cache_req/plugins/cache_req_common.c +@@ -107,3 +107,43 @@ cache_req_well_known_sid_result(TALLOC_CTX *mem_ctx, + + return result; + } ++ ++bool ++cache_req_common_dp_recv(struct tevent_req *subreq, ++ struct cache_req *cr) ++{ ++ char *err_msg; ++ dbus_uint16_t err_maj; ++ dbus_uint32_t err_min; ++ errno_t ret; ++ bool bret; ++ ++ ret = sss_dp_req_recv(NULL, subreq, &err_maj, &err_min, &err_msg); ++ if (ret != EOK) { ++ CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, ++ "Could not get account info [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, ++ "Due to an error we will return cached data\n"); ++ ++ bret = false; ++ goto done; ++ } ++ ++ if (err_maj) { ++ CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, ++ "Data Provider Error: %u, %u, %s\n", ++ (unsigned int)err_maj, (unsigned int)err_min, err_msg); ++ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, ++ "Due to an error we will return cached data\n"); ++ ++ bret = false; ++ goto done; ++ } ++ ++ bret = true; ++ ++done: ++ talloc_free(err_msg); ++ return bret; ++} +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 2056dc2ccdadef98772402bde45aef8e043a0e76..dbb40c98339cc9295e3678e05340396aff51ac78 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 +@@ -44,24 +44,19 @@ cache_req_enum_groups_lookup(TALLOC_CTX *mem_ctx, + return sysdb_enumgrent_with_views(mem_ctx, domain, _result); + } + +-static errno_t +-cache_req_enum_groups_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_enum_groups_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = NULL; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_GROUP, NULL, 0, NULL); + } + + const struct cache_req_plugin cache_req_enum_groups = { + .name = "Enumerate groups", +- .dp_type = SSS_DP_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -81,7 +76,8 @@ const struct cache_req_plugin cache_req_enum_groups = { + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_enum_groups_lookup, +- .dpreq_params_fn = cache_req_enum_groups_dpreq_params ++ .dp_send_fn = cache_req_enum_groups_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 e850212977bb26dc13b900f6e5908865fffa59b0..2c4917cde750c9063d898c16d3a58ca8c179bc70 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 +@@ -45,24 +45,19 @@ cache_req_enum_svc_lookup(TALLOC_CTX *mem_ctx, + return sysdb_enumservent(mem_ctx, domain, _result); + } + +-static errno_t +-cache_req_enum_svc_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_enum_svc_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = NULL; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_SERVICES, NULL, 0, NULL); + } + + const struct cache_req_plugin cache_req_enum_svc = { + .name = "Enumerate services", +- .dp_type = SSS_DP_SERVICES, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -82,7 +77,8 @@ const struct cache_req_plugin cache_req_enum_svc = { + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_enum_svc_lookup, +- .dpreq_params_fn = cache_req_enum_svc_dpreq_params ++ .dp_send_fn = cache_req_enum_svc_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 2adeddb6b4bea044371f168f5d39aecc1f06cc45..3b1a85841e3ed853cd329dfa9d762cb7a05cbd43 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 +@@ -44,24 +44,19 @@ cache_req_enum_users_lookup(TALLOC_CTX *mem_ctx, + return sysdb_enumpwent_with_views(mem_ctx, domain, _result); + } + +-static errno_t +-cache_req_enum_users_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_enum_users_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = NULL; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_USER, NULL, 0, NULL); + } + + const struct cache_req_plugin cache_req_enum_users = { + .name = "Enumerate users", +- .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -81,7 +76,8 @@ const struct cache_req_plugin cache_req_enum_users = { + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_enum_users_lookup, +- .dpreq_params_fn = cache_req_enum_users_dpreq_params ++ .dp_send_fn = cache_req_enum_users_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 bc42eb7db0830ba31649c2cbb9525dfd1f7b1fae..88e1137a3976308aaf404b684c6d88cc43708bca 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 +@@ -99,24 +99,20 @@ cache_req_group_by_filter_lookup(TALLOC_CTX *mem_ctx, + return ret; + } + +-static errno_t +-cache_req_group_by_filter_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_group_by_filter_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = cr->data->id; +- *_string = cr->data->name.lookup; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_WILDCARD_GROUP, ++ cr->data->name.lookup, cr->data->id, NULL); + } + + const struct cache_req_plugin cache_req_group_by_filter = { + .name = "Group by filter", +- .dp_type = SSS_DP_WILDCARD_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -136,7 +132,8 @@ const struct cache_req_plugin cache_req_group_by_filter = { + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_group_by_filter_lookup, +- .dpreq_params_fn = cache_req_group_by_filter_dpreq_params ++ .dp_send_fn = cache_req_group_by_filter_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 e48588087eafde68a4a85c546cf08e90eb6c7605..e98f76f8cd20742b81ae247df61db159d2584a17 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 +@@ -102,9 +102,30 @@ cache_req_group_by_id_dpreq_params(TALLOC_CTX *mem_ctx, + return EOK; + } + ++static struct tevent_req * ++cache_req_group_by_id_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) ++{ ++ const char *string; ++ const char *flag; ++ uint32_t id; ++ errno_t ret; ++ ++ ret = cache_req_group_by_id_dpreq_params(mem_ctx, cr, result, ++ &string, &id, &flag); ++ if (ret != EOK) { ++ return NULL; ++ } ++ ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_GROUP, string, id, flag); ++} ++ + const struct cache_req_plugin cache_req_group_by_id = { + .name = "Group by ID", +- .dp_type = SSS_DP_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -124,7 +145,8 @@ const struct cache_req_plugin cache_req_group_by_id = { + .ncache_check_fn = cache_req_group_by_id_ncache_check, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_group_by_id_lookup, +- .dpreq_params_fn = cache_req_group_by_id_dpreq_params ++ .dp_send_fn = cache_req_group_by_id_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 962b38866a1408bbdff556e20df5a69b0d4bbba0..be1eb9bd8552156d777e934b0be397b0e66df7cc 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 +@@ -152,9 +152,30 @@ cache_req_group_by_name_dpreq_params(TALLOC_CTX *mem_ctx, + return EOK; + } + ++static struct tevent_req * ++cache_req_group_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) ++{ ++ const char *string; ++ const char *flag; ++ uint32_t id; ++ errno_t ret; ++ ++ ret = cache_req_group_by_name_dpreq_params(mem_ctx, cr, result, ++ &string, &id, &flag); ++ if (ret != EOK) { ++ return NULL; ++ } ++ ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_GROUP, string, id, flag); ++} ++ + const struct cache_req_plugin cache_req_group_by_name = { + .name = "Group by name", +- .dp_type = SSS_DP_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -174,7 +195,8 @@ const struct cache_req_plugin cache_req_group_by_name = { + .ncache_check_fn = cache_req_group_by_name_ncache_check, + .ncache_add_fn = cache_req_group_by_name_ncache_add, + .lookup_fn = cache_req_group_by_name_lookup, +- .dpreq_params_fn = cache_req_group_by_name_dpreq_params ++ .dp_send_fn = cache_req_group_by_name_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 d2f03cbea0780e4e0b88d56fcfbcf8903bcb3c85..10fb67cbf6e78cfae33bc7208585cb80ea6a9bc4 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 +@@ -167,9 +167,30 @@ cache_req_initgroups_by_name_dpreq_params(TALLOC_CTX *mem_ctx, + return EOK; + } + ++static struct tevent_req * ++cache_req_initgroups_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) ++{ ++ const char *string; ++ const char *flag; ++ uint32_t id; ++ errno_t ret; ++ ++ ret = cache_req_initgroups_by_name_dpreq_params(mem_ctx, cr, result, ++ &string, &id, &flag); ++ if (ret != EOK) { ++ return NULL; ++ } ++ ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_INITGROUPS, string, id, flag); ++} ++ + const struct cache_req_plugin cache_req_initgroups_by_name = { + .name = "Initgroups by name", +- .dp_type = SSS_DP_INITGROUPS, + .attr_expiration = SYSDB_INITGR_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -189,7 +210,8 @@ const struct cache_req_plugin cache_req_initgroups_by_name = { + .ncache_check_fn = cache_req_initgroups_by_name_ncache_check, + .ncache_add_fn = cache_req_initgroups_by_name_ncache_add, + .lookup_fn = cache_req_initgroups_by_name_lookup, +- .dpreq_params_fn = cache_req_initgroups_by_name_dpreq_params ++ .dp_send_fn = cache_req_initgroups_by_name_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 9b2d07d4afa98cbfca4a62f944b744f01897a0ee..266ec7b8a28d496d9603bd9b6cdfef268ffa8559 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 +@@ -88,24 +88,20 @@ cache_req_initgroups_by_upn_lookup(TALLOC_CTX *mem_ctx, + _result); + } + +-static errno_t +-cache_req_initgroups_by_upn_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_initgroups_by_upn_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->name.lookup; +- *_flag = EXTRA_NAME_IS_UPN; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_INITGROUPS, cr->data->name.lookup, ++ 0, EXTRA_NAME_IS_UPN); + } + + const struct cache_req_plugin cache_req_initgroups_by_upn = { + .name = "Initgroups by UPN", +- .dp_type = SSS_DP_INITGROUPS, + .attr_expiration = SYSDB_INITGR_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -125,5 +121,6 @@ const struct cache_req_plugin cache_req_initgroups_by_upn = { + .ncache_check_fn = cache_req_initgroups_by_upn_ncache_check, + .ncache_add_fn = cache_req_initgroups_by_upn_ncache_add, + .lookup_fn = cache_req_initgroups_by_upn_lookup, +- .dpreq_params_fn = cache_req_initgroups_by_upn_dpreq_params ++ .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 5b19edeb2952b83406ff20d001dd7d24449f69c9..bc6fc9a8f476f97cc4bc5004bc19ba35258a2b6d 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 +@@ -96,24 +96,20 @@ cache_req_netgroup_by_name_lookup(TALLOC_CTX *mem_ctx, + return sysdb_getnetgr(mem_ctx, domain, data->name.lookup, _result); + } + +-static errno_t +-cache_req_netgroup_by_name_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_netgroup_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->name.lookup; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_NETGR, cr->data->name.lookup, ++ 0, NULL); + } + + const struct cache_req_plugin cache_req_netgroup_by_name = { + .name = "Netgroup by name", +- .dp_type = SSS_DP_NETGR, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -133,7 +129,8 @@ const struct cache_req_plugin cache_req_netgroup_by_name = { + .ncache_check_fn = cache_req_netgroup_by_name_ncache_check, + .ncache_add_fn = cache_req_netgroup_by_name_ncache_add, + .lookup_fn = cache_req_netgroup_by_name_lookup, +- .dpreq_params_fn = cache_req_netgroup_by_name_dpreq_params ++ .dp_send_fn = cache_req_netgroup_by_name_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 3f47807616054c644e27e4c240ad7c4b752a563e..046e313c83d1d4c75237b047be779201b8a5d3c0 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 +@@ -79,24 +79,20 @@ cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx, + data->attrs, _result); + } + +-static errno_t +-cache_req_object_by_id_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_object_by_id_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = cr->data->id; +- *_string = NULL; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_USER_AND_GROUP, NULL, ++ cr->data->id, NULL); + } + + const struct cache_req_plugin cache_req_object_by_id = { + .name = "Object by ID", +- .dp_type = SSS_DP_USER_AND_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -116,7 +112,8 @@ const struct cache_req_plugin cache_req_object_by_id = { + .ncache_check_fn = cache_req_object_by_id_ncache_check, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_object_by_id_lookup, +- .dpreq_params_fn = cache_req_object_by_id_dpreq_params ++ .dp_send_fn = cache_req_object_by_id_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 6829d0ec97c147aafda46b6eace25b97a28e626a..2b2caeea172b23b1b1b226def5d926e26c5c0090 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 +@@ -172,24 +172,20 @@ cache_req_object_by_name_lookup(TALLOC_CTX *mem_ctx, + data->attrs, _result); + } + +-static errno_t +-cache_req_object_by_name_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_object_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->name.lookup; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_USER_AND_GROUP, ++ cr->data->name.lookup, 0, NULL); + } + + const struct cache_req_plugin cache_req_object_by_name = { + .name = "Object by name", +- .dp_type = SSS_DP_USER_AND_GROUP, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -209,7 +205,8 @@ const struct cache_req_plugin cache_req_object_by_name = { + .ncache_check_fn = cache_req_object_by_name_ncache_check, + .ncache_add_fn = cache_req_object_by_name_ncache_add, + .lookup_fn = cache_req_object_by_name_lookup, +- .dpreq_params_fn = cache_req_object_by_name_dpreq_params ++ .dp_send_fn = cache_req_object_by_name_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 6a6eb8e72c52c069935ca4e612e60f602c7b91bd..ab577663111cfd424e7f46308b2621af7f1ca264 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 +@@ -89,24 +89,19 @@ cache_req_object_by_sid_lookup(TALLOC_CTX *mem_ctx, + _result); + } + +-static errno_t +-cache_req_object_by_sid_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_object_by_sid_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->sid; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_SECID, cr->data->sid, 0, NULL); + } + + const struct cache_req_plugin cache_req_object_by_sid = { + .name = "Object by SID", +- .dp_type = SSS_DP_SECID, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -126,7 +121,8 @@ const struct cache_req_plugin cache_req_object_by_sid = { + .ncache_check_fn = cache_req_object_by_sid_ncache_check, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_object_by_sid_lookup, +- .dpreq_params_fn = cache_req_object_by_sid_dpreq_params ++ .dp_send_fn = cache_req_object_by_sid_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 9562354ed3a453e3aec7264bb32dbd5273fb0927..cbb186df04c7ca7c02dceb98bd5700c984285a4d 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 +@@ -120,24 +120,20 @@ cache_req_svc_by_name_lookup(TALLOC_CTX *mem_ctx, + data->svc.protocol.lookup, _result); + } + +-static errno_t +-cache_req_svc_by_name_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_svc_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->svc.name->lookup; +- *_flag = cr->data->svc.protocol.lookup; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_SERVICES, cr->data->svc.name->lookup, ++ 0, cr->data->svc.protocol.lookup); + } + + const struct cache_req_plugin cache_req_svc_by_name = { + .name = "Service by name", +- .dp_type = SSS_DP_SERVICES, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -157,7 +153,8 @@ const struct cache_req_plugin cache_req_svc_by_name = { + .ncache_check_fn = cache_req_svc_by_name_ncache_check, + .ncache_add_fn = cache_req_svc_by_name_ncache_add, + .lookup_fn = cache_req_svc_by_name_lookup, +- .dpreq_params_fn = cache_req_svc_by_name_dpreq_params ++ .dp_send_fn = cache_req_svc_by_name_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 55117492f6f8aa6a4e31c1e23862215255cdf660..1da23d4505a1dad3b2425a996134f8298c03518a 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 +@@ -93,24 +93,20 @@ cache_req_svc_by_port_lookup(TALLOC_CTX *mem_ctx, + data->svc.protocol.lookup, _result); + } + +-static errno_t +-cache_req_svc_by_port_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_svc_by_port_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = cr->data->svc.port; +- *_string = NULL; +- *_flag = cr->data->svc.protocol.lookup; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_SERVICES, NULL, cr->data->svc.port, ++ cr->data->svc.protocol.lookup); + } + + const struct cache_req_plugin cache_req_svc_by_port = { + .name = "Service by port", +- .dp_type = SSS_DP_SERVICES, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -130,7 +126,8 @@ const struct cache_req_plugin cache_req_svc_by_port = { + .ncache_check_fn = cache_req_svc_by_port_ncache_check, + .ncache_add_fn = cache_req_svc_by_port_ncache_add, + .lookup_fn = cache_req_svc_by_port_lookup, +- .dpreq_params_fn = cache_req_svc_by_port_dpreq_params ++ .dp_send_fn = cache_req_svc_by_port_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 5203d3f94421715b711bcd1e01b7a42737b6fe41..cead8524ba68bc18b29649a2d44774da43d676a3 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 +@@ -63,24 +63,19 @@ cache_req_user_by_cert_lookup(TALLOC_CTX *mem_ctx, + _result); + } + +-static errno_t +-cache_req_user_by_cert_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_user_by_cert_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->cert; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_CERT, cr->data->cert, 0, NULL); + } + + const struct cache_req_plugin cache_req_user_by_cert = { + .name = "User by certificate", +- .dp_type = SSS_DP_CERT, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -100,7 +95,8 @@ const struct cache_req_plugin cache_req_user_by_cert = { + .ncache_check_fn = cache_req_user_by_cert_ncache_check, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_user_by_cert_lookup, +- .dpreq_params_fn = cache_req_user_by_cert_dpreq_params ++ .dp_send_fn = cache_req_user_by_cert_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 4c328a5d900e37de0f3396a8c2f1c937360ce081..ee7e69399e318b9835f1623bddc635bf09aa7a1c 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 +@@ -99,24 +99,20 @@ cache_req_user_by_filter_lookup(TALLOC_CTX *mem_ctx, + return ret; + } + +-static errno_t +-cache_req_user_by_filter_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_user_by_filter_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = cr->data->id; +- *_string = cr->data->name.lookup; +- *_flag = NULL; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_WILDCARD_USER, cr->data->name.lookup, ++ cr->data->id, NULL); + } + + const struct cache_req_plugin cache_req_user_by_filter = { + .name = "User by filter", +- .dp_type = SSS_DP_WILDCARD_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -136,7 +132,8 @@ const struct cache_req_plugin cache_req_user_by_filter = { + .ncache_check_fn = NULL, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_user_by_filter_lookup, +- .dpreq_params_fn = cache_req_user_by_filter_dpreq_params ++ .dp_send_fn = cache_req_user_by_filter_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 d794d248b1e9b11cd41210b8180823e3a2565847..fa783714b7c67ca029d18a223b64a3a69e3e6929 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 +@@ -102,9 +102,30 @@ cache_req_user_by_id_dpreq_params(TALLOC_CTX *mem_ctx, + return EOK; + } + ++static struct tevent_req * ++cache_req_user_by_id_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) ++{ ++ const char *string; ++ const char *flag; ++ uint32_t id; ++ errno_t ret; ++ ++ ret = cache_req_user_by_id_dpreq_params(mem_ctx, cr, result, ++ &string, &id, &flag); ++ if (ret != EOK) { ++ return NULL; ++ } ++ ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_USER, string, id, flag); ++} ++ + const struct cache_req_plugin cache_req_user_by_id = { + .name = "User by ID", +- .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -124,7 +145,8 @@ const struct cache_req_plugin cache_req_user_by_id = { + .ncache_check_fn = cache_req_user_by_id_ncache_check, + .ncache_add_fn = NULL, + .lookup_fn = cache_req_user_by_id_lookup, +- .dpreq_params_fn = cache_req_user_by_id_dpreq_params ++ .dp_send_fn = cache_req_user_by_id_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 3f343870c7e7c28ac72f4e94272c6dee281b963c..4289f5fd4c79f0e512f0249abe4422589fa800a0 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 +@@ -157,9 +157,30 @@ cache_req_user_by_name_dpreq_params(TALLOC_CTX *mem_ctx, + return EOK; + } + ++static struct tevent_req * ++cache_req_user_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) ++{ ++ const char *string; ++ const char *flag; ++ uint32_t id; ++ errno_t ret; ++ ++ ret = cache_req_user_by_name_dpreq_params(mem_ctx, cr, result, ++ &string, &id, &flag); ++ if (ret != EOK) { ++ return NULL; ++ } ++ ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_USER, string, id, flag); ++} ++ + const struct cache_req_plugin cache_req_user_by_name = { + .name = "User by name", +- .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = false, +@@ -179,7 +200,8 @@ const struct cache_req_plugin cache_req_user_by_name = { + .ncache_check_fn = cache_req_user_by_name_ncache_check, + .ncache_add_fn = cache_req_user_by_name_ncache_add, + .lookup_fn = cache_req_user_by_name_lookup, +- .dpreq_params_fn = cache_req_user_by_name_dpreq_params ++ .dp_send_fn = cache_req_user_by_name_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; + + struct tevent_req * +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 4c6e6bcd056392abb729d416d406f28c28cdaa77..f496479a354517090617806a051a1b2fbc54f18f 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 +@@ -92,24 +92,20 @@ cache_req_user_by_upn_lookup(TALLOC_CTX *mem_ctx, + data->attrs, _result); + } + +-static errno_t +-cache_req_user_by_upn_dpreq_params(TALLOC_CTX *mem_ctx, +- struct cache_req *cr, +- struct ldb_result *result, +- const char **_string, +- uint32_t *_id, +- const char **_flag) ++static struct tevent_req * ++cache_req_user_by_upn_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) + { +- *_id = 0; +- *_string = cr->data->name.lookup; +- *_flag = EXTRA_NAME_IS_UPN; +- +- return EOK; ++ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true, ++ SSS_DP_USER, cr->data->name.lookup, ++ 0, EXTRA_NAME_IS_UPN); + } + + const struct cache_req_plugin cache_req_user_by_upn = { + .name = "User by UPN", +- .dp_type = SSS_DP_USER, + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = false, + .ignore_default_domain = false, +@@ -129,5 +125,6 @@ const struct cache_req_plugin cache_req_user_by_upn = { + .ncache_check_fn = cache_req_user_by_upn_ncache_check, + .ncache_add_fn = cache_req_user_by_upn_ncache_add, + .lookup_fn = cache_req_user_by_upn_lookup, +- .dpreq_params_fn = cache_req_user_by_upn_dpreq_params ++ .dp_send_fn = cache_req_user_by_upn_dp_send, ++ .dp_recv_fn = cache_req_common_dp_recv + }; +diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c +index 0b6870346c00954a3e2accf8f21625a14da8afb5..cbdb65d745a63ae00613001847351d3dba0fe290 100644 +--- a/src/tests/cmocka/common_mock_resp_dp.c ++++ b/src/tests/cmocka/common_mock_resp_dp.c +@@ -61,16 +61,37 @@ sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + return test_request_recv(req); + } + ++errno_t ++sss_dp_req_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ dbus_uint16_t *dp_err, ++ dbus_uint32_t *dp_ret, ++ char **err_msg) ++{ ++ acct_cb_t cb; ++ ++ *dp_err = sss_mock_type(dbus_uint16_t); ++ *dp_ret = sss_mock_type(dbus_uint32_t); ++ *err_msg = sss_mock_ptr_type(char *); ++ ++ cb = sss_mock_ptr_type(acct_cb_t); ++ if (cb) { ++ (cb)(sss_mock_ptr_type(void *)); ++ } ++ ++ return test_request_recv(req); ++} ++ + void mock_account_recv(uint16_t dp_err, uint32_t dp_ret, char *msg, + acct_cb_t acct_cb, void *pvt) + { +- will_return(sss_dp_get_account_recv, dp_err); +- will_return(sss_dp_get_account_recv, dp_ret); +- will_return(sss_dp_get_account_recv, msg); ++ will_return(sss_dp_req_recv, dp_err); ++ will_return(sss_dp_req_recv, dp_ret); ++ will_return(sss_dp_req_recv, msg); + +- will_return(sss_dp_get_account_recv, acct_cb); ++ will_return(sss_dp_req_recv, acct_cb); + if (acct_cb) { +- will_return(sss_dp_get_account_recv, pvt); ++ will_return(sss_dp_req_recv, pvt); + } + } + +diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c +index d4e54cd474b9b22cb4b877d4afe5dca19130e53f..94a902c036dd09ba016b7aa520ce82f23aaa6a35 100644 +--- a/src/tests/cmocka/test_responder_cache_req.c ++++ b/src/tests/cmocka/test_responder_cache_req.c +@@ -530,7 +530,7 @@ void test_user_by_name_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ +@@ -547,7 +547,7 @@ void test_user_by_name_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ +@@ -749,7 +749,7 @@ void test_user_by_upn_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ +@@ -766,7 +766,7 @@ void test_user_by_upn_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ +@@ -904,7 +904,7 @@ void test_user_by_id_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); +@@ -920,7 +920,7 @@ void test_user_by_id_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ENOENT); +@@ -1045,7 +1045,7 @@ void test_group_by_name_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ +@@ -1062,7 +1062,7 @@ void test_group_by_name_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ +@@ -1261,7 +1261,7 @@ void test_group_by_id_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); +@@ -1277,7 +1277,7 @@ void test_group_by_id_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ENOENT); +@@ -1912,7 +1912,7 @@ void test_object_by_sid_user_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, users[0].sid, attrs, 0, ERR_OK); +@@ -1929,7 +1929,7 @@ void test_object_by_sid_user_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, users[0].sid, attrs, 0, ENOENT); +@@ -2068,7 +2068,7 @@ void test_object_by_sid_group_multiple_domains_found(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, groups[0].sid, attrs, 0, ERR_OK); +@@ -2085,7 +2085,7 @@ void test_object_by_sid_group_multiple_domains_notfound(void **state) + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); +- will_return_always(sss_dp_get_account_recv, 0); ++ will_return_always(sss_dp_req_recv, 0); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, groups[0].sid, attrs, 0, ENOENT); +-- +2.9.3 + diff --git a/0029-cache_req-add-host-by-name-search.patch b/0029-cache_req-add-host-by-name-search.patch new file mode 100644 index 0000000..baa4077 --- /dev/null +++ b/0029-cache_req-add-host-by-name-search.patch @@ -0,0 +1,453 @@ +From 53c31b83e4d06ea4c2813eec2f1e647a613b4a2b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 18 Jan 2017 12:12:01 +0100 +Subject: [PATCH 29/79] cache_req: add host by name search + +Reviewed-by: Jakub Hrozek +--- + Makefile.am | 3 +- + src/responder/common/cache_req/cache_req.c | 2 + + src/responder/common/cache_req/cache_req.h | 23 ++++ + src/responder/common/cache_req/cache_req_data.c | 39 +++++++ + src/responder/common/cache_req/cache_req_plugin.h | 1 + + src/responder/common/cache_req/cache_req_private.h | 1 + + .../cache_req/plugins/cache_req_host_by_name.c | 121 +++++++++++++++++++++ + src/responder/common/responder.h | 15 +++ + .../{ssh/sshsrv_dp.c => common/responder_dp_ssh.c} | 3 +- + src/responder/ssh/sshsrv_private.h | 15 --- + src/tests/cmocka/common_mock_resp_dp.c | 33 ++++++ + src/tests/cwrap/Makefile.am | 2 + + 12 files changed, 240 insertions(+), 18 deletions(-) + create mode 100644 src/responder/common/cache_req/plugins/cache_req_host_by_name.c + rename src/responder/{ssh/sshsrv_dp.c => common/responder_dp_ssh.c} (99%) + +diff --git a/Makefile.am b/Makefile.am +index 9dd2060c6615b1c23ae8adb61886341bcdc49560..6592261df87fc4fd0b83aba42e9f5cd12238a6cb 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -513,6 +513,7 @@ SSSD_CACHE_REQ_OBJ = \ + src/responder/common/cache_req/plugins/cache_req_svc_by_name.c \ + src/responder/common/cache_req/plugins/cache_req_svc_by_port.c \ + src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c \ ++ src/responder/common/cache_req/plugins/cache_req_host_by_name.c \ + $(NULL) + + SSSD_RESPONDER_OBJ = \ +@@ -521,6 +522,7 @@ SSSD_RESPONDER_OBJ = \ + src/responder/common/responder_cmd.c \ + src/responder/common/responder_common.c \ + src/responder/common/responder_dp.c \ ++ src/responder/common/responder_dp_ssh.c \ + src/responder/common/responder_packet.c \ + src/responder/common/responder_get_domains.c \ + src/responder/common/responder_utils.c \ +@@ -1331,7 +1333,6 @@ endif + if BUILD_SSH + sssd_ssh_SOURCES = \ + src/responder/ssh/sshsrv.c \ +- src/responder/ssh/sshsrv_dp.c \ + src/responder/ssh/sshsrv_cmd.c \ + $(SSSD_RESPONDER_OBJ) \ + $(NULL) +diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c +index 31c220b3a66db815100b10a4f2e04388c13eaf78..16429c666a6db79afaad52b509fc63d639815b31 100644 +--- a/src/responder/common/cache_req/cache_req.c ++++ b/src/responder/common/cache_req/cache_req.c +@@ -56,6 +56,8 @@ cache_req_get_plugin(enum cache_req_type type) + &cache_req_svc_by_port, + + &cache_req_netgroup_by_name, ++ ++ &cache_req_host_by_name, + }; + + if (type >= CACHE_REQ_SENTINEL) { +diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h +index 2740c21ee0e390c64d94fedd6ab2cb7483cfe302..185558d7d7abd03429e35f391616d249e52c2f76 100644 +--- a/src/responder/common/cache_req/cache_req.h ++++ b/src/responder/common/cache_req/cache_req.h +@@ -52,6 +52,8 @@ enum cache_req_type { + + CACHE_REQ_NETGROUP_BY_NAME, + ++ CACHE_REQ_HOST_BY_NAME, ++ + CACHE_REQ_SENTINEL + }; + +@@ -103,6 +105,13 @@ cache_req_data_svc(TALLOC_CTX *mem_ctx, + const char *protocol, + uint16_t port); + ++struct cache_req_data * ++cache_req_data_host(TALLOC_CTX *mem_ctx, ++ enum cache_req_type type, ++ const char *name, ++ const char *alias, ++ const char **attrs); ++ + /* Output data. */ + + struct cache_req_result { +@@ -377,4 +386,18 @@ cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx, + #define cache_req_netgroup_by_name_recv(mem_ctx, req, _result) \ + cache_req_single_domain_recv(mem_ctx, req, _result) + ++struct tevent_req * ++cache_req_host_by_name_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct resp_ctx *rctx, ++ struct sss_nc_ctx *ncache, ++ int cache_refresh_percent, ++ const char *domain, ++ const char *name, ++ const char *alias, ++ const char **attrs); ++ ++#define cache_req_host_by_name_recv(mem_ctx, req, _result) \ ++ cache_req_single_domain_recv(mem_ctx, req, _result) ++ + #endif /* _CACHE_REQ_H_ */ +diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c +index d0564785f7fc5ffe826b197a41da720e9f26a43a..b2e22ec1bab699ad71978df6905df19908369ff1 100644 +--- a/src/responder/common/cache_req/cache_req_data.c ++++ b/src/responder/common/cache_req/cache_req_data.c +@@ -188,6 +188,29 @@ cache_req_data_create(TALLOC_CTX *mem_ctx, + } + + break; ++ case CACHE_REQ_HOST_BY_NAME: ++ if (input->name.input == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n"); ++ ret = ERR_INTERNAL; ++ goto done; ++ } ++ ++ data->name.input = talloc_strdup(data, input->name.input); ++ if (data->name.input == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ if (input->alias == NULL) { ++ break; ++ } ++ ++ data->alias = talloc_strdup(data, input->alias); ++ if (data->alias == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ break; + case CACHE_REQ_SENTINEL: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid cache request type!\n"); + ret = ERR_INTERNAL; +@@ -318,3 +341,19 @@ cache_req_data_svc(TALLOC_CTX *mem_ctx, + + return cache_req_data_create(mem_ctx, type, &input); + } ++ ++struct cache_req_data * ++cache_req_data_host(TALLOC_CTX *mem_ctx, ++ enum cache_req_type type, ++ const char *name, ++ const char *alias, ++ const char **attrs) ++{ ++ struct cache_req_data input = {0}; ++ ++ input.name.input = name; ++ input.alias = alias; ++ input.attrs = attrs; ++ ++ return cache_req_data_create(mem_ctx, type, &input); ++} +diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h +index 61e346dacfe0d180fb2aae354bc7867093276ab0..e0b619528f6aa31a10a5b48c3c5acc96de90caa1 100644 +--- a/src/responder/common/cache_req/cache_req_plugin.h ++++ b/src/responder/common/cache_req/cache_req_plugin.h +@@ -231,5 +231,6 @@ extern const struct cache_req_plugin cache_req_enum_svc; + extern const struct cache_req_plugin cache_req_svc_by_name; + extern const struct cache_req_plugin cache_req_svc_by_port; + extern const struct cache_req_plugin cache_req_netgroup_by_name; ++extern const struct cache_req_plugin cache_req_host_by_name; + + #endif /* _CACHE_REQ_PLUGIN_H_ */ +diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h +index b544b739e92552189f806f4675ff28689b91ce66..cc473759159fe324e37a4c51dc15ed136f6a09ef 100644 +--- a/src/responder/common/cache_req/cache_req_private.h ++++ b/src/responder/common/cache_req/cache_req_private.h +@@ -76,6 +76,7 @@ struct cache_req_data { + uint32_t id; + const char *cert; + const char *sid; ++ const char *alias; + const char **attrs; + + struct { +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 +new file mode 100644 +index 0000000000000000000000000000000000000000..18511e33bc18e44f418a26764f066ff287092d26 +--- /dev/null ++++ b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c +@@ -0,0 +1,121 @@ ++/* ++ Authors: ++ Pavel Březina ++ ++ Copyright (C) 2016 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 "db/sysdb_ssh.h" ++#include "util/util.h" ++#include "providers/data_provider.h" ++#include "responder/common/cache_req/cache_req_plugin.h" ++ ++static const char * ++cache_req_host_by_name_create_debug_name(TALLOC_CTX *mem_ctx, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain) ++{ ++ return talloc_strdup(mem_ctx, data->name.name); ++} ++ ++static errno_t ++cache_req_host_by_name_lookup(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result **_result) ++{ ++ struct ldb_result *result; ++ struct ldb_message *msg; ++ errno_t ret; ++ ++ ret = sysdb_get_ssh_host(mem_ctx, domain, data->name.name, ++ data->attrs, &msg); ++ if (ret != EOK) { ++ return ret; ++ } ++ ++ result = cache_req_create_ldb_result_from_msg(mem_ctx, msg); ++ if (result == NULL) { ++ return ENOMEM; ++ } ++ ++ *_result = result; ++ ++ return EOK; ++} ++ ++struct tevent_req * ++cache_req_host_by_name_dp_send(TALLOC_CTX *mem_ctx, ++ struct cache_req *cr, ++ struct cache_req_data *data, ++ struct sss_domain_info *domain, ++ struct ldb_result *result) ++{ ++ return sss_dp_get_ssh_host_send(mem_ctx, cr->rctx, domain, false, ++ data->name.name, data->alias); ++} ++ ++const struct cache_req_plugin cache_req_host_by_name = { ++ .name = "Host by name", ++ .attr_expiration = SYSDB_CACHE_EXPIRE, ++ .parse_name = true, ++ .ignore_default_domain = true, ++ .bypass_cache = false, ++ .only_one_result = true, ++ .search_all_domains = false, ++ .require_enumeration = false, ++ .allow_missing_fqn = true, ++ .allow_switch_to_upn = false, ++ .upn_equivalent = CACHE_REQ_SENTINEL, ++ .get_next_domain_flags = 0, ++ ++ .is_well_known_fn = NULL, ++ .prepare_domain_data_fn = NULL, ++ .create_debug_name_fn = cache_req_host_by_name_create_debug_name, ++ .global_ncache_add_fn = NULL, ++ .ncache_check_fn = NULL, ++ .ncache_add_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 ++}; ++ ++struct tevent_req * ++cache_req_host_by_name_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct resp_ctx *rctx, ++ struct sss_nc_ctx *ncache, ++ int cache_refresh_percent, ++ const char *domain, ++ const char *name, ++ const char *alias, ++ const char **attrs) ++{ ++ struct cache_req_data *data; ++ ++ data = cache_req_data_host(mem_ctx, CACHE_REQ_HOST_BY_NAME, name, ++ alias, attrs); ++ if (data == NULL) { ++ return NULL; ++ } ++ ++ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache, ++ cache_refresh_percent, domain, data); ++} +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index c387c6ec326c612eef8798673c1c70c67efd5452..748dec4301b4a018691d9b8c8fca0193d18167a5 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -318,6 +318,21 @@ sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + dbus_uint32_t *err_min, + char **err_msg); + ++struct tevent_req * ++sss_dp_get_ssh_host_send(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ struct sss_domain_info *dom, ++ bool fast_reply, ++ const char *name, ++ const char *alias); ++ ++errno_t ++sss_dp_get_ssh_host_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ dbus_uint16_t *dp_err, ++ dbus_uint32_t *dp_ret, ++ char **err_msg); ++ + bool sss_utf8_check(const uint8_t *s, size_t n); + + void responder_set_fd_limit(rlim_t fd_limit); +diff --git a/src/responder/ssh/sshsrv_dp.c b/src/responder/common/responder_dp_ssh.c +similarity index 99% +rename from src/responder/ssh/sshsrv_dp.c +rename to src/responder/common/responder_dp_ssh.c +index f02c3f477e3789360075a6022086d21cfcd7aefd..303ba1568b6230b0d4dfa718e4a7c024ae84d4e9 100644 +--- a/src/responder/ssh/sshsrv_dp.c ++++ b/src/responder/common/responder_dp_ssh.c +@@ -21,13 +21,12 @@ + #include + #include + #include +-#include "sbus/sssd_dbus.h" + + #include "util/util.h" + #include "sbus/sbus_client.h" ++#include "sbus/sssd_dbus.h" + #include "providers/data_provider/dp_responder_iface.h" + #include "responder/common/responder.h" +-#include "responder/ssh/sshsrv_private.h" + + struct sss_dp_get_ssh_host_info { + struct sss_domain_info *dom; +diff --git a/src/responder/ssh/sshsrv_private.h b/src/responder/ssh/sshsrv_private.h +index 9553cd7940571bf107d9fb4562d11d8c1eab3624..3ea895536657cbfa82328b8a2661da56859eb929 100644 +--- a/src/responder/ssh/sshsrv_private.h ++++ b/src/responder/ssh/sshsrv_private.h +@@ -51,19 +51,4 @@ struct ssh_cmd_ctx { + + struct sss_cmd_table *get_ssh_cmds(void); + +-struct tevent_req * +-sss_dp_get_ssh_host_send(TALLOC_CTX *mem_ctx, +- struct resp_ctx *rctx, +- struct sss_domain_info *dom, +- bool fast_reply, +- const char *name, +- const char *alias); +- +-errno_t +-sss_dp_get_ssh_host_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- dbus_uint16_t *dp_err, +- dbus_uint32_t *dp_ret, +- char **err_msg); +- + #endif /* _SSHSRV_PRIVATE_H_ */ +diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c +index cbdb65d745a63ae00613001847351d3dba0fe290..5db5255ab61231870982c4b78a39504ae8954bcd 100644 +--- a/src/tests/cmocka/common_mock_resp_dp.c ++++ b/src/tests/cmocka/common_mock_resp_dp.c +@@ -61,6 +61,39 @@ sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + return test_request_recv(req); + } + ++struct tevent_req * ++sss_dp_get_ssh_host_send(TALLOC_CTX *mem_ctx, ++ struct resp_ctx *rctx, ++ struct sss_domain_info *dom, ++ bool fast_reply, ++ const char *name, ++ const char *alias) ++{ ++ return test_req_succeed_send(mem_ctx, rctx->ev); ++} ++ ++ ++errno_t ++sss_dp_get_ssh_host_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ dbus_uint16_t *dp_err, ++ dbus_uint32_t *dp_ret, ++ char **err_msg) ++{ ++ acct_cb_t cb; ++ ++ *dp_err = sss_mock_type(dbus_uint16_t); ++ *dp_ret = sss_mock_type(dbus_uint32_t); ++ *err_msg = sss_mock_ptr_type(char *); ++ ++ cb = sss_mock_ptr_type(acct_cb_t); ++ if (cb) { ++ (cb)(sss_mock_ptr_type(void *)); ++ } ++ ++ return test_request_recv(req); ++} ++ + errno_t + sss_dp_req_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, +diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am +index 8ca0026178d79271167a09d295940f7c5f55d98b..09a8b5307dd3ebf9c7f27148097a90eac527a213 100644 +--- a/src/tests/cwrap/Makefile.am ++++ b/src/tests/cwrap/Makefile.am +@@ -60,6 +60,7 @@ SSSD_CACHE_REQ_OBJ = \ + ../../../src/responder/common/cache_req/plugins/cache_req_svc_by_name.c \ + ../../../src/responder/common/cache_req/plugins/cache_req_svc_by_port.c \ + ../../../src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c \ ++ ../../../src/responder/common/cache_req/plugins/cache_req_host_by_name.c \ + $(NULL) + + SSSD_RESPONDER_OBJ = \ +@@ -68,6 +69,7 @@ SSSD_RESPONDER_OBJ = \ + ../../../src/responder/common/responder_cmd.c \ + ../../../src/responder/common/responder_common.c \ + ../../../src/responder/common/responder_dp.c \ ++ ../../../src/responder/common/responder_dp_ssh.c \ + ../../../src/responder/common/responder_packet.c \ + ../../../src/responder/common/responder_get_domains.c \ + ../../../src/responder/common/responder_utils.c \ +-- +2.9.3 + diff --git a/0030-ssh-rewrite-ssh-responder-to-use-cache_req.patch b/0030-ssh-rewrite-ssh-responder-to-use-cache_req.patch new file mode 100644 index 0000000..ce9e0d1 --- /dev/null +++ b/0030-ssh-rewrite-ssh-responder-to-use-cache_req.patch @@ -0,0 +1,2506 @@ +From a8191ce7ad5364801ad9458c3194075a7ca77b8a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 18 Jan 2017 12:49:48 +0100 +Subject: [PATCH 30/79] ssh: rewrite ssh responder to use cache_req + +This is a bigger change since both supported commands could be +rewritten for cache_req and the logic could be deleted. I decided +to also split the file into more modules and follow similar pattern +as with nss responder. + +Resolves: +https://fedorahosted.org/sssd/ticket/1126 + +Reviewed-by: Jakub Hrozek +--- + Makefile.am | 7 +- + src/responder/ssh/ssh_cmd.c | 256 +++++ + src/responder/ssh/ssh_known_hosts.c | 329 ++++++ + .../ssh/{sshsrv_private.h => ssh_private.h} | 45 +- + src/responder/ssh/ssh_protocol.c | 217 ++++ + src/responder/ssh/ssh_reply.c | 333 ++++++ + src/responder/ssh/sshsrv.c | 2 +- + src/responder/ssh/sshsrv_cmd.c | 1203 -------------------- + 8 files changed, 1172 insertions(+), 1220 deletions(-) + create mode 100644 src/responder/ssh/ssh_cmd.c + create mode 100644 src/responder/ssh/ssh_known_hosts.c + rename src/responder/ssh/{sshsrv_private.h => ssh_private.h} (53%) + create mode 100644 src/responder/ssh/ssh_protocol.c + create mode 100644 src/responder/ssh/ssh_reply.c + delete mode 100644 src/responder/ssh/sshsrv_cmd.c + +diff --git a/Makefile.am b/Makefile.am +index 6592261df87fc4fd0b83aba42e9f5cd12238a6cb..713a83ce0b5c2b8d71495ff05b52e52e413b5c95 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -656,7 +656,7 @@ dist_noinst_HEADERS = \ + src/responder/common/negcache.h \ + src/responder/sudo/sudosrv_private.h \ + src/responder/autofs/autofs_private.h \ +- src/responder/ssh/sshsrv_private.h \ ++ src/responder/ssh/ssh_private.h \ + src/responder/ifp/ifp_iface_generated.h \ + src/responder/ifp/ifp_iface.h \ + src/responder/ifp/ifp_private.h \ +@@ -1333,7 +1333,10 @@ endif + if BUILD_SSH + sssd_ssh_SOURCES = \ + src/responder/ssh/sshsrv.c \ +- src/responder/ssh/sshsrv_cmd.c \ ++ src/responder/ssh/ssh_cmd.c \ ++ src/responder/ssh/ssh_known_hosts.c \ ++ src/responder/ssh/ssh_protocol.c \ ++ src/responder/ssh/ssh_reply.c \ + $(SSSD_RESPONDER_OBJ) \ + $(NULL) + sssd_ssh_LDADD = \ +diff --git a/src/responder/ssh/ssh_cmd.c b/src/responder/ssh/ssh_cmd.c +new file mode 100644 +index 0000000000000000000000000000000000000000..a1188280dc2d1f73c726aec7c203692a63c37a32 +--- /dev/null ++++ b/src/responder/ssh/ssh_cmd.c +@@ -0,0 +1,256 @@ ++/* ++ Authors: ++ Pavel Březina ++ ++ Copyright (C) 2016 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 "config.h" ++ ++#include ++#include ++#include ++ ++#include "util/util.h" ++#include "responder/common/responder.h" ++#include "responder/common/cache_req/cache_req.h" ++#include "responder/ssh/ssh_private.h" ++ ++struct ssh_cmd_ctx { ++ struct cli_ctx *cli_ctx; ++ const char *name; ++ const char *alias; ++ const char *domain; ++}; ++ ++static errno_t ++ssh_check_non_sssd_user(const char *username) ++{ ++ struct passwd *pwd; ++ ++ pwd = getpwnam(username); ++ if (pwd != NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "%s is a non-SSSD user\n", username); ++ return ERR_NON_SSSD_USER; ++ } ++ ++ return ENOENT; ++} ++ ++ ++static struct sss_domain_info * ++ssh_get_result_domain(struct resp_ctx *rctx, ++ struct cache_req_result *result, ++ const char *name) ++{ ++ if (result != NULL) { ++ return result->domain; ++ } ++ ++ return find_domain_by_name(rctx->domains, name, true); ++} ++ ++static void ssh_cmd_get_user_pubkeys_done(struct tevent_req *subreq); ++ ++static errno_t ssh_cmd_get_user_pubkeys(struct cli_ctx *cli_ctx) ++{ ++ struct ssh_cmd_ctx *cmd_ctx; ++ struct tevent_req *subreq; ++ errno_t ret; ++ ++ static const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, ++ SYSDB_USER_CERT, NULL }; ++ ++ cmd_ctx = talloc_zero(cli_ctx, struct ssh_cmd_ctx); ++ if (cmd_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cmd_ctx->cli_ctx = cli_ctx; ++ ++ ret = ssh_protocol_parse_user(cli_ctx, cli_ctx->rctx->default_domain, ++ &cmd_ctx->name, &cmd_ctx->domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n"); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Requesting SSH user public keys for [%s] from [%s]\n", ++ cmd_ctx->name, cmd_ctx->domain ? cmd_ctx->domain : ""); ++ ++ if (strcmp(cmd_ctx->name, "root") == 0) { ++ ret = ERR_NON_SSSD_USER; ++ goto done; ++ } ++ ++ subreq = cache_req_user_by_name_attrs_send(cmd_ctx, cli_ctx->ev, ++ cli_ctx->rctx, ++ cli_ctx->rctx->ncache, 0, ++ cmd_ctx->domain, ++ cmd_ctx->name, attrs); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tevent_req_set_callback(subreq, ssh_cmd_get_user_pubkeys_done, cmd_ctx); ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(cmd_ctx); ++ return ssh_protocol_done(cli_ctx, ret); ++ } ++ ++ return ret; ++} ++ ++static void ssh_cmd_get_user_pubkeys_done(struct tevent_req *subreq) ++{ ++ struct cache_req_result *result; ++ struct ssh_cmd_ctx *cmd_ctx; ++ errno_t ret; ++ ++ cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx); ++ ++ ret = cache_req_user_by_name_attrs_recv(cmd_ctx, subreq, &result); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ if (ret == ENOENT) { ++ /* Check if it is a non SSSD user. */ ++ ret = ssh_check_non_sssd_user(cmd_ctx->name); ++ } ++ ++ ssh_protocol_done(cmd_ctx->cli_ctx, ret); ++ goto done; ++ } ++ ++ ssh_protocol_reply(cmd_ctx->cli_ctx, result); ++ ++done: ++ talloc_free(cmd_ctx); ++} ++ ++static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq); ++ ++static errno_t ssh_cmd_get_host_pubkeys(struct cli_ctx *cli_ctx) ++{ ++ struct ssh_cmd_ctx *cmd_ctx; ++ struct tevent_req *subreq; ++ errno_t ret; ++ ++ static const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL }; ++ ++ cmd_ctx = talloc_zero(cli_ctx, struct ssh_cmd_ctx); ++ if (cmd_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ cmd_ctx->cli_ctx = cli_ctx; ++ ++ ret = ssh_protocol_parse_host(cli_ctx, &cmd_ctx->name, &cmd_ctx->alias, ++ &cmd_ctx->domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n"); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Requesting SSH host public keys for [%s] from [%s]\n", ++ cmd_ctx->name, cmd_ctx->domain ? cmd_ctx->domain : ""); ++ ++ subreq = cache_req_host_by_name_send(cmd_ctx, cli_ctx->ev, ++ cli_ctx->rctx, ++ cli_ctx->rctx->ncache, 0, ++ cmd_ctx->domain, ++ cmd_ctx->name, ++ cmd_ctx->alias, attrs); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tevent_req_set_callback(subreq, ssh_cmd_get_host_pubkeys_done, cmd_ctx); ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(cmd_ctx); ++ return ssh_protocol_done(cli_ctx, ret); ++ } ++ ++ return ret; ++} ++ ++static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq) ++{ ++ struct cache_req_result *result = NULL; ++ struct sss_domain_info *domain; ++ struct ssh_cmd_ctx *cmd_ctx; ++ struct ssh_ctx *ssh_ctx; ++ errno_t ret; ++ ++ cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx); ++ ssh_ctx = talloc_get_type(cmd_ctx->cli_ctx->rctx->pvt_ctx, struct ssh_ctx); ++ ++ ret = cache_req_user_by_name_attrs_recv(cmd_ctx, subreq, &result); ++ talloc_zfree(subreq); ++ ++ if (ret == EOK || ret == ENOENT) { ++ domain = ssh_get_result_domain(ssh_ctx->rctx, result, cmd_ctx->domain); ++ ++ ssh_update_known_hosts_file(ssh_ctx->rctx->domains, domain, ++ cmd_ctx->name, ssh_ctx->hash_known_hosts, ++ ssh_ctx->known_hosts_timeout); ++ } ++ ++ if (ret != EOK) { ++ ssh_protocol_done(cmd_ctx->cli_ctx, ret); ++ goto done; ++ } ++ ++ ssh_protocol_reply(cmd_ctx->cli_ctx, result); ++ ++done: ++ talloc_free(cmd_ctx); ++} ++ ++struct cli_protocol_version *register_cli_protocol_version(void) ++{ ++ static struct cli_protocol_version ssh_cli_protocol_version[] = { ++ {0, NULL, NULL} ++ }; ++ ++ return ssh_cli_protocol_version; ++} ++ ++struct sss_cmd_table *get_ssh_cmds(void) { ++ static struct sss_cmd_table ssh_cmds[] = { ++ {SSS_GET_VERSION, sss_cmd_get_version}, ++ {SSS_SSH_GET_USER_PUBKEYS, ssh_cmd_get_user_pubkeys}, ++ {SSS_SSH_GET_HOST_PUBKEYS, ssh_cmd_get_host_pubkeys}, ++ {SSS_CLI_NULL, NULL} ++ }; ++ ++ return ssh_cmds; ++} +diff --git a/src/responder/ssh/ssh_known_hosts.c b/src/responder/ssh/ssh_known_hosts.c +new file mode 100644 +index 0000000000000000000000000000000000000000..ca0872264f6180bb72f33ee17f0c703fd6520574 +--- /dev/null ++++ b/src/responder/ssh/ssh_known_hosts.c +@@ -0,0 +1,329 @@ ++/* ++ Authors: ++ Jan Cholasta ++ ++ Copyright (C) 2012 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 "config.h" ++ ++#include ++ ++#include "util/util.h" ++#include "util/crypto/sss_crypto.h" ++#include "util/sss_ssh.h" ++#include "db/sysdb.h" ++#include "db/sysdb_ssh.h" ++#include "responder/ssh/ssh_private.h" ++ ++static char * ++ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx, ++ struct sss_ssh_ent *ent) ++{ ++ TALLOC_CTX *tmp_ctx; ++ errno_t ret; ++ char *name, *pubkey; ++ char *result = NULL; ++ size_t i; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (!tmp_ctx) { ++ return NULL; ++ } ++ ++ name = talloc_strdup(tmp_ctx, ent->name); ++ if (!name) { ++ goto done; ++ } ++ ++ for (i = 0; i < ent->num_aliases; i++) { ++ name = talloc_asprintf_append(name, ",%s", ent->aliases[i]); ++ if (!name) { ++ goto done; ++ } ++ } ++ ++ result = talloc_strdup(tmp_ctx, ""); ++ if (!result) { ++ goto done; ++ } ++ ++ for (i = 0; i < ent->num_pubkeys; i++) { ++ ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey); ++ if (ret != EOK) { ++ result = NULL; ++ goto done; ++ } ++ ++ result = talloc_asprintf_append(result, "%s %s\n", name, pubkey); ++ if (!result) { ++ goto done; ++ } ++ ++ talloc_free(pubkey); ++ } ++ ++ talloc_steal(mem_ctx, result); ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return result; ++} ++ ++static char * ++ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx, ++ struct sss_ssh_ent *ent) ++{ ++ TALLOC_CTX *tmp_ctx; ++ errno_t ret; ++ char *name, *pubkey, *saltstr, *hashstr, *result; ++ unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH]; ++ size_t i, j, k; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (!tmp_ctx) { ++ return NULL; ++ } ++ ++ result = talloc_strdup(tmp_ctx, ""); ++ if (!result) { ++ goto done; ++ } ++ ++ for (i = 0; i < ent->num_pubkeys; i++) { ++ ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey); ++ if (ret != EOK) { ++ result = NULL; ++ goto done; ++ } ++ ++ for (j = 0; j <= ent->num_aliases; j++) { ++ name = (j == 0 ? ent->name : ent->aliases[j-1]); ++ ++ for (k = 0; k < SSS_SHA1_LENGTH; k++) { ++ salt[k] = rand(); ++ } ++ ++ ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH, ++ (unsigned char *)name, strlen(name), ++ hash); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_hmac_sha1() failed (%d): %s\n", ++ ret, strerror(ret)); ++ result = NULL; ++ goto done; ++ } ++ ++ saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH); ++ if (!saltstr) { ++ result = NULL; ++ goto done; ++ } ++ ++ hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH); ++ if (!hashstr) { ++ result = NULL; ++ goto done; ++ } ++ ++ result = talloc_asprintf_append(result, "|1|%s|%s %s\n", ++ saltstr, hashstr, pubkey); ++ if (!result) { ++ goto done; ++ } ++ ++ talloc_free(saltstr); ++ talloc_free(hashstr); ++ } ++ ++ talloc_free(pubkey); ++ } ++ ++ talloc_steal(mem_ctx, result); ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return result; ++} ++ ++static errno_t ++ssh_write_known_hosts(struct sss_domain_info *domains, ++ bool hash_known_hosts, ++ time_t now, ++ int fd) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct sss_domain_info *dom; ++ struct ldb_message **hosts; ++ struct sysdb_ctx *sysdb; ++ struct sss_ssh_ent *ent; ++ char *entstr; ++ size_t num_hosts; ++ size_t i; ++ ssize_t wret; ++ errno_t ret; ++ ++ static const char *attrs[] = { ++ SYSDB_NAME, ++ SYSDB_NAME_ALIAS, ++ SYSDB_SSH_PUBKEY, ++ NULL ++ }; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; ++ } ++ ++ for (dom = domains; dom != NULL; dom = get_next_domain(dom, false)) { ++ sysdb = dom->sysdb; ++ if (sysdb == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Fatal: Sysdb CTX not found for this domain!\n"); ++ ret = EFAULT; ++ goto done; ++ } ++ ++ ret = sysdb_get_ssh_known_hosts(tmp_ctx, dom, now, attrs, ++ &hosts, &num_hosts); ++ if (ret == ENOENT) { ++ continue; ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Host search failed for domain " ++ "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret)); ++ continue; ++ } ++ ++ for (i = 0; i < num_hosts; i++) { ++ ret = sss_ssh_make_ent(tmp_ctx, hosts[i], &ent); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to get SSH host public keys\n"); ++ continue; ++ } ++ ++ if (hash_known_hosts) { ++ entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent); ++ } else { ++ entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent); ++ } ++ ++ if (entstr == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to format known_hosts data " ++ "for [%s]\n", ent->name); ++ continue; ++ } ++ ++ wret = sss_atomic_write_s(fd, entstr, strlen(entstr)); ++ if (wret == -1) { ++ ret = errno; ++ goto done; ++ } ++ ++ talloc_free(ent); ++ } ++ ++ talloc_free(hosts); ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ ++errno_t ++ssh_update_known_hosts_file(struct sss_domain_info *domains, ++ struct sss_domain_info *domain, ++ const char *name, ++ bool hash_known_hosts, ++ int known_hosts_timeout) ++{ ++ TALLOC_CTX *tmp_ctx; ++ char *filename; ++ errno_t ret; ++ time_t now; ++ int fd = -1; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; ++ } ++ ++ now = time(NULL); ++ ++ /* Update host's expiration time. */ ++ if (domain != NULL) { ++ ret = sysdb_update_ssh_known_host_expire(domain, name, now, ++ known_hosts_timeout); ++ if (ret != EOK && ret != ENOENT) { ++ goto done; ++ } ++ } ++ ++ /* Create temporary known hosts file. */ ++ filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL); ++ if (filename == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ fd = sss_unique_file_ex(tmp_ctx, filename, 0133, &ret); ++ if (fd == -1) { ++ filename = NULL; ++ goto done; ++ } ++ ++ /* Write contents. */ ++ ret = ssh_write_known_hosts(domains, hash_known_hosts, now, fd); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write known hosts file " ++ "[%d]: %s\n", ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ++ /* Rename to SSH known hosts file. */ ++ ret = fchmod(fd, 0644); ++ if (ret == -1) { ++ ret = errno; ++ goto done; ++ } ++ ++ ret = rename(filename, SSS_SSH_KNOWN_HOSTS_PATH); ++ if (ret == -1) { ++ ret = errno; ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ if (fd != -1) { ++ close(fd); ++ } ++ ++ return ret; ++} +diff --git a/src/responder/ssh/sshsrv_private.h b/src/responder/ssh/ssh_private.h +similarity index 53% +rename from src/responder/ssh/sshsrv_private.h +rename to src/responder/ssh/ssh_private.h +index 3ea895536657cbfa82328b8a2661da56859eb929..f32112e89ac0afa8556c891634785b5fccd9b9df 100644 +--- a/src/responder/ssh/sshsrv_private.h ++++ b/src/responder/ssh/ssh_private.h +@@ -22,6 +22,7 @@ + #define _SSHSRV_PRIVATE_H_ + + #include "responder/common/responder.h" ++#include "responder/common/cache_req/cache_req.h" + + #define SSS_SSH_KNOWN_HOSTS_PATH PUBCONF_PATH"/known_hosts" + #define SSS_SSH_KNOWN_HOSTS_TEMP_TMPL PUBCONF_PATH"/.known_hosts.XXXXXX" +@@ -35,20 +36,36 @@ struct ssh_ctx { + char *ca_db; + }; + +-struct ssh_cmd_ctx { +- struct cli_ctx *cctx; +- char *name; +- char *alias; +- char *domname; +- bool is_user; +- +- struct sss_domain_info *domain; +- bool check_next; +- char *fqdn; +- +- struct ldb_message *result; +-}; +- + struct sss_cmd_table *get_ssh_cmds(void); + ++errno_t ++ssh_protocol_parse_user(struct cli_ctx *cli_ctx, ++ const char *default_domain, ++ const char **_name, ++ const char **_domain); ++ ++errno_t ++ssh_protocol_parse_host(struct cli_ctx *cli_ctx, ++ const char **_name, ++ const char **_alias, ++ const char **_domain); ++ ++void ssh_protocol_reply(struct cli_ctx *cli_ctx, ++ struct cache_req_result *result); ++ ++errno_t ++ssh_protocol_done(struct cli_ctx *cli_ctx, errno_t error); ++ ++errno_t ++ssh_protocol_build_reply(struct sss_packet *packet, ++ struct ssh_ctx *ssh_ctx, ++ struct cache_req_result *result); ++ ++errno_t ++ssh_update_known_hosts_file(struct sss_domain_info *domains, ++ struct sss_domain_info *domain, ++ const char *name, ++ bool hash_known_hosts, ++ int known_hosts_timeout); ++ + #endif /* _SSHSRV_PRIVATE_H_ */ +diff --git a/src/responder/ssh/ssh_protocol.c b/src/responder/ssh/ssh_protocol.c +new file mode 100644 +index 0000000000000000000000000000000000000000..4198f821418ca168b1f0015acbd47d8a3e13f0a3 +--- /dev/null ++++ b/src/responder/ssh/ssh_protocol.c +@@ -0,0 +1,217 @@ ++/* ++ Authors: ++ Pavel Březina ++ ++ 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 "config.h" ++ ++#include ++ ++#include "util/util.h" ++#include "util/sss_ssh.h" ++#include "responder/common/responder.h" ++#include "responder/common/responder_packet.h" ++#include "responder/common/cache_req/cache_req.h" ++#include "responder/ssh/ssh_private.h" ++ ++errno_t ++ssh_protocol_done(struct cli_ctx *cli_ctx, errno_t error) ++{ ++ struct cli_protocol *pctx; ++ errno_t ret; ++ ++ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); ++ ++ switch (error) { ++ case EOK: ++ /* Create empty packet if none was provided. */ ++ if (pctx->creq->out == NULL) { ++ ret = sss_packet_new(pctx->creq, 0, ++ sss_packet_get_cmd(pctx->creq->in), ++ &pctx->creq->out); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ sss_packet_set_error(pctx->creq->out, EOK); ++ } ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: success\n"); ++ ret = EOK; ++ goto done; ++ default: ++ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: error [%d]: %s\n", ++ error, sss_strerror(error)); ++ ret = sss_cmd_send_error(cli_ctx, error); ++ goto done; ++ } ++ ++done: ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send reply [%d]: %s!\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ sss_cmd_done(cli_ctx, NULL); ++ return EOK; ++} ++ ++void ssh_protocol_reply(struct cli_ctx *cli_ctx, ++ struct cache_req_result *result) ++{ ++ struct cli_protocol *pctx; ++ struct ssh_ctx *ssh_ctx; ++ errno_t ret; ++ ++ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); ++ ssh_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct ssh_ctx); ++ ++ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), ++ &pctx->creq->out); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = ssh_protocol_build_reply(pctx->creq->out, ssh_ctx, result); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ sss_packet_set_error(pctx->creq->out, EOK); ++ ++done: ++ ssh_protocol_done(cli_ctx, ret); ++} ++ ++static errno_t ++ssh_protocol_parse_request(struct cli_ctx *cli_ctx, ++ const char *default_domain, ++ const char **_name, ++ const char **_alias, ++ const char **_domain) ++{ ++ struct cli_protocol *pctx; ++ const char *name = NULL; ++ const char *alias = NULL; ++ const char *domain = NULL; ++ uint32_t flags; ++ uint32_t name_len; ++ uint32_t alias_len; ++ uint32_t domain_len; ++ size_t body_len; ++ uint8_t *body; ++ size_t c = 0; ++ ++ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); ++ ++ sss_packet_get_body(pctx->creq->in, &body, &body_len); ++ ++ SAFEALIGN_COPY_UINT32_CHECK(&flags, body + c, body_len, &c); ++ if (flags & ~(uint32_t)SSS_SSH_REQ_MASK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid flags received [0x%x]\n", flags); ++ return EINVAL; ++ } ++ ++ SAFEALIGN_COPY_UINT32_CHECK(&name_len, body + c, body_len, &c); ++ if (name_len == 0 || name_len > body_len - c) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid name length\n"); ++ return EINVAL; ++ } ++ ++ name = (const char *)(body + c); ++ if (!sss_utf8_check((const uint8_t *)name, name_len-1) || ++ name[name_len - 1] != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Name is not valid UTF-8 string\n"); ++ return EINVAL; ++ } ++ c += name_len; ++ ++ if (flags & SSS_SSH_REQ_ALIAS) { ++ SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body + c, body_len, &c); ++ if (alias_len == 0 || alias_len > body_len - c) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid alias length\n"); ++ return EINVAL; ++ } ++ ++ alias = (const char *)(body+c); ++ if (!sss_utf8_check((const uint8_t *)alias, alias_len - 1) || ++ alias[alias_len - 1] != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Alias is not valid UTF-8 string\n"); ++ return EINVAL; ++ } ++ c += alias_len; ++ } ++ ++ if (flags & SSS_SSH_REQ_DOMAIN) { ++ SAFEALIGN_COPY_UINT32_CHECK(&domain_len, body + c, body_len, &c); ++ if (domain_len > 0) { ++ if (domain_len > body_len - c) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid domain length\n"); ++ return EINVAL; ++ } ++ ++ domain = (const char *)(body + c); ++ if (!sss_utf8_check((const uint8_t *)domain, domain_len - 1) || ++ domain[domain_len - 1] != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Domain is not valid UTF-8 string\n"); ++ return EINVAL; ++ } ++ c += domain_len; ++ } else { ++ domain = default_domain; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Requested domain [%s]\n", domain ? domain : ""); ++ } ++ ++ if (_name != NULL) { ++ *_name = name; ++ } ++ ++ if (_alias != NULL) { ++ *_alias = alias; ++ } ++ ++ if (_domain != NULL) { ++ *_domain = domain; ++ } ++ ++ return EOK; ++} ++ ++errno_t ++ssh_protocol_parse_user(struct cli_ctx *cli_ctx, ++ const char *default_domain, ++ const char **_name, ++ const char **_domain) ++{ ++ return ssh_protocol_parse_request(cli_ctx, default_domain, ++ _name, NULL, _domain); ++} ++ ++errno_t ++ssh_protocol_parse_host(struct cli_ctx *cli_ctx, ++ const char **_name, ++ const char **_alias, ++ const char **_domain) ++{ ++ return ssh_protocol_parse_request(cli_ctx, NULL, _name, _alias, _domain); ++} +diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c +new file mode 100644 +index 0000000000000000000000000000000000000000..807f4ee079128b4a3f1719de890ffac6e0d5b2e0 +--- /dev/null ++++ b/src/responder/ssh/ssh_reply.c +@@ -0,0 +1,333 @@ ++/* ++ Authors: ++ Jan Cholasta ++ ++ Copyright (C) 2012 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 "config.h" ++ ++#include ++#include ++ ++#include "util/util.h" ++#include "util/crypto/sss_crypto.h" ++#include "util/sss_ssh.h" ++#include "util/cert.h" ++#include "responder/common/responder.h" ++#include "responder/common/responder_packet.h" ++#include "responder/common/cache_req/cache_req.h" ++#include "responder/ssh/ssh_private.h" ++ ++static errno_t get_valid_certs_keys(TALLOC_CTX *mem_ctx, ++ struct ssh_ctx *ssh_ctx, ++ struct ldb_message_element *el_cert, ++ struct ldb_message_element **_el_res) ++{ ++ TALLOC_CTX *tmp_ctx; ++ uint8_t *key; ++ size_t key_len; ++ char *cert_verification_opts; ++ struct cert_verify_opts *cert_verify_opts; ++ int ret; ++ struct ldb_message_element *el_res; ++ size_t d; ++ ++ if (el_cert == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n"); ++ return EOK; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = confdb_get_string(ssh_ctx->rctx->cdb, tmp_ctx, ++ CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_MONITOR_CERT_VERIFICATION, NULL, ++ &cert_verification_opts); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to read p11_child_timeout from confdb: [%d] %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = parse_cert_verify_opts(tmp_ctx, cert_verification_opts, ++ &cert_verify_opts); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Failed to parse verifiy option.\n"); ++ goto done; ++ } ++ ++ el_res = talloc_zero(tmp_ctx, struct ldb_message_element); ++ if (el_res == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ el_res->values = talloc_array(el_res, struct ldb_val, el_cert->num_values); ++ if (el_res->values == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (d = 0; d < el_cert->num_values; d++) { ++ ret = cert_to_ssh_key(tmp_ctx, ssh_ctx->ca_db, ++ el_cert->values[d].data, ++ el_cert->values[d].length, ++ cert_verify_opts, &key, &key_len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key failed, ignoring.\n"); ++ continue; ++ } ++ ++ el_res->values[el_res->num_values].data = ++ talloc_steal(el_res->values, key); ++ el_res->values[el_res->num_values].length = key_len; ++ el_res->num_values++; ++ } ++ ++ if (el_res->num_values == 0) { ++ *_el_res = NULL; ++ } else { ++ *_el_res = talloc_steal(mem_ctx, el_res); ++ } ++ ++ ret = EOK; ++ ++done: ++ ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ ++static errno_t decode_and_add_base64_data(struct sss_packet *packet, ++ struct ldb_message_element *el, ++ bool skip_base64_decode, ++ size_t fqname_len, ++ const char *fqname, ++ size_t *c) ++{ ++ uint8_t *key; ++ size_t key_len; ++ uint8_t *body; ++ size_t body_len; ++ int ret; ++ size_t d; ++ TALLOC_CTX *tmp_ctx; ++ ++ if (el == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n"); ++ return EOK; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ for (d = 0; d < el->num_values; d++) { ++ if (skip_base64_decode) { ++ key = el->values[d].data; ++ key_len = el->values[d].length; ++ } else { ++ key = sss_base64_decode(tmp_ctx, (const char *) el->values[d].data, ++ &key_len); ++ if (key == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ ret = sss_packet_grow(packet, ++ 3*sizeof(uint32_t) + key_len + fqname_len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); ++ goto done; ++ } ++ sss_packet_get_body(packet, &body, &body_len); ++ ++ SAFEALIGN_SET_UINT32(body+(*c), 0, c); ++ SAFEALIGN_SET_UINT32(body+(*c), fqname_len, c); ++ safealign_memcpy(body+(*c), fqname, fqname_len, c); ++ SAFEALIGN_SET_UINT32(body+(*c), key_len, c); ++ safealign_memcpy(body+(*c), key, key_len, c); ++ ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ ++static errno_t ++ssh_get_output_keys(TALLOC_CTX *mem_ctx, ++ struct ssh_ctx *ssh_ctx, ++ struct sss_domain_info *domain, ++ struct ldb_message *msg, ++ struct ldb_message_element ***_elements, ++ uint32_t *_num_keys) ++{ ++ struct ldb_message_element **elements; ++ struct ldb_message_element *user_cert; ++ uint32_t num_keys = 0; ++ uint32_t i = 0; ++ errno_t ret; ++ ++ elements = talloc_zero_array(mem_ctx, struct ldb_message_element *, 5); ++ if (elements == NULL) { ++ return ENOMEM; ++ } ++ ++ elements[i] = ldb_msg_find_element(msg, SYSDB_SSH_PUBKEY); ++ if (elements[i] != NULL) { ++ num_keys += elements[i]->num_values; ++ i++; ++ } ++ ++ elements[i] = ldb_msg_find_element(msg, ORIGINALAD_PREFIX SYSDB_SSH_PUBKEY); ++ if (elements[i] != NULL) { ++ num_keys += elements[i]->num_values; ++ i++; ++ } ++ ++ if (DOM_HAS_VIEWS(domain)) { ++ elements[i] = ldb_msg_find_element(msg, OVERRIDE_PREFIX SYSDB_SSH_PUBKEY); ++ if (elements[i] != NULL) { ++ num_keys += elements[i]->num_values; ++ i++; ++ } ++ } ++ ++ user_cert = ldb_msg_find_element(msg, 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) { ++ num_keys += elements[i]->num_values; ++ i++; ++ } ++ } ++ ++ *_elements = elements; ++ *_num_keys = num_keys; ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(elements); ++ } ++ ++ return ret; ++} ++ ++static errno_t ++ssh_get_name(struct ldb_message *msg, ++ struct sized_string *sz_name) ++{ ++ const char *name; ++ ++ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); ++ if (name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "Got unnamed result!\n"); ++ return ENOENT; ++ } ++ ++ to_sized_string(sz_name, name); ++ ++ return EOK; ++} ++ ++errno_t ++ssh_protocol_build_reply(struct sss_packet *packet, ++ struct ssh_ctx *ssh_ctx, ++ struct cache_req_result *result) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_message_element **elements; ++ struct sized_string name; ++ uint32_t num_keys; ++ size_t body_len; ++ uint8_t *body; ++ size_t c = 0; ++ errno_t ret; ++ int i; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; ++ } ++ ++ ret = ssh_get_output_keys(tmp_ctx, ssh_ctx, result->domain, ++ result->msgs[0], &elements, &num_keys); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = ssh_get_name(result->msgs[0], &name); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ sss_packet_get_body(packet, &body, &body_len); ++ ++ SAFEALIGN_SET_UINT32(&body[c], num_keys, &c); ++ SAFEALIGN_SET_UINT32(&body[c], 0, &c); ++ ++ if (num_keys == 0) { ++ ret = EOK; ++ goto done; ++ } ++ ++ for (i = 0; elements[i] != NULL; i++) { ++ ret = decode_and_add_base64_data(packet, elements[i], false, ++ name.len, name.str, &c); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); ++ goto done; ++ } ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} +diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c +index 80230794a687bbef3d2d68aa58e9ed6a0c127799..440f0e2b9dc06e3dc52ff96d7207b8a3727865c0 100644 +--- a/src/responder/ssh/sshsrv.c ++++ b/src/responder/ssh/sshsrv.c +@@ -25,7 +25,7 @@ + #include "monitor/monitor_interfaces.h" + #include "responder/common/responder.h" + #include "responder/common/responder_sbus.h" +-#include "responder/ssh/sshsrv_private.h" ++#include "responder/ssh/ssh_private.h" + #include "providers/data_provider.h" + + struct mon_cli_iface monitor_ssh_methods = { +diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c +deleted file mode 100644 +index 195d5763e9c5f4f9ff2f2f5ac49cd856d9198e7a..0000000000000000000000000000000000000000 +--- a/src/responder/ssh/sshsrv_cmd.c ++++ /dev/null +@@ -1,1203 +0,0 @@ +-/* +- Authors: +- Jan Cholasta +- +- Copyright (C) 2012 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 "config.h" +- +-#include +-#include +-#include +- +-#include "util/util.h" +-#include "util/crypto/sss_crypto.h" +-#include "util/sss_ssh.h" +-#include "util/cert.h" +-#include "db/sysdb.h" +-#include "db/sysdb_ssh.h" +-#include "providers/data_provider.h" +-#include "responder/common/responder.h" +-#include "responder/common/responder_packet.h" +-#include "responder/ssh/sshsrv_private.h" +- +-static errno_t +-ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx, +- char *default_domain); +- +-static errno_t +-ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx); +-static errno_t +-ssh_cmd_get_user_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx, +- errno_t ret); +- +-int +-sss_ssh_cmd_get_user_pubkeys(struct cli_ctx *cctx) +-{ +- errno_t ret; +- struct ssh_cmd_ctx *cmd_ctx; +- +- cmd_ctx = talloc_zero(cctx, struct ssh_cmd_ctx); +- if (!cmd_ctx) { +- return ENOMEM; +- } +- cmd_ctx->cctx = cctx; +- cmd_ctx->is_user = true; +- +- ret = ssh_cmd_parse_request(cmd_ctx, cctx->rctx->default_domain); +- if (ret != EOK) { +- goto done; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, +- "Requesting SSH user public keys for [%s] from [%s]\n", +- cmd_ctx->name, cmd_ctx->domname ? cmd_ctx->domname : ""); +- +- if (strcmp(cmd_ctx->name, "root") == 0) { +- ret = ERR_NON_SSSD_USER; +- goto done; +- } +- +- if (cmd_ctx->domname) { +- cmd_ctx->domain = responder_get_domain(cctx->rctx, cmd_ctx->domname); +- if (!cmd_ctx->domain) { +- ret = ENOENT; +- goto done; +- } +- } else { +- cmd_ctx->domain = cctx->rctx->domains; +- cmd_ctx->check_next = true; +- } +- +- ret = ssh_user_pubkeys_search(cmd_ctx); +- +-done: +- return ssh_cmd_get_user_pubkeys_done(cmd_ctx, ret); +-} +- +-static errno_t +-ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx); +-static errno_t +-ssh_cmd_get_host_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx, +- errno_t ret); +- +-static int +-sss_ssh_cmd_get_host_pubkeys(struct cli_ctx *cctx) +-{ +- errno_t ret; +- struct ssh_cmd_ctx *cmd_ctx; +- +- cmd_ctx = talloc_zero(cctx, struct ssh_cmd_ctx); +- if (!cmd_ctx) { +- return ENOMEM; +- } +- cmd_ctx->cctx = cctx; +- cmd_ctx->is_user = false; +- +- ret = ssh_cmd_parse_request(cmd_ctx, NULL); +- if (ret != EOK) { +- goto done; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, +- "Requesting SSH host public keys for [%s][%s] from [%s]\n", +- cmd_ctx->name, cmd_ctx->alias ? cmd_ctx->alias : "", +- cmd_ctx->domname ? cmd_ctx->domname : ""); +- +- if (cmd_ctx->domname) { +- cmd_ctx->domain = responder_get_domain(cctx->rctx, cmd_ctx->domname); +- if (!cmd_ctx->domain) { +- ret = ENOENT; +- goto done; +- } +- } else { +- cmd_ctx->domain = cctx->rctx->domains; +- cmd_ctx->check_next = true; +- } +- +- ret = ssh_host_pubkeys_search(cmd_ctx); +- +-done: +- return ssh_cmd_get_host_pubkeys_done(cmd_ctx, ret); +-} +- +-static void +-ssh_dp_send_req_done(struct tevent_req *req) +-{ +- struct dp_callback_ctx *cb_ctx = +- tevent_req_callback_data(req, struct dp_callback_ctx); +- +- errno_t ret; +- dbus_uint16_t err_maj; +- dbus_uint32_t err_min; +- char *err_msg; +- +- ret = sss_dp_get_ssh_host_recv(cb_ctx->mem_ctx, req, +- &err_maj, &err_min, +- &err_msg); +- talloc_zfree(req); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Fatal error, killing connection!\n"); +- talloc_free(cb_ctx->cctx); +- return; +- } +- +- cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr); +-} +- +-static errno_t +-ssh_user_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx); +-static void +-ssh_user_pubkeys_search_dp_callback(uint16_t err_maj, +- uint32_t err_min, +- const char *err_msg, +- void *ptr); +- +-static errno_t +-ssh_user_handle_not_found(const char *username) +-{ +- struct passwd *pwd; +- +- pwd = getpwnam(username); +- if (pwd != NULL) { +- DEBUG(SSSDBG_TRACE_ALL, "%s is a non-SSSD user\n", username); +- return ERR_NON_SSSD_USER; +- } +- +- return ENOENT; +-} +- +-static errno_t +-ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx) +-{ +- struct tevent_req *req; +- struct dp_callback_ctx *cb_ctx; +- +- /* if it is a domainless search, skip domains that require fully +- * qualified names instead */ +- while (cmd_ctx->domain && cmd_ctx->check_next && cmd_ctx->domain->fqnames) { +- cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false); +- } +- +- if (!cmd_ctx->domain) { +- DEBUG(SSSDBG_OP_FAILURE, +- "No matching domain found for [%s], fail!\n", cmd_ctx->name); +- return ssh_user_handle_not_found(cmd_ctx->name); +- } +- +- talloc_zfree(cmd_ctx->fqdn); +- cmd_ctx->fqdn = sss_resp_create_fqname(cmd_ctx, cmd_ctx->cctx->rctx, +- cmd_ctx->domain, false, cmd_ctx->name); +- if (cmd_ctx->fqdn == NULL) { +- return ENOMEM; +- } +- +- /* refresh the user's cache entry */ +- if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) { +- req = sss_dp_get_account_send(cmd_ctx, cmd_ctx->cctx->rctx, +- cmd_ctx->domain, false, SSS_DP_USER, +- cmd_ctx->fqdn, 0, NULL); +- if (!req) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Out of memory sending data provider request\n"); +- return ENOMEM; +- } +- +- cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx); +- if (!cb_ctx) { +- talloc_zfree(req); +- return ENOMEM; +- } +- +- cb_ctx->callback = ssh_user_pubkeys_search_dp_callback; +- cb_ctx->ptr = cmd_ctx; +- cb_ctx->cctx = cmd_ctx->cctx; +- cb_ctx->mem_ctx = cmd_ctx; +- +- tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx); +- +- /* tell caller we are in an async call */ +- return EAGAIN; +- } +- +- return ssh_user_pubkeys_search_next(cmd_ctx); +-} +- +-static errno_t +-ssh_user_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx) +-{ +- errno_t ret; +- const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, SYSDB_USER_CERT, +- NULL }; +- struct ldb_result *res; +- +- DEBUG(SSSDBG_TRACE_FUNC, +- "Requesting SSH user public keys for [%s@%s]\n", +- cmd_ctx->name, cmd_ctx->domain->name); +- +- if (cmd_ctx->domain->sysdb == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Fatal: Sysdb CTX not found for this domain!\n"); +- return EFAULT; +- } +- +- ret = sysdb_get_user_attr_with_views(cmd_ctx, cmd_ctx->domain, +- cmd_ctx->fqdn, attrs, &res); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to make request to our cache!\n"); +- return EIO; +- } +- +- if (res->count > 1) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "User search by name (%s) returned > 1 results!\n", +- cmd_ctx->name); +- return EINVAL; +- } +- +- if (!res->count) { +- /* if a multidomain search, try with next */ +- if (cmd_ctx->check_next) { +- cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false); +- return ssh_user_pubkeys_search(cmd_ctx); +- } +- +- DEBUG(SSSDBG_MINOR_FAILURE, +- "No attributes for user [%s] found.\n", cmd_ctx->name); +- +- return ssh_user_handle_not_found(cmd_ctx->name); +- } +- +- cmd_ctx->result = res->msgs[0]; +- +- /* one result found */ +- return EOK; +-} +- +-static void +-ssh_user_pubkeys_search_dp_callback(uint16_t err_maj, +- uint32_t err_min, +- const char *err_msg, +- void *ptr) +-{ +- struct ssh_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct ssh_cmd_ctx); +- errno_t ret; +- +- if (err_maj) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to get information from Data Provider\n" +- "Error: %u, %u, %s\n", +- (unsigned int)err_maj, (unsigned int)err_min, err_msg); +- } +- +- ret = ssh_user_pubkeys_search_next(cmd_ctx); +- ssh_cmd_get_user_pubkeys_done(cmd_ctx, ret); +-} +- +-static errno_t +-ssh_host_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx); +-static void +-ssh_host_pubkeys_search_dp_callback(uint16_t err_maj, +- uint32_t err_min, +- const char *err_msg, +- void *ptr); +- +-static errno_t +-ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx) +-{ +- struct tevent_req *req; +- struct dp_callback_ctx *cb_ctx; +- +- if (!cmd_ctx->domain) { +- DEBUG(SSSDBG_OP_FAILURE, +- "No matching domain found for [%s], fail!\n", cmd_ctx->name); +- return ENOENT; +- } +- +- /* refresh the host's cache entry */ +- if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) { +- req = sss_dp_get_ssh_host_send(cmd_ctx, cmd_ctx->cctx->rctx, +- cmd_ctx->domain, false, +- cmd_ctx->name, cmd_ctx->alias); +- if (!req) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Out of memory sending data provider request\n"); +- return ENOMEM; +- } +- +- cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx); +- if (!cb_ctx) { +- talloc_zfree(req); +- return ENOMEM; +- } +- +- cb_ctx->callback = ssh_host_pubkeys_search_dp_callback; +- cb_ctx->ptr = cmd_ctx; +- cb_ctx->cctx = cmd_ctx->cctx; +- cb_ctx->mem_ctx = cmd_ctx; +- +- tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx); +- +- /* tell caller we are in an async call */ +- return EAGAIN; +- } +- +- return ssh_host_pubkeys_search_next(cmd_ctx); +-} +- +-static errno_t +-ssh_host_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx) +-{ +- errno_t ret; +- struct sysdb_ctx *sysdb; +- const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL }; +- +- DEBUG(SSSDBG_TRACE_FUNC, +- "Requesting SSH host public keys for [%s@%s]\n", +- cmd_ctx->name, cmd_ctx->domain->name); +- +- sysdb = cmd_ctx->domain->sysdb; +- if (sysdb == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Fatal: Sysdb CTX not found for this domain!\n"); +- return EFAULT; +- } +- +- ret = sysdb_get_ssh_host(cmd_ctx, cmd_ctx->domain, +- cmd_ctx->name, attrs, &cmd_ctx->result); +- if (ret != EOK && ret != ENOENT) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to make request to our cache!\n"); +- return EIO; +- } +- +- if (ret == ENOENT) { +- /* if a multidomain search, try with next */ +- if (cmd_ctx->check_next) { +- cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false); +- return ssh_host_pubkeys_search(cmd_ctx); +- } +- +- DEBUG(SSSDBG_OP_FAILURE, +- "No attributes for host [%s] found.\n", cmd_ctx->name); +- +- return ENOENT; +- } +- +- return EOK; +-} +- +-static void +-ssh_host_pubkeys_search_dp_callback(uint16_t err_maj, +- uint32_t err_min, +- const char *err_msg, +- void *ptr) +-{ +- struct ssh_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct ssh_cmd_ctx); +- errno_t ret; +- +- if (err_maj) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to get information from Data Provider\n" +- "Error: %u, %u, %s\n", +- (unsigned int)err_maj, (unsigned int)err_min, err_msg); +- } +- +- ret = ssh_host_pubkeys_search_next(cmd_ctx); +- ssh_cmd_get_host_pubkeys_done(cmd_ctx, ret); +-} +- +-static char * +-ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx, +- struct sss_ssh_ent *ent) +-{ +- TALLOC_CTX *tmp_ctx; +- errno_t ret; +- char *name, *pubkey; +- char *result = NULL; +- size_t i; +- +- tmp_ctx = talloc_new(NULL); +- if (!tmp_ctx) { +- return NULL; +- } +- +- name = talloc_strdup(tmp_ctx, ent->name); +- if (!name) { +- goto done; +- } +- +- for (i = 0; i < ent->num_aliases; i++) { +- name = talloc_asprintf_append(name, ",%s", ent->aliases[i]); +- if (!name) { +- goto done; +- } +- } +- +- result = talloc_strdup(tmp_ctx, ""); +- if (!result) { +- goto done; +- } +- +- for (i = 0; i < ent->num_pubkeys; i++) { +- ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey); +- if (ret != EOK) { +- result = NULL; +- goto done; +- } +- +- result = talloc_asprintf_append(result, "%s %s\n", name, pubkey); +- if (!result) { +- goto done; +- } +- +- talloc_free(pubkey); +- } +- +- talloc_steal(mem_ctx, result); +- +-done: +- talloc_free(tmp_ctx); +- +- return result; +-} +- +-static char * +-ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx, +- struct sss_ssh_ent *ent) +-{ +- TALLOC_CTX *tmp_ctx; +- errno_t ret; +- char *name, *pubkey, *saltstr, *hashstr, *result; +- unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH]; +- size_t i, j, k; +- +- tmp_ctx = talloc_new(NULL); +- if (!tmp_ctx) { +- return NULL; +- } +- +- result = talloc_strdup(tmp_ctx, ""); +- if (!result) { +- goto done; +- } +- +- for (i = 0; i < ent->num_pubkeys; i++) { +- ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey); +- if (ret != EOK) { +- result = NULL; +- goto done; +- } +- +- for (j = 0; j <= ent->num_aliases; j++) { +- name = (j == 0 ? ent->name : ent->aliases[j-1]); +- +- for (k = 0; k < SSS_SHA1_LENGTH; k++) { +- salt[k] = rand(); +- } +- +- ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH, +- (unsigned char *)name, strlen(name), +- hash); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "sss_hmac_sha1() failed (%d): %s\n", +- ret, strerror(ret)); +- result = NULL; +- goto done; +- } +- +- saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH); +- if (!saltstr) { +- result = NULL; +- goto done; +- } +- +- hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH); +- if (!hashstr) { +- result = NULL; +- goto done; +- } +- +- result = talloc_asprintf_append(result, "|1|%s|%s %s\n", +- saltstr, hashstr, pubkey); +- if (!result) { +- goto done; +- } +- +- talloc_free(saltstr); +- talloc_free(hashstr); +- } +- +- talloc_free(pubkey); +- } +- +- talloc_steal(mem_ctx, result); +- +-done: +- talloc_free(tmp_ctx); +- +- return result; +-} +- +-static errno_t +-ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx) +-{ +- TALLOC_CTX *tmp_ctx; +- errno_t ret; +- const char *attrs[] = { +- SYSDB_NAME, +- SYSDB_NAME_ALIAS, +- SYSDB_SSH_PUBKEY, +- NULL +- }; +- struct cli_ctx *cctx = cmd_ctx->cctx; +- struct sss_domain_info *dom = cctx->rctx->domains; +- struct ssh_ctx *ssh_ctx = (struct ssh_ctx *)cctx->rctx->pvt_ctx; +- struct sysdb_ctx *sysdb; +- time_t now = time(NULL); +- struct ldb_message **hosts; +- size_t num_hosts, i; +- struct sss_ssh_ent *ent; +- int fd = -1; +- char *filename = NULL; +- char *entstr; +- ssize_t wret; +- +- tmp_ctx = talloc_new(NULL); +- if (!tmp_ctx) { +- return ENOMEM; +- } +- +- if (cmd_ctx->domain) { +- ret = sysdb_update_ssh_known_host_expire(cmd_ctx->domain, +- cmd_ctx->name, now, +- ssh_ctx->known_hosts_timeout); +- if (ret != EOK && ret != ENOENT) { +- goto done; +- } +- } +- +- /* write known_hosts file */ +- filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL); +- if (!filename) { +- ret = ENOMEM; +- goto done; +- } +- +- fd = sss_unique_file_ex(tmp_ctx, filename, 0133, &ret); +- if (fd == -1) { +- filename = NULL; +- goto done; +- } +- +- for (; dom; dom = get_next_domain(dom, false)) { +- sysdb = dom->sysdb; +- if (sysdb == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Fatal: Sysdb CTX not found for this domain!\n"); +- ret = EFAULT; +- goto done; +- } +- +- ret = sysdb_get_ssh_known_hosts(tmp_ctx, dom, now, attrs, +- &hosts, &num_hosts); +- if (ret != EOK) { +- if (ret != ENOENT) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Host search failed for domain [%s]\n", dom->name); +- } +- continue; +- } +- +- for (i = 0; i < num_hosts; i++) { +- ret = sss_ssh_make_ent(tmp_ctx, hosts[i], &ent); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to get SSH host public keys\n"); +- continue; +- } +- +- if (ssh_ctx->hash_known_hosts) { +- entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent); +- } else { +- entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent); +- } +- if (!entstr) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Failed to format known_hosts data for [%s]\n", +- ent->name); +- continue; +- } +- +- wret = sss_atomic_write_s(fd, entstr, strlen(entstr)); +- if (wret == -1) { +- ret = errno; +- goto done; +- } +- +- talloc_free(ent); +- } +- +- talloc_free(hosts); +- } +- +- ret = fchmod(fd, 0644); +- if (ret == -1) { +- ret = errno; +- goto done; +- } +- +- ret = rename(filename, SSS_SSH_KNOWN_HOSTS_PATH); +- if (ret == -1) { +- ret = errno; +- goto done; +- } +- +- ret = EOK; +- +-done: +- if (fd != -1) { +- close(fd); +- } +- talloc_free(tmp_ctx); +- +- return ret; +-} +- +-static errno_t +-ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx, +- char *default_domain) +-{ +- struct cli_protocol *pctx; +- struct ssh_ctx *ssh_ctx; +- errno_t ret; +- uint8_t *body; +- size_t body_len; +- size_t c = 0; +- uint32_t flags; +- uint32_t name_len; +- char *name; +- uint32_t alias_len; +- char *alias = NULL; +- uint32_t domain_len; +- char *domain = NULL; +- +- ssh_ctx = talloc_get_type(cmd_ctx->cctx->rctx->pvt_ctx, struct ssh_ctx); +- pctx = talloc_get_type(cmd_ctx->cctx->protocol_ctx, struct cli_protocol); +- +- sss_packet_get_body(pctx->creq->in, &body, &body_len); +- +- SAFEALIGN_COPY_UINT32_CHECK(&flags, body+c, body_len, &c); +- if (flags & ~(uint32_t)SSS_SSH_REQ_MASK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Invalid flags received [0x%x]\n", flags); +- return EINVAL; +- } +- +- SAFEALIGN_COPY_UINT32_CHECK(&name_len, body+c, body_len, &c); +- if (name_len == 0 || name_len > body_len - c) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Invalid name length\n"); +- return EINVAL; +- } +- +- name = (char *)(body+c); +- if (!sss_utf8_check((const uint8_t *)name, name_len-1) || +- name[name_len-1] != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Name is not valid UTF-8 string\n"); +- return EINVAL; +- } +- c += name_len; +- +- if (flags & SSS_SSH_REQ_ALIAS) { +- SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body+c, body_len, &c); +- if (alias_len == 0 || alias_len > body_len - c) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Invalid alias length\n"); +- return EINVAL; +- } +- +- alias = (char *)(body+c); +- if (!sss_utf8_check((const uint8_t *)alias, alias_len-1) || +- alias[alias_len-1] != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Alias is not valid UTF-8 string\n"); +- return EINVAL; +- } +- c += alias_len; +- } +- +- if (flags & SSS_SSH_REQ_DOMAIN) { +- SAFEALIGN_COPY_UINT32_CHECK(&domain_len, body+c, body_len, &c); +- if (domain_len > 0) { +- if (domain_len > body_len - c) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Invalid domain length\n"); +- return EINVAL; +- } +- +- domain = (char *)(body+c); +- if (!sss_utf8_check((const uint8_t *)domain, domain_len-1) || +- domain[domain_len-1] != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Domain is not valid UTF-8 string\n"); +- return EINVAL; +- } +- c += domain_len; +- } else { +- domain = default_domain; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, +- "Requested domain [%s]\n", domain ? domain : ""); +- } else { +- DEBUG(SSSDBG_TRACE_FUNC, "Splitting domain from name [%s]\n", name); +- +- ret = sss_parse_name(cmd_ctx, ssh_ctx->snctx, name, +- &cmd_ctx->domname, &cmd_ctx->name); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", name); +- return ENOENT; +- } +- +- name = cmd_ctx->name; +- } +- +- if (cmd_ctx->is_user && cmd_ctx->domname == NULL) { +- DEBUG(SSSDBG_TRACE_FUNC, +- "Parsing name [%s][%s]\n", name, domain ? domain : ""); +- +- ret = sss_parse_name_for_domains(cmd_ctx, +- cmd_ctx->cctx->rctx->domains, +- domain, name, +- &cmd_ctx->domname, +- &cmd_ctx->name); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Invalid name received [%s]\n", name); +- return ENOENT; +- } +- } else { +- if (cmd_ctx->name == NULL) { +- cmd_ctx->name = talloc_strdup(cmd_ctx, name); +- if (!cmd_ctx->name) return ENOMEM; +- } +- +- if (cmd_ctx->domname == NULL && domain != NULL) { +- cmd_ctx->domname = talloc_strdup(cmd_ctx, domain); +- if (!cmd_ctx->domname) return ENOMEM; +- } +- } +- +- if (alias != NULL && strcmp(cmd_ctx->name, alias) != 0) { +- cmd_ctx->alias = talloc_strdup(cmd_ctx, alias); +- if (!cmd_ctx->alias) return ENOMEM; +- } +- +- return EOK; +-} +- +-static errno_t get_valid_certs_keys(TALLOC_CTX *mem_ctx, +- struct ssh_cmd_ctx *cmd_ctx, +- struct ldb_message_element *el_cert, +- struct ssh_ctx *ssh_ctx, +- struct ldb_message_element **_el_res) +-{ +- TALLOC_CTX *tmp_ctx; +- uint8_t *key; +- size_t key_len; +- char *cert_verification_opts; +- struct cert_verify_opts *cert_verify_opts; +- int ret; +- struct ldb_message_element *el_res; +- struct cli_ctx *cctx = cmd_ctx->cctx; +- size_t d; +- +- if (el_cert == NULL) { +- DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n"); +- return EOK; +- } +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); +- return ENOMEM; +- } +- +- ret = confdb_get_string(cctx->rctx->cdb, tmp_ctx, +- CONFDB_MONITOR_CONF_ENTRY, +- CONFDB_MONITOR_CERT_VERIFICATION, NULL, +- &cert_verification_opts); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to read p11_child_timeout from confdb: [%d] %s\n", +- ret, sss_strerror(ret)); +- goto done; +- } +- +- ret = parse_cert_verify_opts(tmp_ctx, cert_verification_opts, +- &cert_verify_opts); +- if (ret != EOK) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to parse verifiy option.\n"); +- goto done; +- } +- +- el_res = talloc_zero(tmp_ctx, struct ldb_message_element); +- if (el_res == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- +- el_res->values = talloc_array(el_res, struct ldb_val, el_cert->num_values); +- if (el_res->values == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- +- for (d = 0; d < el_cert->num_values; d++) { +- ret = cert_to_ssh_key(tmp_ctx, ssh_ctx->ca_db, +- el_cert->values[d].data, +- el_cert->values[d].length, +- cert_verify_opts, &key, &key_len); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key failed, ignoring.\n"); +- continue; +- } +- +- el_res->values[el_res->num_values].data = +- talloc_steal(el_res->values, key); +- el_res->values[el_res->num_values].length = key_len; +- el_res->num_values++; +- } +- +- if (el_res->num_values == 0) { +- *_el_res = NULL; +- } else { +- *_el_res = talloc_steal(mem_ctx, el_res); +- } +- +- ret = EOK; +- +-done: +- +- talloc_free(tmp_ctx); +- +- return ret; +-} +- +-static errno_t decode_and_add_base64_data(struct ssh_cmd_ctx *cmd_ctx, +- struct ldb_message_element *el, +- bool skip_base64_decode, +- struct ssh_ctx *ssh_ctx, +- size_t fqname_len, +- const char *fqname, +- size_t *c) +-{ +- struct cli_protocol *pctx; +- uint8_t *key; +- size_t key_len; +- uint8_t *body; +- size_t body_len; +- int ret; +- size_t d; +- TALLOC_CTX *tmp_ctx; +- +- if (el == NULL) { +- DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n"); +- return EOK; +- } +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); +- return ENOMEM; +- } +- +- pctx = talloc_get_type(cmd_ctx->cctx->protocol_ctx, struct cli_protocol); +- +- for (d = 0; d < el->num_values; d++) { +- if (skip_base64_decode) { +- key = el->values[d].data; +- key_len = el->values[d].length; +- } else { +- key = sss_base64_decode(tmp_ctx, (const char *) el->values[d].data, +- &key_len); +- if (key == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- } +- +- ret = sss_packet_grow(pctx->creq->out, +- 3*sizeof(uint32_t) + key_len + fqname_len); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n"); +- goto done; +- } +- sss_packet_get_body(pctx->creq->out, &body, &body_len); +- +- SAFEALIGN_SET_UINT32(body+(*c), 0, c); +- SAFEALIGN_SET_UINT32(body+(*c), fqname_len, c); +- safealign_memcpy(body+(*c), fqname, fqname_len, c); +- SAFEALIGN_SET_UINT32(body+(*c), key_len, c); +- safealign_memcpy(body+(*c), key, key_len, c); +- +- } +- +- ret = EOK; +- +-done: +- talloc_free(tmp_ctx); +- +- return ret; +-} +- +-static errno_t +-ssh_cmd_build_reply(struct ssh_cmd_ctx *cmd_ctx) +-{ +- errno_t ret; +- uint8_t *body; +- size_t body_len; +- size_t c = 0; +- struct ldb_message_element *el = NULL; +- struct ldb_message_element *el_override = NULL; +- struct ldb_message_element *el_orig = NULL; +- struct ldb_message_element *el_user_cert = NULL; +- struct ldb_message_element *el_user_cert_keys = NULL; +- uint32_t count = 0; +- const char *name; +- uint32_t name_len; +- TALLOC_CTX *tmp_ctx; +- struct ssh_ctx *ssh_ctx; +- struct cli_protocol *pctx; +- +- ssh_ctx = talloc_get_type(cmd_ctx->cctx->rctx->pvt_ctx, struct ssh_ctx); +- pctx = talloc_get_type(cmd_ctx->cctx->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) { +- return ret; +- } +- +- tmp_ctx = talloc_new(NULL); +- if (tmp_ctx == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); +- return ENOMEM; +- } +- +- el = ldb_msg_find_element(cmd_ctx->result, SYSDB_SSH_PUBKEY); +- if (el) { +- count = el->num_values; +- } +- +- el_orig = ldb_msg_find_element(cmd_ctx->result, +- ORIGINALAD_PREFIX SYSDB_SSH_PUBKEY); +- if (el_orig) { +- count += el_orig->num_values; +- } +- +- if (DOM_HAS_VIEWS(cmd_ctx->domain)) { +- el_override = ldb_msg_find_element(cmd_ctx->result, +- OVERRIDE_PREFIX SYSDB_SSH_PUBKEY); +- if (el_override) { +- count += el_override->num_values; +- } +- } +- +- el_user_cert = ldb_msg_find_element(cmd_ctx->result, SYSDB_USER_CERT); +- if (el_user_cert) { +- ret = get_valid_certs_keys(cmd_ctx, cmd_ctx, el_user_cert, ssh_ctx, +- &el_user_cert_keys); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "get_valid_certs_keys failed.\n"); +- goto done; +- } +- +- if (el_user_cert_keys) { +- count += el_user_cert_keys->num_values; +- } +- } +- +- ret = sss_packet_grow(pctx->creq->out, 2*sizeof(uint32_t)); +- if (ret != EOK) { +- goto done; +- } +- sss_packet_get_body(pctx->creq->out, &body, &body_len); +- +- SAFEALIGN_SET_UINT32(body+c, count, &c); +- SAFEALIGN_SET_UINT32(body+c, 0, &c); +- +- if (count == 0) { +- ret = EOK; +- goto done; +- } +- +- name = ldb_msg_find_attr_as_string(cmd_ctx->result, SYSDB_NAME, NULL); +- if (!name) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Got unnamed result for [%s@%s]\n", +- cmd_ctx->name, cmd_ctx->domain->name); +- ret = ENOENT; +- goto done; +- } +- +- name_len = strlen(name) + 1; +- +- ret = decode_and_add_base64_data(cmd_ctx, el, false, ssh_ctx, +- name_len, name, &c); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); +- goto done; +- } +- +- ret = decode_and_add_base64_data(cmd_ctx, el_orig, false, ssh_ctx, +- name_len, name, &c); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); +- goto done; +- } +- +- ret = decode_and_add_base64_data(cmd_ctx, el_override, false, ssh_ctx, +- name_len, name, &c); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); +- goto done; +- } +- +- ret = decode_and_add_base64_data(cmd_ctx, el_user_cert_keys, true, ssh_ctx, +- name_len, name, &c); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n"); +- goto done; +- } +- +- ret = EOK; +- +-done: +- +- talloc_free(tmp_ctx); +- +- return ret; +-} +- +-static errno_t +-ssh_cmd_send_error(struct ssh_cmd_ctx *cmd_ctx, +- errno_t error) +-{ +- struct cli_ctx *cctx = cmd_ctx->cctx; +- errno_t ret; +- +- ret = sss_cmd_send_error(cctx, error); +- if (ret != EOK) { +- return ret; +- } +- +- sss_cmd_done(cctx, cmd_ctx); +- +- return EOK; +-} +- +-static errno_t +-ssh_cmd_send_reply(struct ssh_cmd_ctx *cmd_ctx) +-{ +- struct cli_protocol *pctx; +- errno_t ret; +- +- pctx = talloc_get_type(cmd_ctx->cctx->protocol_ctx, struct cli_protocol); +- +- /* create response packet */ +- ret = ssh_cmd_build_reply(cmd_ctx); +- if (ret != EOK) { +- return ret; +- } +- +- sss_packet_set_error(pctx->creq->out, EOK); +- sss_cmd_done(cmd_ctx->cctx, cmd_ctx); +- +- return EOK; +-} +- +-static errno_t +-ssh_cmd_done(struct ssh_cmd_ctx *cmd_ctx, +- errno_t ret) +-{ +- switch (ret) { +- case EOK: +- ret = ssh_cmd_send_reply(cmd_ctx); +- break; +- +- case EAGAIN: +- return EOK; +- +- case EFAULT: +- break; +- +- default: +- ret = ssh_cmd_send_error(cmd_ctx, ret); +- break; +- } +- +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Fatal error, killing connection!\n"); +- talloc_free(cmd_ctx->cctx); +- return EFAULT; +- } +- +- return EOK; +-} +- +-static errno_t +-ssh_cmd_get_user_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx, +- errno_t ret) +-{ +- return ssh_cmd_done(cmd_ctx, ret); +-} +- +-static errno_t +-ssh_cmd_get_host_pubkeys_done(struct ssh_cmd_ctx *cmd_ctx, +- errno_t ret) +-{ +- if (ret == EOK || ret == ENOENT) { +- ssh_host_pubkeys_update_known_hosts(cmd_ctx); +- } +- +- return ssh_cmd_done(cmd_ctx, ret); +-} +- +-struct cli_protocol_version *register_cli_protocol_version(void) +-{ +- static struct cli_protocol_version ssh_cli_protocol_version[] = { +- {0, NULL, NULL} +- }; +- +- return ssh_cli_protocol_version; +-} +- +-struct sss_cmd_table *get_ssh_cmds(void) { +- static struct sss_cmd_table ssh_cmds[] = { +- {SSS_GET_VERSION, sss_cmd_get_version}, +- {SSS_SSH_GET_USER_PUBKEYS, sss_ssh_cmd_get_user_pubkeys}, +- {SSS_SSH_GET_HOST_PUBKEYS, sss_ssh_cmd_get_host_pubkeys}, +- {SSS_CLI_NULL, NULL} +- }; +- +- return ssh_cmds; +-} +-- +2.9.3 + diff --git a/0031-AD-Use-ad_domain-to-match-forest-root-domain-not-the.patch b/0031-AD-Use-ad_domain-to-match-forest-root-domain-not-the.patch new file mode 100644 index 0000000..d69b81c --- /dev/null +++ b/0031-AD-Use-ad_domain-to-match-forest-root-domain-not-the.patch @@ -0,0 +1,76 @@ +From e947a871f7d3cfc4389e981a147fe10bedca0569 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 7 Feb 2017 11:05:47 +0100 +Subject: [PATCH 31/79] AD: Use ad_domain to match forest root domain, not the + configured domain from sssd.conf +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the sssd.conf domain name was different from the joined domain name, +but sssd was joined to the forest root, the AD subdomains code considered +sssd joined to a non-root domain and tried to discover the forest root. + +This could be reproduced by joining sssd to a domain, for example +win.trust.test but calling the sssd.conf domain otherwise, for example: +[domain/addomain] +ad_domain = win.trust.test + +This is/was a frequent use-case in the RHEL world, where authconfig +often names the sssd.conf domain 'default'. + +Without the patch, the trusted domains were not detected. + +Reviewed-by: Pavel Březina +--- + src/providers/ad/ad_subdomains.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index 5e57d218c072a2627f165ae072cb761e1a146048..ad075c19a5824b98092ddf534004680784577c0f 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -948,6 +948,7 @@ static void ad_get_root_domain_done(struct tevent_req *subreq); + static struct tevent_req * + ad_get_root_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, ++ const char *domain, + const char *forest, + struct sdap_handle *sh, + struct ad_subdomains_ctx *sd_ctx) +@@ -968,7 +969,7 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx, + return NULL; + } + +- if (forest != NULL && strcasecmp(sd_ctx->be_ctx->domain->name, forest) == 0) { ++ if (forest != NULL && strcasecmp(domain, forest) == 0) { + state->root_id_ctx = sd_ctx->ad_id_ctx; + state->root_domain_attrs = NULL; + ret = EOK; +@@ -1230,6 +1231,7 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq) + struct ad_subdomains_refresh_state *state; + struct tevent_req *req; + const char *realm; ++ const char *ad_domain; + char *master_sid; + char *flat_name; + char *forest; +@@ -1277,7 +1279,14 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq) + } + } + +- subreq = ad_get_root_domain_send(state, state->ev, forest, ++ ad_domain = dp_opt_get_cstring(state->ad_options->basic, AD_DOMAIN); ++ if (ad_domain == NULL) { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "Missing AD domain name, falling back to sssd domain name\n"); ++ ad_domain = state->sd_ctx->be_ctx->domain->name; ++ } ++ ++ subreq = ad_get_root_domain_send(state, state->ev, ad_domain, forest, + sdap_id_op_handle(state->sdap_op), + state->sd_ctx); + if (subreq == NULL) { +-- +2.9.3 + diff --git a/0032-BUILD-Fix-linking-of-test_sdap_initgr.patch b/0032-BUILD-Fix-linking-of-test_sdap_initgr.patch new file mode 100644 index 0000000..9e9c617 --- /dev/null +++ b/0032-BUILD-Fix-linking-of-test_sdap_initgr.patch @@ -0,0 +1,44 @@ +From e5d8b0e10238490c5d199063c0a258ba53c2ac65 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Wed, 8 Feb 2017 17:58:41 +0100 +Subject: [PATCH 32/79] BUILD: Fix linking of test_sdap_initgr +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There was a linking fialure on debian: +/usr/bin/ld: src/tests/cmocka/test_sdap_initgr-test_sdap_initgr.o: + undefined reference to symbol 'hash_iterate@@DHASH_0.4.3' +//usr/lib64/libdhash.so.1: error adding symbols: DSO missing from command line +collect2: error: ld returned 1 exit status + +This patch adds some missing libraries and remove unnecessary libraries. +Bug was intoduced in commit 0b7ded15e53b3f31f1570c366f04bc41e5761929 + +Reviewed-by: Michal Židek +--- + Makefile.am | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 713a83ce0b5c2b8d71495ff05b52e52e413b5c95..2304b39c7eb75225f7cd8cbc30d23592506c146e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -2879,11 +2879,12 @@ test_sdap_initgr_CFLAGS = \ + test_sdap_initgr_LDADD = \ + $(CMOCKA_LIBS) \ + $(POPT_LIBS) \ ++ $(DHASH_LIBS) \ + $(TALLOC_LIBS) \ ++ $(TEVENT_LIBS) \ ++ $(LDB_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ + libsss_ldap_common.la \ +- libsss_ad_tests.la \ +- libsss_idmap.la \ + libsss_test_common.la \ + libdlopen_test_providers.la \ + $(NULL) +-- +2.9.3 + diff --git a/0033-ssh-fix-typo.patch b/0033-ssh-fix-typo.patch new file mode 100644 index 0000000..a25c81b --- /dev/null +++ b/0033-ssh-fix-typo.patch @@ -0,0 +1,31 @@ +From 2ffa245e79a5ed66e69d141f4001c13697e01450 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 8 Feb 2017 13:22:11 +0100 +Subject: [PATCH 33/79] ssh: fix typo +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Those macros are the same so there is no functional difference. + +Reviewed-by: Lukáš Slebodník +--- + src/responder/ssh/ssh_cmd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/responder/ssh/ssh_cmd.c b/src/responder/ssh/ssh_cmd.c +index a1188280dc2d1f73c726aec7c203692a63c37a32..1b9aff2b5053b436a9a0bf2797d812a954f25984 100644 +--- a/src/responder/ssh/ssh_cmd.c ++++ b/src/responder/ssh/ssh_cmd.c +@@ -213,7 +213,7 @@ static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq) + cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx); + ssh_ctx = talloc_get_type(cmd_ctx->cli_ctx->rctx->pvt_ctx, struct ssh_ctx); + +- ret = cache_req_user_by_name_attrs_recv(cmd_ctx, subreq, &result); ++ ret = cache_req_host_by_name_recv(cmd_ctx, subreq, &result); + talloc_zfree(subreq); + + if (ret == EOK || ret == ENOENT) { +-- +2.9.3 + diff --git a/0034-cache_req-always-go-to-dp-first-when-looking-up-host.patch b/0034-cache_req-always-go-to-dp-first-when-looking-up-host.patch new file mode 100644 index 0000000..19b8dc8 --- /dev/null +++ b/0034-cache_req-always-go-to-dp-first-when-looking-up-host.patch @@ -0,0 +1,32 @@ +From d9780d2860b2f2c9d707bfd8f2fc72099b9545d7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 8 Feb 2017 13:22:42 +0100 +Subject: [PATCH 34/79] cache_req: always go to dp first when looking up host +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We need to always lookup host in DP first to update host certificates so +we are consinstent during ssh authentication. + +Reviewed-by: Lukáš Slebodník +--- + src/responder/common/cache_req/plugins/cache_req_host_by_name.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +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 18511e33bc18e44f418a26764f066ff287092d26..77b46831fec3abc4126ef9d9be67221469801094 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 +@@ -77,7 +77,7 @@ const struct cache_req_plugin cache_req_host_by_name = { + .attr_expiration = SYSDB_CACHE_EXPIRE, + .parse_name = true, + .ignore_default_domain = true, +- .bypass_cache = false, ++ .bypass_cache = true, + .only_one_result = true, + .search_all_domains = false, + .require_enumeration = false, +-- +2.9.3 + diff --git a/0035-MONITOR-Wrap-up-sending-sd_notify-ready-into-a-new-f.patch b/0035-MONITOR-Wrap-up-sending-sd_notify-ready-into-a-new-f.patch new file mode 100644 index 0000000..cb1224f --- /dev/null +++ b/0035-MONITOR-Wrap-up-sending-sd_notify-ready-into-a-new-f.patch @@ -0,0 +1,74 @@ +From 040ade7b2e11fecf615aedf58592cc7245900e86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Sun, 5 Feb 2017 01:48:35 +0100 +Subject: [PATCH 35/79] MONITOR: Wrap up sending sd_notify "ready" into a new + function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This new function will be used later on in this series as we also will +need to notify systemd that we're up in at least one more scenario (for +now). + +Related: +https://fedorahosted.org/sssd/ticket/3299 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +--- + src/monitor/monitor.c | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index b82c6e5fb651796e977085a1fcb87330632fbf3b..f55a89edc38900c3eaaf2a294fb26125e571cf82 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -487,6 +487,26 @@ static void svc_child_info(struct mt_svc *svc, int wait_status) + } + } + ++static int notify_startup(void) ++{ ++#ifdef HAVE_SYSTEMD ++ int ret; ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Sending startup notification to systemd\n"); ++ ret = sd_notify(0, "READY=1"); ++ if (ret < 0) { ++ ret = -ret; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Error sending notification to systemd %d: %s\n", ++ ret, sss_strerror(ret)); ++ ++ return ret; ++ } ++#endif ++ ++ return EOK; ++} ++ + static int mark_service_as_started(struct mt_svc *svc) + { + struct mt_ctx *ctx = svc->mt_ctx; +@@ -557,15 +577,7 @@ static int mark_service_as_started(struct mt_svc *svc) + + ctx->pid_file_created = true; + +-#ifdef HAVE_SYSTEMD +- DEBUG(SSSDBG_TRACE_FUNC, "Sending startup notification to systemd\n"); +- ret = sd_notify(0, "READY=1"); +- if (ret < 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Error sending notification to systemd %d: %s\n", +- -ret, strerror(-ret)); +- } +-#endif ++ notify_startup(); + + /* Initialization is complete, terminate parent process if in daemon + * mode. Make sure we send the signal to the right process */ +-- +2.9.3 + diff --git a/0036-MONITOR-Don-t-timeout-if-using-local-provider-socket.patch b/0036-MONITOR-Don-t-timeout-if-using-local-provider-socket.patch new file mode 100644 index 0000000..4a099b6 --- /dev/null +++ b/0036-MONITOR-Don-t-timeout-if-using-local-provider-socket.patch @@ -0,0 +1,51 @@ +From 00c0b7bc6969d31deab9e8e7541b4a6483b78b3e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Sun, 5 Feb 2017 01:55:56 +0100 +Subject: [PATCH 36/79] MONITOR: Don't timeout if using local provider + + socket-activated responders +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When using only the local provider with socket-activated services SSSD +ends up never notifying systemd its startup has been done, as notifying +systemd is done *only* when a service (provider or responder) is started +up, leading SSSD's startup to fail due to a timeout. + +So, in order to avoid this situation, let's just notify the startup +earlier in case we have *only* socket-activated services and the *only* +provider set up is the LOCAL one. + +Resolves: +https://fedorahosted.org/sssd/ticket/3299 + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +--- + src/monitor/monitor.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index f55a89edc38900c3eaaf2a294fb26125e571cf82..1fa3d4baf579f15b9f93355a4b0c8b9d706bbacf 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -2403,6 +2403,15 @@ static int monitor_process_init(struct mt_ctx *ctx, + } + } + ++ /* When the only provider set up is the local one (num_providers == 0) and ++ * there's no responder explicitly set up it means that we should notify ++ * systemd that SSSD is ready right now as any other provider/responder ++ * would be able to do so and the SSSD would end up hitting a systemd ++ * timeout! */ ++ if (num_providers == 0 && ctx->services == NULL) { ++ ret = notify_startup(); ++ } ++ + return EOK; + } + +-- +2.9.3 + diff --git a/0037-SUDO-Only-store-lowercased-attribute-value-once.patch b/0037-SUDO-Only-store-lowercased-attribute-value-once.patch new file mode 100644 index 0000000..5aef2d7 --- /dev/null +++ b/0037-SUDO-Only-store-lowercased-attribute-value-once.patch @@ -0,0 +1,83 @@ +From a5ecc93abb01cece628fdef04ebad43bba267419 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Sun, 5 Feb 2017 20:25:23 +0100 +Subject: [PATCH 37/79] SUDO: Only store lowercased attribute value once +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current code doesn't handle the situation where lowercasing the +sudoUser attribute would yield the same value again. + +For example: + sudoUser: TUSER + sudoUser tuser +would break. + +This patch switches to using the utility function +sysdb_attrs_add_lower_case_string() which already checks for duplicates. + +Resolves: +https://fedorahosted.org/sssd/ticket/3301 + +Reviewed-by: Fabiano Fidêncio +Reviewed-by: Pavel Březina +--- + src/db/sysdb_sudo.c | 17 +++-------------- + src/tests/cmocka/test_sysdb_sudo.c | 5 +++++ + 2 files changed, 8 insertions(+), 14 deletions(-) + +diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c +index f5160f19012028f92723b9012fad85d803aa5137..97a1bee99c0255579f42cc7263d3d755429cd417 100644 +--- a/src/db/sysdb_sudo.c ++++ b/src/db/sysdb_sudo.c +@@ -857,7 +857,6 @@ static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain, + { + TALLOC_CTX *tmp_ctx; + const char **users = NULL; +- const char *lowered = NULL; + errno_t ret; + + if (domain->case_sensitive == true || rule == NULL) { +@@ -884,19 +883,9 @@ static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain, + } + + for (int i = 0; users[i] != NULL; i++) { +- lowered = sss_tc_utf8_str_tolower(tmp_ctx, users[i]); +- if (lowered == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n"); +- ret = ENOMEM; +- goto done; +- } +- +- if (strcmp(users[i], lowered) == 0) { +- /* It protects us from adding duplicate. */ +- continue; +- } +- +- ret = sysdb_attrs_add_string(rule, SYSDB_SUDO_CACHE_AT_USER, lowered); ++ ret = sysdb_attrs_add_lower_case_string(rule, true, ++ SYSDB_SUDO_CACHE_AT_USER, ++ users[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Unable to add %s attribute [%d]: %s\n", +diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c +index f21ff3655efbdc5b66a1fdbc24a51ec8174c3c8c..34afe120d97e99e3213a85bf7489a5e0f6309e4b 100644 +--- a/src/tests/cmocka/test_sysdb_sudo.c ++++ b/src/tests/cmocka/test_sysdb_sudo.c +@@ -335,6 +335,11 @@ void test_store_sudo_case_insensitive(void **state) + + test_ctx->tctx->dom->case_sensitive = false; + ++ ret = sysdb_attrs_add_lower_case_string(rule, false, ++ SYSDB_SUDO_CACHE_AT_USER, ++ users[0].name); ++ assert_int_equal(ret, EOK); ++ + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + +-- +2.9.3 + diff --git a/0038-NEGCACHE-Add-API-to-reset-all-users-and-groups.patch b/0038-NEGCACHE-Add-API-to-reset-all-users-and-groups.patch new file mode 100644 index 0000000..c682b70 --- /dev/null +++ b/0038-NEGCACHE-Add-API-to-reset-all-users-and-groups.patch @@ -0,0 +1,190 @@ +From 99a32e4f5164e174d5a3ffa5a1fe622075a8fe45 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 2 Nov 2016 16:59:12 +0100 +Subject: [PATCH 38/79] NEGCACHE: Add API to reset all users and groups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a negative cache API to reset negatively cached users and groups. +This will be used when the files back end finishes enumeration to make +sure all results are available. + +Reviewed-by: Pavel Březina +--- + src/responder/common/negcache.c | 56 ++++++++++++++++++++++++++++++++ + src/responder/common/negcache.h | 2 ++ + src/tests/cmocka/test_negcache.c | 70 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 128 insertions(+) + +diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c +index 5b7ad69f432518be94b88e92e24265add722c852..944a06e158f778948c16bb931f0af5659a00b13b 100644 +--- a/src/responder/common/negcache.c ++++ b/src/responder/common/negcache.c +@@ -674,6 +674,62 @@ int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx) + return EOK; + } + ++static int delete_prefix(struct tdb_context *tdb, ++ TDB_DATA key, TDB_DATA data, void *state) ++{ ++ const char *prefix = (const char *) state; ++ ++ if (strncmp((char *)key.dptr, prefix, strlen(prefix) - 1) != 0) { ++ /* not interested in this key */ ++ return 0; ++ } ++ ++ return tdb_delete(tdb, key); ++} ++ ++static int sss_ncache_reset_pfx(struct sss_nc_ctx *ctx, ++ const char **prefixes) ++{ ++ int ret; ++ ++ if (prefixes == NULL) { ++ return EOK; ++ } ++ ++ for (int i = 0; prefixes[i] != NULL; i++) { ++ ret = tdb_traverse(ctx->tdb, ++ delete_prefix, ++ discard_const(prefixes[i])); ++ if (ret < 0) { ++ return EIO; ++ } ++ } ++ ++ return EOK; ++} ++ ++int sss_ncache_reset_users(struct sss_nc_ctx *ctx) ++{ ++ const char *prefixes[] = { ++ NC_USER_PREFIX, ++ NC_UID_PREFIX, ++ NULL, ++ }; ++ ++ return sss_ncache_reset_pfx(ctx, prefixes); ++} ++ ++int sss_ncache_reset_groups(struct sss_nc_ctx *ctx) ++{ ++ const char *prefixes[] = { ++ NC_GROUP_PREFIX, ++ NC_GID_PREFIX, ++ NULL, ++ }; ++ ++ return sss_ncache_reset_pfx(ctx, prefixes); ++} ++ + errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + struct confdb_ctx *cdb, + struct resp_ctx *rctx) +diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h +index 377f97c8b3b20ec5b4a284e08d891737e2e25225..8af736a67aada91d6ac42495399f5de469dec753 100644 +--- a/src/responder/common/negcache.h ++++ b/src/responder/common/negcache.h +@@ -78,6 +78,8 @@ int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent, + uint16_t port, const char *proto); + + int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx); ++int sss_ncache_reset_users(struct sss_nc_ctx *ctx); ++int sss_ncache_reset_groups(struct sss_nc_ctx *ctx); + + struct resp_ctx; + +diff --git a/src/tests/cmocka/test_negcache.c b/src/tests/cmocka/test_negcache.c +index 14e4fa639a056d712b2453230745d7dc49853dec..d608c20ad3248c80e68029c8c27b826395a61ddc 100644 +--- a/src/tests/cmocka/test_negcache.c ++++ b/src/tests/cmocka/test_negcache.c +@@ -785,6 +785,74 @@ static void test_sss_ncache_reset_prepopulate(void **state) + ret = check_group_in_ncache(ncache, dom2, "testgroup2"); + assert_int_equal(ret, EEXIST); + } ++ ++static void test_sss_ncache_reset(void **state) ++{ ++ errno_t ret; ++ struct test_state *ts; ++ struct sss_domain_info *dom; ++ ++ ts = talloc_get_type_abort(*state, struct test_state); ++ dom = talloc(ts, struct sss_domain_info); ++ assert_non_null(dom); ++ dom->case_sensitive = true; ++ ++ dom->name = discard_const_p(char, TEST_DOM_NAME); ++ ++ /* Set users */ ++ ret = sss_ncache_check_uid(ts->ctx, NULL, 123); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_set_uid(ts->ctx, false, NULL, 123); ++ assert_int_equal(ret, EOK); ++ ret = sss_ncache_check_uid(ts->ctx, NULL, 123); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = sss_ncache_check_user(ts->ctx, dom, "foo"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_set_user(ts->ctx, false, dom, "foo"); ++ assert_int_equal(ret, EOK); ++ ret = sss_ncache_check_user(ts->ctx, dom, "foo"); ++ assert_int_equal(ret, EEXIST); ++ ++ /* Set groups */ ++ ret = sss_ncache_check_gid(ts->ctx, NULL, 456); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_set_gid(ts->ctx, false, NULL, 456); ++ assert_int_equal(ret, EOK); ++ ret = sss_ncache_check_gid(ts->ctx, NULL, 456); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = sss_ncache_check_group(ts->ctx, dom, "bar"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_set_group(ts->ctx, false, dom, "bar"); ++ assert_int_equal(ret, EOK); ++ ret = sss_ncache_check_group(ts->ctx, dom, "bar"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = sss_ncache_reset_users(ts->ctx); ++ assert_int_equal(ret, EOK); ++ ++ /* Users are no longer negatively cached */ ++ ret = sss_ncache_check_user(ts->ctx, dom, "foo"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_uid(ts->ctx, NULL, 123); ++ assert_int_equal(ret, ENOENT); ++ ++ /* Groups still are */ ++ ret = sss_ncache_check_gid(ts->ctx, NULL, 456); ++ assert_int_equal(ret, EEXIST); ++ ret = sss_ncache_check_group(ts->ctx, dom, "bar"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = sss_ncache_reset_groups(ts->ctx); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_ncache_check_gid(ts->ctx, NULL, 456); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_group(ts->ctx, dom, "bar"); ++ assert_int_equal(ret, ENOENT); ++} ++ + int main(void) + { + int rv; +@@ -809,6 +877,8 @@ int main(void) + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset_prepopulate, + setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_ncache_reset, ++ setup, teardown), + }; + + tests_set_cwd(); +-- +2.9.3 + diff --git a/0039-NSS-Add-sbus-interface-to-clear-memory-cache.patch b/0039-NSS-Add-sbus-interface-to-clear-memory-cache.patch new file mode 100644 index 0000000..83ecf56 --- /dev/null +++ b/0039-NSS-Add-sbus-interface-to-clear-memory-cache.patch @@ -0,0 +1,193 @@ +From c3a225d4d735d3a01883125592dda7a030a64e00 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 2 Nov 2016 15:59:37 +0100 +Subject: [PATCH 39/79] NSS: Add sbus interface to clear memory cache +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds three new NSS interface sbus methods to disable memory caches of +users, groups and initgroups. It's enough to add this interface to the +NSS responder because the NSS responder is the only writer to the memory +cache. + +Reviewed-by: Pavel Březina +--- + src/responder/nss/nss_iface.c | 40 ++++++++++++++++++++++++++++++++- + src/responder/nss/nss_iface.xml | 6 +++++ + src/responder/nss/nss_iface_generated.c | 39 ++++++++++++++++++++++++++++++++ + src/responder/nss/nss_iface_generated.h | 15 +++++++++++++ + 4 files changed, 99 insertions(+), 1 deletion(-) + +diff --git a/src/responder/nss/nss_iface.c b/src/responder/nss/nss_iface.c +index 58c70c8a01bfcc143eda14c9185672302345ef75..4a38681b54d6c9d0ac9adece69bdebb3d305fcf9 100644 +--- a/src/responder/nss/nss_iface.c ++++ b/src/responder/nss/nss_iface.c +@@ -144,6 +144,41 @@ done: + talloc_free(tmp_ctx); + } + ++int nss_memorycache_invalidate_users(struct sbus_request *req, void *data) ++{ ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); ++ struct nss_ctx *nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx); ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Invalidating all users in memory cache\n"); ++ sss_mmap_cache_reset(nctx->pwd_mc_ctx); ++ ++ return iface_nss_memorycache_InvalidateAllUsers_finish(req); ++} ++ ++int nss_memorycache_invalidate_groups(struct sbus_request *req, void *data) ++{ ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); ++ struct nss_ctx *nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx); ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Invalidating all groups in memory cache\n"); ++ sss_mmap_cache_reset(nctx->grp_mc_ctx); ++ ++ return iface_nss_memorycache_InvalidateAllGroups_finish(req); ++} ++ ++int nss_memorycache_invalidate_initgroups(struct sbus_request *req, void *data) ++{ ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); ++ struct nss_ctx *nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx); ++ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Invalidating all initgroup records in memory cache\n"); ++ sss_mmap_cache_reset(nctx->initgr_mc_ctx); ++ ++ return iface_nss_memorycache_InvalidateAllInitgrRecords_finish(req); ++} ++ ++ + int nss_memorycache_update_initgroups(struct sbus_request *sbus_req, + void *data, + const char *user, +@@ -164,7 +199,10 @@ int nss_memorycache_update_initgroups(struct sbus_request *sbus_req, + + struct iface_nss_memorycache iface_nss_memorycache = { + { &iface_nss_memorycache_meta, 0 }, +- .UpdateInitgroups = nss_memorycache_update_initgroups ++ .UpdateInitgroups = nss_memorycache_update_initgroups, ++ .InvalidateAllUsers = nss_memorycache_invalidate_users, ++ .InvalidateAllGroups = nss_memorycache_invalidate_groups, ++ .InvalidateAllInitgrRecords = nss_memorycache_invalidate_initgroups, + }; + + static struct sbus_iface_map iface_map[] = { +diff --git a/src/responder/nss/nss_iface.xml b/src/responder/nss/nss_iface.xml +index b7cc4deb77135a592bad2ca62570f206231129b7..79e42c7424e800601bdc2dbe9ecd3e4a49829d68 100644 +--- a/src/responder/nss/nss_iface.xml ++++ b/src/responder/nss/nss_iface.xml +@@ -8,5 +8,11 @@ + + + ++ ++ ++ ++ ++ ++ + + +diff --git a/src/responder/nss/nss_iface_generated.c b/src/responder/nss/nss_iface_generated.c +index 2d0031090e33df9c9e9d9fbf1a18825026509803..4c07080148f62c1d8e18e51e1be62bb261a13566 100644 +--- a/src/responder/nss/nss_iface_generated.c ++++ b/src/responder/nss/nss_iface_generated.c +@@ -23,6 +23,24 @@ int iface_nss_memorycache_UpdateInitgroups_finish(struct sbus_request *req) + DBUS_TYPE_INVALID); + } + ++int iface_nss_memorycache_InvalidateAllUsers_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ ++int iface_nss_memorycache_InvalidateAllGroups_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ ++int iface_nss_memorycache_InvalidateAllInitgrRecords_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ + /* methods for org.freedesktop.sssd.nss.MemoryCache */ + const struct sbus_method_meta iface_nss_memorycache__methods[] = { + { +@@ -32,6 +50,27 @@ const struct sbus_method_meta iface_nss_memorycache__methods[] = { + offsetof(struct iface_nss_memorycache, UpdateInitgroups), + invoke_ssau_method, + }, ++ { ++ "InvalidateAllUsers", /* name */ ++ NULL, /* no in_args */ ++ NULL, /* no out_args */ ++ offsetof(struct iface_nss_memorycache, InvalidateAllUsers), ++ NULL, /* no invoker */ ++ }, ++ { ++ "InvalidateAllGroups", /* name */ ++ NULL, /* no in_args */ ++ NULL, /* no out_args */ ++ offsetof(struct iface_nss_memorycache, InvalidateAllGroups), ++ NULL, /* no invoker */ ++ }, ++ { ++ "InvalidateAllInitgrRecords", /* name */ ++ NULL, /* no in_args */ ++ NULL, /* no out_args */ ++ offsetof(struct iface_nss_memorycache, InvalidateAllInitgrRecords), ++ NULL, /* no invoker */ ++ }, + { NULL, } + }; + +diff --git a/src/responder/nss/nss_iface_generated.h b/src/responder/nss/nss_iface_generated.h +index ad902482a9be03a60cbf3663b6f771d0a2020b88..6f4d13a35dc5cbe33182ad8744769b37ce449d50 100644 +--- a/src/responder/nss/nss_iface_generated.h ++++ b/src/responder/nss/nss_iface_generated.h +@@ -14,6 +14,9 @@ + /* constants for org.freedesktop.sssd.nss.MemoryCache */ + #define IFACE_NSS_MEMORYCACHE "org.freedesktop.sssd.nss.MemoryCache" + #define IFACE_NSS_MEMORYCACHE_UPDATEINITGROUPS "UpdateInitgroups" ++#define IFACE_NSS_MEMORYCACHE_INVALIDATEALLUSERS "InvalidateAllUsers" ++#define IFACE_NSS_MEMORYCACHE_INVALIDATEALLGROUPS "InvalidateAllGroups" ++#define IFACE_NSS_MEMORYCACHE_INVALIDATEALLINITGRRECORDS "InvalidateAllInitgrRecords" + + /* ------------------------------------------------------------------------ + * DBus handlers +@@ -37,11 +40,23 @@ + struct iface_nss_memorycache { + struct sbus_vtable vtable; /* derive from sbus_vtable */ + int (*UpdateInitgroups)(struct sbus_request *req, void *data, const char *arg_user, const char *arg_domain, uint32_t arg_groups[], int len_groups); ++ int (*InvalidateAllUsers)(struct sbus_request *req, void *data); ++ int (*InvalidateAllGroups)(struct sbus_request *req, void *data); ++ int (*InvalidateAllInitgrRecords)(struct sbus_request *req, void *data); + }; + + /* finish function for UpdateInitgroups */ + int iface_nss_memorycache_UpdateInitgroups_finish(struct sbus_request *req); + ++/* finish function for InvalidateAllUsers */ ++int iface_nss_memorycache_InvalidateAllUsers_finish(struct sbus_request *req); ++ ++/* finish function for InvalidateAllGroups */ ++int iface_nss_memorycache_InvalidateAllGroups_finish(struct sbus_request *req); ++ ++/* finish function for InvalidateAllInitgrRecords */ ++int iface_nss_memorycache_InvalidateAllInitgrRecords_finish(struct sbus_request *req); ++ + /* ------------------------------------------------------------------------ + * DBus Interface Metadata + * +-- +2.9.3 + diff --git a/0040-NSS-Rename-the-interface-to-invalidate-memory-cache-.patch b/0040-NSS-Rename-the-interface-to-invalidate-memory-cache-.patch new file mode 100644 index 0000000..47820c4 --- /dev/null +++ b/0040-NSS-Rename-the-interface-to-invalidate-memory-cache-.patch @@ -0,0 +1,111 @@ +From f2047f6c5b56d6759bd8e6d504f572a593476c65 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Mon, 23 Jan 2017 22:55:20 +0100 +Subject: [PATCH 40/79] NSS: Rename the interface to invalidate memory cache + initgroup records for consistency + +Reviewed-by: Jakub Hrozek +--- + src/responder/nss/nss_iface.c | 4 ++-- + src/responder/nss/nss_iface.xml | 2 +- + src/responder/nss/nss_iface_generated.c | 6 +++--- + src/responder/nss/nss_iface_generated.h | 8 ++++---- + 4 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/src/responder/nss/nss_iface.c b/src/responder/nss/nss_iface.c +index 4a38681b54d6c9d0ac9adece69bdebb3d305fcf9..fee95f8fc6806d2e70112d02690469fb094efa17 100644 +--- a/src/responder/nss/nss_iface.c ++++ b/src/responder/nss/nss_iface.c +@@ -175,7 +175,7 @@ int nss_memorycache_invalidate_initgroups(struct sbus_request *req, void *data) + "Invalidating all initgroup records in memory cache\n"); + sss_mmap_cache_reset(nctx->initgr_mc_ctx); + +- return iface_nss_memorycache_InvalidateAllInitgrRecords_finish(req); ++ return iface_nss_memorycache_InvalidateAllInitgroups_finish(req); + } + + +@@ -202,7 +202,7 @@ struct iface_nss_memorycache iface_nss_memorycache = { + .UpdateInitgroups = nss_memorycache_update_initgroups, + .InvalidateAllUsers = nss_memorycache_invalidate_users, + .InvalidateAllGroups = nss_memorycache_invalidate_groups, +- .InvalidateAllInitgrRecords = nss_memorycache_invalidate_initgroups, ++ .InvalidateAllInitgroups = nss_memorycache_invalidate_initgroups, + }; + + static struct sbus_iface_map iface_map[] = { +diff --git a/src/responder/nss/nss_iface.xml b/src/responder/nss/nss_iface.xml +index 79e42c7424e800601bdc2dbe9ecd3e4a49829d68..27aae019758c49ab7ec04161394d58da88077b60 100644 +--- a/src/responder/nss/nss_iface.xml ++++ b/src/responder/nss/nss_iface.xml +@@ -12,7 +12,7 @@ + + + +- ++ + + + +diff --git a/src/responder/nss/nss_iface_generated.c b/src/responder/nss/nss_iface_generated.c +index 4c07080148f62c1d8e18e51e1be62bb261a13566..e4f3aec2d1394fbbe75185acfa68b6f947c0e142 100644 +--- a/src/responder/nss/nss_iface_generated.c ++++ b/src/responder/nss/nss_iface_generated.c +@@ -35,7 +35,7 @@ int iface_nss_memorycache_InvalidateAllGroups_finish(struct sbus_request *req) + DBUS_TYPE_INVALID); + } + +-int iface_nss_memorycache_InvalidateAllInitgrRecords_finish(struct sbus_request *req) ++int iface_nss_memorycache_InvalidateAllInitgroups_finish(struct sbus_request *req) + { + return sbus_request_return_and_finish(req, + DBUS_TYPE_INVALID); +@@ -65,10 +65,10 @@ const struct sbus_method_meta iface_nss_memorycache__methods[] = { + NULL, /* no invoker */ + }, + { +- "InvalidateAllInitgrRecords", /* name */ ++ "InvalidateAllInitgroups", /* name */ + NULL, /* no in_args */ + NULL, /* no out_args */ +- offsetof(struct iface_nss_memorycache, InvalidateAllInitgrRecords), ++ offsetof(struct iface_nss_memorycache, InvalidateAllInitgroups), + NULL, /* no invoker */ + }, + { NULL, } +diff --git a/src/responder/nss/nss_iface_generated.h b/src/responder/nss/nss_iface_generated.h +index 6f4d13a35dc5cbe33182ad8744769b37ce449d50..cacadc57808d6f16998889cccf0c5973682bbe5d 100644 +--- a/src/responder/nss/nss_iface_generated.h ++++ b/src/responder/nss/nss_iface_generated.h +@@ -16,7 +16,7 @@ + #define IFACE_NSS_MEMORYCACHE_UPDATEINITGROUPS "UpdateInitgroups" + #define IFACE_NSS_MEMORYCACHE_INVALIDATEALLUSERS "InvalidateAllUsers" + #define IFACE_NSS_MEMORYCACHE_INVALIDATEALLGROUPS "InvalidateAllGroups" +-#define IFACE_NSS_MEMORYCACHE_INVALIDATEALLINITGRRECORDS "InvalidateAllInitgrRecords" ++#define IFACE_NSS_MEMORYCACHE_INVALIDATEALLINITGROUPS "InvalidateAllInitgroups" + + /* ------------------------------------------------------------------------ + * DBus handlers +@@ -42,7 +42,7 @@ struct iface_nss_memorycache { + int (*UpdateInitgroups)(struct sbus_request *req, void *data, const char *arg_user, const char *arg_domain, uint32_t arg_groups[], int len_groups); + int (*InvalidateAllUsers)(struct sbus_request *req, void *data); + int (*InvalidateAllGroups)(struct sbus_request *req, void *data); +- int (*InvalidateAllInitgrRecords)(struct sbus_request *req, void *data); ++ int (*InvalidateAllInitgroups)(struct sbus_request *req, void *data); + }; + + /* finish function for UpdateInitgroups */ +@@ -54,8 +54,8 @@ int iface_nss_memorycache_InvalidateAllUsers_finish(struct sbus_request *req); + /* finish function for InvalidateAllGroups */ + int iface_nss_memorycache_InvalidateAllGroups_finish(struct sbus_request *req); + +-/* finish function for InvalidateAllInitgrRecords */ +-int iface_nss_memorycache_InvalidateAllInitgrRecords_finish(struct sbus_request *req); ++/* finish function for InvalidateAllInitgroups */ ++int iface_nss_memorycache_InvalidateAllInitgroups_finish(struct sbus_request *req); + + /* ------------------------------------------------------------------------ + * DBus Interface Metadata +-- +2.9.3 + diff --git a/0041-UTIL-Add-a-new-domain-state-called-DOM_INCONSISTENT.patch b/0041-UTIL-Add-a-new-domain-state-called-DOM_INCONSISTENT.patch new file mode 100644 index 0000000..32d6b9d --- /dev/null +++ b/0041-UTIL-Add-a-new-domain-state-called-DOM_INCONSISTENT.patch @@ -0,0 +1,56 @@ +From 2d1a59f6c2cf3cf4667cf2d01b2d780db916db42 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 10 Feb 2017 12:22:23 +0100 +Subject: [PATCH 41/79] UTIL: Add a new domain state called DOM_INCONSISTENT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is a new domain state that indicates to the responder that it +should always send a DP request because the provider is rebuilding the +cache. + +Currently it will be only used by the files provider when it is updating +the cache to make sure sssd always returns current data and updating the +cache from files is not as racy. + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.h | 4 ++++ + src/providers/data_provider_be.c | 4 +++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index dd6ac77f5a787b0434b56fccba49aa195b13297a..7c944698157619652441fb0722a4363053d6a8f3 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -248,6 +248,10 @@ enum sss_domain_state { + * return cached data + */ + DOM_INACTIVE, ++ /** Domain is being updated. Responders should ignore cached data and ++ * always contact the DP ++ */ ++ DOM_INCONSISTENT, + }; + + /** +diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c +index 12b5f43d0b5d514ce06ae8875ae2a75d37f84f88..7e7b74c36993489a93c15ad9acb33af7864f852d 100644 +--- a/src/providers/data_provider_be.c ++++ b/src/providers/data_provider_be.c +@@ -166,8 +166,10 @@ static void be_mark_subdom_offline(struct sss_domain_info *subdom, + tv = tevent_timeval_current_ofs(reset_status_timeout, 0); + + switch (subdom->state) { ++ case DOM_INCONSISTENT: + case DOM_DISABLED: +- DEBUG(SSSDBG_MINOR_FAILURE, "Won't touch disabled subdomain\n"); ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Won't touch disabled or inconsistent subdomain\n"); + return; + case DOM_INACTIVE: + DEBUG(SSSDBG_TRACE_ALL, "Subdomain already inactive\n"); +-- +2.9.3 + diff --git a/0042-RESPONDER-Add-a-responder-sbus-interface-to-set-doma.patch b/0042-RESPONDER-Add-a-responder-sbus-interface-to-set-doma.patch new file mode 100644 index 0000000..3799aa7 --- /dev/null +++ b/0042-RESPONDER-Add-a-responder-sbus-interface-to-set-doma.patch @@ -0,0 +1,561 @@ +From c109f063b4469818fd335b8b509f0458e7b33b0a Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Sun, 30 Oct 2016 07:05:43 +0100 +Subject: [PATCH 42/79] RESPONDER: Add a responder sbus interface to set domain + state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a generic responder s-bus interface that all responders implement. +The interface currently contains methods that make it possible for a sssd +domain to be marked as active or inconsistent by a back end. + +In the future, this commit will be superseded by sbus signals. + +Reviewed-by: Pavel Březina +--- + Makefile.am | 18 ++++- + src/responder/common/iface/responder_domain.c | 73 ++++++++++++++++++++ + src/responder/common/iface/responder_iface.c | 36 ++++++++++ + src/responder/common/iface/responder_iface.h | 37 ++++++++++ + src/responder/common/iface/responder_iface.xml | 13 ++++ + .../common/iface/responder_iface_generated.c | 78 ++++++++++++++++++++++ + .../common/iface/responder_iface_generated.h | 63 +++++++++++++++++ + src/responder/common/responder_common.c | 15 +++++ + src/tests/cwrap/Makefile.am | 12 ++++ + src/util/domain_info_utils.c | 19 ++++++ + 10 files changed, 362 insertions(+), 2 deletions(-) + create mode 100644 src/responder/common/iface/responder_domain.c + create mode 100644 src/responder/common/iface/responder_iface.c + create mode 100644 src/responder/common/iface/responder_iface.h + create mode 100644 src/responder/common/iface/responder_iface.xml + create mode 100644 src/responder/common/iface/responder_iface_generated.c + create mode 100644 src/responder/common/iface/responder_iface_generated.h + +diff --git a/Makefile.am b/Makefile.am +index 2304b39c7eb75225f7cd8cbc30d23592506c146e..32f62b5b4391e5d6efb7f7dc19e9b29eaa658550 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -516,6 +516,12 @@ SSSD_CACHE_REQ_OBJ = \ + src/responder/common/cache_req/plugins/cache_req_host_by_name.c \ + $(NULL) + ++SSSD_RESPONDER_IFACE_OBJ = \ ++ src/responder/common/iface/responder_iface.c \ ++ src/responder/common/iface/responder_domain.c \ ++ src/responder/common/iface/responder_iface_generated.c \ ++ $(NULL) ++ + SSSD_RESPONDER_OBJ = \ + src/responder/common/negcache_files.c \ + src/responder/common/negcache.c \ +@@ -530,6 +536,7 @@ SSSD_RESPONDER_OBJ = \ + src/responder/common/data_provider/rdp_client.c \ + src/monitor/monitor_iface_generated.c \ + src/providers/data_provider_req.c \ ++ $(SSSD_RESPONDER_IFACE_OBJ) \ + $(SSSD_CACHE_REQ_OBJ) \ + $(NULL) + +@@ -640,6 +647,8 @@ dist_noinst_HEADERS = \ + src/responder/common/responder.h \ + src/responder/common/responder_packet.h \ + src/responder/common/responder_sbus.h \ ++ 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_plugin.h \ + src/responder/common/cache_req/cache_req_private.h \ +@@ -1221,7 +1230,9 @@ CODEGEN_XML = \ + $(srcdir)/src/providers/data_provider/dp_iface.xml \ + $(srcdir)/src/providers/proxy/proxy_iface.xml \ + $(srcdir)/src/responder/ifp/ifp_iface.xml \ +- $(srcdir)/src/responder/nss/nss_iface.xml ++ $(srcdir)/src/responder/nss/nss_iface.xml \ ++ $(srcdir)/src/responder/common/iface/responder_iface.xml \ ++ $(NULL) + + SBUS_CODEGEN = src/sbus/sbus_codegen + +@@ -2038,7 +2049,9 @@ responder_socket_access_tests_SOURCES = \ + src/responder/common/responder_packet.c \ + src/responder/common/responder_cmd.c \ + src/responder/common/data_provider/rdp_message.c \ +- src/responder/common/data_provider/rdp_client.c ++ src/responder/common/data_provider/rdp_client.c \ ++ $(SSSD_RESPONDER_IFACE_OBJ) \ ++ $(NULL) + responder_socket_access_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(CHECK_CFLAGS) +@@ -2125,6 +2138,7 @@ TEST_MOCK_RESP_OBJ = \ + src/responder/common/data_provider/rdp_client.c \ + src/responder/common/responder_utils.c \ + $(SSSD_CACHE_REQ_OBJ) \ ++ $(SSSD_RESPONDER_IFACE_OBJ) \ + $(NULL) + + TEST_MOCK_PROVIDER_OBJ = \ +diff --git a/src/responder/common/iface/responder_domain.c b/src/responder/common/iface/responder_domain.c +new file mode 100644 +index 0000000000000000000000000000000000000000..2e7f788550b3cd0dcec20fc5b91fe8a4cb366875 +--- /dev/null ++++ b/src/responder/common/iface/responder_domain.c +@@ -0,0 +1,73 @@ ++/* ++ Copyright (C) 2016 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 "util/util.h" ++#include "sbus/sssd_dbus.h" ++#include "responder/common/responder.h" ++#include "responder/common/iface/responder_iface.h" ++ ++static void set_domain_state_by_name(struct resp_ctx *rctx, ++ const char *domain_name, ++ enum sss_domain_state state) ++{ ++ struct sss_domain_info *dom; ++ ++ if (domain_name == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "BUG: NULL domain name\n"); ++ return; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Setting state of domain %s\n", domain_name); ++ ++ for (dom = rctx->domains; ++ dom != NULL; ++ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { ++ ++ if (strcasecmp(dom->name, domain_name) == 0) { ++ break; ++ } ++ } ++ ++ if (dom != NULL) { ++ sss_domain_set_state(dom, state); ++ } ++} ++ ++int sss_resp_domain_active(struct sbus_request *req, ++ void *data, ++ const char *domain_name) ++{ ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Enabling domain %s\n", domain_name); ++ set_domain_state_by_name(rctx, domain_name, DOM_ACTIVE); ++ return iface_responder_domain_SetActive_finish(req); ++} ++ ++int sss_resp_domain_inconsistent(struct sbus_request *req, ++ void *data, ++ const char *domain_name) ++{ ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Disabling domain %s\n", domain_name); ++ set_domain_state_by_name(rctx, domain_name, DOM_INCONSISTENT); ++ return iface_responder_domain_SetInconsistent_finish(req); ++} +diff --git a/src/responder/common/iface/responder_iface.c b/src/responder/common/iface/responder_iface.c +new file mode 100644 +index 0000000000000000000000000000000000000000..f1e618b659af3e7a5ffa1b7307f3d61124180f0c +--- /dev/null ++++ b/src/responder/common/iface/responder_iface.c +@@ -0,0 +1,36 @@ ++/* ++ Copyright (C) 2016 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 "sbus/sssd_dbus.h" ++#include "responder/common/iface/responder_iface.h" ++#include "responder/common/responder.h" ++ ++struct iface_responder_domain iface_responder_domain = { ++ { &iface_responder_domain_meta, 0 }, ++ .SetActive = sss_resp_domain_active, ++ .SetInconsistent = sss_resp_domain_inconsistent, ++}; ++ ++static struct sbus_iface_map iface_map[] = { ++ { RESPONDER_PATH, &iface_responder_domain.vtable }, ++ { NULL, NULL } ++}; ++ ++struct sbus_iface_map *responder_get_sbus_interface() ++{ ++ return iface_map; ++} +diff --git a/src/responder/common/iface/responder_iface.h b/src/responder/common/iface/responder_iface.h +new file mode 100644 +index 0000000000000000000000000000000000000000..abd7c83ce0b0efbc13867ffb56ec871503c92567 +--- /dev/null ++++ b/src/responder/common/iface/responder_iface.h +@@ -0,0 +1,37 @@ ++/* ++ Copyright (C) 2016 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 _RESPONDER_IFACE_H_ ++#define _RESPONDER_IFACE_H_ ++ ++#include "responder/common/iface/responder_iface_generated.h" ++ ++#define RESPONDER_PATH "/org/freedesktop/sssd/responder" ++ ++struct sbus_iface_map *responder_get_sbus_interface(void); ++ ++/* org.freedesktop.sssd.Responder.Domain */ ++ ++int sss_resp_domain_active(struct sbus_request *req, ++ void *data, ++ const char *domain_name); ++ ++int sss_resp_domain_inconsistent(struct sbus_request *req, ++ void *data, ++ const char *domain_name); ++ ++#endif /* _RESPONDER_IFACE_H_ */ +diff --git a/src/responder/common/iface/responder_iface.xml b/src/responder/common/iface/responder_iface.xml +new file mode 100644 +index 0000000000000000000000000000000000000000..d3d0ff40ed5a8457492f2f54d551d9ae20cc56c3 +--- /dev/null ++++ b/src/responder/common/iface/responder_iface.xml +@@ -0,0 +1,13 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/responder/common/iface/responder_iface_generated.c b/src/responder/common/iface/responder_iface_generated.c +new file mode 100644 +index 0000000000000000000000000000000000000000..1d59eafed0eb739fb208c864b5b726cf9883df94 +--- /dev/null ++++ b/src/responder/common/iface/responder_iface_generated.c +@@ -0,0 +1,78 @@ ++/* The following definitions are auto-generated from responder_iface.xml */ ++ ++#include "util/util.h" ++#include "sbus/sssd_dbus.h" ++#include "sbus/sssd_dbus_meta.h" ++#include "sbus/sssd_dbus_invokers.h" ++#include "responder_iface_generated.h" ++ ++/* invokes a handler with a 's' DBus signature */ ++static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr); ++ ++/* arguments for org.freedesktop.sssd.Responder.Domain.SetActive */ ++const struct sbus_arg_meta iface_responder_domain_SetActive__in[] = { ++ { "name", "s" }, ++ { NULL, } ++}; ++ ++int iface_responder_domain_SetActive_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ ++/* arguments for org.freedesktop.sssd.Responder.Domain.SetInconsistent */ ++const struct sbus_arg_meta iface_responder_domain_SetInconsistent__in[] = { ++ { "name", "s" }, ++ { NULL, } ++}; ++ ++int iface_responder_domain_SetInconsistent_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ ++/* methods for org.freedesktop.sssd.Responder.Domain */ ++const struct sbus_method_meta iface_responder_domain__methods[] = { ++ { ++ "SetActive", /* name */ ++ iface_responder_domain_SetActive__in, ++ NULL, /* no out_args */ ++ offsetof(struct iface_responder_domain, SetActive), ++ invoke_s_method, ++ }, ++ { ++ "SetInconsistent", /* name */ ++ iface_responder_domain_SetInconsistent__in, ++ NULL, /* no out_args */ ++ offsetof(struct iface_responder_domain, SetInconsistent), ++ invoke_s_method, ++ }, ++ { NULL, } ++}; ++ ++/* interface info for org.freedesktop.sssd.Responder.Domain */ ++const struct sbus_interface_meta iface_responder_domain_meta = { ++ "org.freedesktop.sssd.Responder.Domain", /* name */ ++ iface_responder_domain__methods, ++ NULL, /* no signals */ ++ NULL, /* no properties */ ++ sbus_invoke_get_all, /* GetAll invoker */ ++}; ++ ++/* invokes a handler with a 's' DBus signature */ ++static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr) ++{ ++ const char * arg_0; ++ int (*handler)(struct sbus_request *, void *, const char *) = function_ptr; ++ ++ if (!sbus_request_parse_or_finish(dbus_req, ++ DBUS_TYPE_STRING, &arg_0, ++ DBUS_TYPE_INVALID)) { ++ return EOK; /* request handled */ ++ } ++ ++ return (handler)(dbus_req, dbus_req->intf->handler_data, ++ arg_0); ++} +diff --git a/src/responder/common/iface/responder_iface_generated.h b/src/responder/common/iface/responder_iface_generated.h +new file mode 100644 +index 0000000000000000000000000000000000000000..e7f5c64feb062e13dc04352128cada6883f6f4fa +--- /dev/null ++++ b/src/responder/common/iface/responder_iface_generated.h +@@ -0,0 +1,63 @@ ++/* The following declarations are auto-generated from responder_iface.xml */ ++ ++#ifndef __RESPONDER_IFACE_XML__ ++#define __RESPONDER_IFACE_XML__ ++ ++#include "sbus/sssd_dbus.h" ++ ++/* ------------------------------------------------------------------------ ++ * DBus Constants ++ * ++ * Various constants of interface and method names mostly for use by clients ++ */ ++ ++/* constants for org.freedesktop.sssd.Responder.Domain */ ++#define IFACE_RESPONDER_DOMAIN "org.freedesktop.sssd.Responder.Domain" ++#define IFACE_RESPONDER_DOMAIN_SETACTIVE "SetActive" ++#define IFACE_RESPONDER_DOMAIN_SETINCONSISTENT "SetInconsistent" ++ ++/* ------------------------------------------------------------------------ ++ * DBus handlers ++ * ++ * These structures are filled in by implementors of the different ++ * dbus interfaces to handle method calls. ++ * ++ * Handler functions of type sbus_msg_handler_fn accept raw messages, ++ * other handlers are typed appropriately. If a handler that is ++ * set to NULL is invoked it will result in a ++ * org.freedesktop.DBus.Error.NotSupported error for the caller. ++ * ++ * Handlers have a matching xxx_finish() function (unless the method has ++ * accepts raw messages). These finish functions the ++ * sbus_request_return_and_finish() with the appropriate arguments to ++ * construct a valid reply. Once a finish function has been called, the ++ * @dbus_req it was called with is freed and no longer valid. ++ */ ++ ++/* vtable for org.freedesktop.sssd.Responder.Domain */ ++struct iface_responder_domain { ++ struct sbus_vtable vtable; /* derive from sbus_vtable */ ++ int (*SetActive)(struct sbus_request *req, void *data, const char *arg_name); ++ int (*SetInconsistent)(struct sbus_request *req, void *data, const char *arg_name); ++}; ++ ++/* finish function for SetActive */ ++int iface_responder_domain_SetActive_finish(struct sbus_request *req); ++ ++/* finish function for SetInconsistent */ ++int iface_responder_domain_SetInconsistent_finish(struct sbus_request *req); ++ ++/* ------------------------------------------------------------------------ ++ * DBus Interface Metadata ++ * ++ * These structure definitions are filled in with the information about ++ * the interfaces, methods, properties and so on. ++ * ++ * The actual definitions are found in the accompanying C file next ++ * to this header. ++ */ ++ ++/* interface info for org.freedesktop.sssd.Responder.Domain */ ++extern const struct sbus_interface_meta iface_responder_domain_meta; ++ ++#endif /* __RESPONDER_IFACE_XML__ */ +diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c +index 67922bfccda8f00f256a4d1281aebfe20950d169..1959247ffda76d5041bc031c4c774aef9e0295d8 100644 +--- a/src/responder/common/responder_common.c ++++ b/src/responder/common/responder_common.c +@@ -38,6 +38,7 @@ + #include "confdb/confdb.h" + #include "sbus/sssd_dbus.h" + #include "responder/common/responder.h" ++#include "responder/common/iface/responder_iface.h" + #include "responder/common/responder_packet.h" + #include "providers/data_provider.h" + #include "monitor/monitor_interfaces.h" +@@ -666,6 +667,7 @@ static int sss_dp_init(struct resp_ctx *rctx, + { + struct be_conn *be_conn; + int ret; ++ struct sbus_iface_map *resp_sbus_iface; + + be_conn = talloc_zero(rctx, struct be_conn); + if (!be_conn) return ENOMEM; +@@ -697,6 +699,19 @@ static int sss_dp_init(struct resp_ctx *rctx, + } + } + ++ resp_sbus_iface = responder_get_sbus_interface(); ++ if (resp_sbus_iface != NULL) { ++ ret = sbus_conn_register_iface_map(be_conn->conn, ++ resp_sbus_iface, ++ rctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Cannot register generic responder iface at %s: %d\n", ++ resp_sbus_iface->path, ret); ++ return ret; ++ } ++ } ++ + DLIST_ADD_END(rctx->be_conns, be_conn, struct be_conn *); + + /* Identify ourselves to the DP */ +diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am +index 09a8b5307dd3ebf9c7f27148097a90eac527a213..f50e9aa58fa5f2b0b8aa144582500d925a0a6438 100644 +--- a/src/tests/cwrap/Makefile.am ++++ b/src/tests/cwrap/Makefile.am +@@ -63,6 +63,12 @@ SSSD_CACHE_REQ_OBJ = \ + ../../../src/responder/common/cache_req/plugins/cache_req_host_by_name.c \ + $(NULL) + ++SSSD_RESPONDER_IFACE_OBJ = \ ++ ../../../src/responder/common/iface/responder_iface.c \ ++ ../../../src/responder/common/iface/responder_domain.c \ ++ ../../../src/responder/common/iface/responder_iface_generated.c \ ++ $(NULL) ++ + SSSD_RESPONDER_OBJ = \ + ../../../src/responder/common/negcache_files.c \ + ../../../src/responder/common/negcache.c \ +@@ -77,6 +83,7 @@ SSSD_RESPONDER_OBJ = \ + ../../../src/responder/common/data_provider/rdp_client.c \ + ../../../src/monitor/monitor_iface_generated.c \ + ../../../src/providers/data_provider_req.c \ ++ $(SSSD_RESPONDER_IFACE_OBJ) \ + $(SSSD_CACHE_REQ_OBJ) \ + $(NULL) + +@@ -158,6 +165,9 @@ endif + + responder_common_tests_SOURCES =\ + test_responder_common.c \ ++ ../../../src/responder/common/iface/responder_iface.c \ ++ ../../../src/responder/common/iface/responder_domain.c \ ++ ../../../src/responder/common/iface/responder_iface_generated.c \ + ../../../src/responder/common/negcache_files.c \ + ../../../src/responder/common/negcache.c \ + ../../../src/responder/common/data_provider/rdp_message.c \ +@@ -165,6 +175,8 @@ responder_common_tests_SOURCES =\ + ../../../src/responder/common/responder_common.c \ + ../../../src/responder/common/responder_packet.c \ + ../../../src/responder/common/responder_cmd.c \ ++ ../../../src/tests/cmocka/common_mock_resp_dp.c \ ++ $(SSSD_CACHE_REQ_OBJ) \ + $(NULL) + responder_common_tests_CFLAGS = \ + $(AM_CFLAGS) \ +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index 0feda148bd44b9cefc43c094ddc5a72820412322..6ef6bcfb8c078a360673b6bdd2364fc2918cb324 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -814,8 +814,25 @@ done: + return ret; + } + ++static const char *domain_state_str(struct sss_domain_info *dom) ++{ ++ switch (dom->state) { ++ case DOM_ACTIVE: ++ return "Active"; ++ case DOM_DISABLED: ++ return "Disabled"; ++ case DOM_INACTIVE: ++ return "Inactive"; ++ case DOM_INCONSISTENT: ++ return "Inconsistent"; ++ } ++ return "Unknown"; ++} ++ + enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom) + { ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Domain %s is %s\n", dom->name, domain_state_str(dom)); + return dom->state; + } + +@@ -823,6 +840,8 @@ void sss_domain_set_state(struct sss_domain_info *dom, + enum sss_domain_state state) + { + dom->state = state; ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Domain %s is %s\n", dom->name, domain_state_str(dom)); + } + + bool is_email_from_domain(const char *email, struct sss_domain_info *dom) +-- +2.9.3 + diff --git a/0043-RESPONDER-A-sbus-interface-to-reset-negatively-cache.patch b/0043-RESPONDER-A-sbus-interface-to-reset-negatively-cache.patch new file mode 100644 index 0000000..78c2310 --- /dev/null +++ b/0043-RESPONDER-A-sbus-interface-to-reset-negatively-cache.patch @@ -0,0 +1,259 @@ +From 205a0b9e9234327730fa808be95b2e1db7ffee95 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 2 Nov 2016 17:13:32 +0100 +Subject: [PATCH 43/79] RESPONDER: A sbus interface to reset negatively cached + users and groups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds two new responder sbus interface functions: ResetNegcacheUsers and +ResetNegcacheGroups. These functions can be called by a Data Provider to +signal to a responder that it should drop its negative cache. + +Reviewed-by: Pavel Březina +--- + Makefile.am | 1 + + src/responder/common/iface/responder_iface.c | 7 ++++ + src/responder/common/iface/responder_iface.h | 5 +++ + src/responder/common/iface/responder_iface.xml | 6 ++++ + .../common/iface/responder_iface_generated.c | 40 ++++++++++++++++++++++ + .../common/iface/responder_iface_generated.h | 21 ++++++++++++ + .../{responder_iface.c => responder_ncache.c} | 31 ++++++++++------- + src/tests/cwrap/Makefile.am | 2 ++ + 8 files changed, 100 insertions(+), 13 deletions(-) + copy src/responder/common/iface/{responder_iface.c => responder_ncache.c} (55%) + +diff --git a/Makefile.am b/Makefile.am +index 32f62b5b4391e5d6efb7f7dc19e9b29eaa658550..aa28a27f992f9a42b78d37d6de8fd8271c99afef 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -519,6 +519,7 @@ SSSD_CACHE_REQ_OBJ = \ + SSSD_RESPONDER_IFACE_OBJ = \ + src/responder/common/iface/responder_iface.c \ + src/responder/common/iface/responder_domain.c \ ++ src/responder/common/iface/responder_ncache.c \ + src/responder/common/iface/responder_iface_generated.c \ + $(NULL) + +diff --git a/src/responder/common/iface/responder_iface.c b/src/responder/common/iface/responder_iface.c +index f1e618b659af3e7a5ffa1b7307f3d61124180f0c..07fd1ff6276ae9b847ff50b949ce91550fe68296 100644 +--- a/src/responder/common/iface/responder_iface.c ++++ b/src/responder/common/iface/responder_iface.c +@@ -25,8 +25,15 @@ struct iface_responder_domain iface_responder_domain = { + .SetInconsistent = sss_resp_domain_inconsistent, + }; + ++struct iface_responder_ncache iface_responder_ncache = { ++ { &iface_responder_ncache_meta, 0 }, ++ .ResetUsers = sss_resp_reset_ncache_users, ++ .ResetGroups = sss_resp_reset_ncache_groups, ++}; ++ + static struct sbus_iface_map iface_map[] = { + { RESPONDER_PATH, &iface_responder_domain.vtable }, ++ { RESPONDER_PATH, &iface_responder_ncache.vtable }, + { NULL, NULL } + }; + +diff --git a/src/responder/common/iface/responder_iface.h b/src/responder/common/iface/responder_iface.h +index abd7c83ce0b0efbc13867ffb56ec871503c92567..5166b624cf9f7278c46f10dfc26c717ac4462408 100644 +--- a/src/responder/common/iface/responder_iface.h ++++ b/src/responder/common/iface/responder_iface.h +@@ -34,4 +34,9 @@ int sss_resp_domain_inconsistent(struct sbus_request *req, + void *data, + const char *domain_name); + ++/* org.freedesktop.sssd.Responder.NegativeCache */ ++ ++int sss_resp_reset_ncache_users(struct sbus_request *req, void *data); ++int sss_resp_reset_ncache_groups(struct sbus_request *req, void *data); ++ + #endif /* _RESPONDER_IFACE_H_ */ +diff --git a/src/responder/common/iface/responder_iface.xml b/src/responder/common/iface/responder_iface.xml +index d3d0ff40ed5a8457492f2f54d551d9ae20cc56c3..9f092e00ffc5354efe98b6c8bde1cdf414ee36d2 100644 +--- a/src/responder/common/iface/responder_iface.xml ++++ b/src/responder/common/iface/responder_iface.xml +@@ -10,4 +10,10 @@ + + + ++ ++ ++ ++ ++ ++ + +diff --git a/src/responder/common/iface/responder_iface_generated.c b/src/responder/common/iface/responder_iface_generated.c +index 1d59eafed0eb739fb208c864b5b726cf9883df94..837e67cfd4305494be6ee3de949d56d47179707c 100644 +--- a/src/responder/common/iface/responder_iface_generated.c ++++ b/src/responder/common/iface/responder_iface_generated.c +@@ -61,6 +61,46 @@ const struct sbus_interface_meta iface_responder_domain_meta = { + sbus_invoke_get_all, /* GetAll invoker */ + }; + ++int iface_responder_ncache_ResetUsers_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ ++int iface_responder_ncache_ResetGroups_finish(struct sbus_request *req) ++{ ++ return sbus_request_return_and_finish(req, ++ DBUS_TYPE_INVALID); ++} ++ ++/* methods for org.freedesktop.sssd.Responder.NegativeCache */ ++const struct sbus_method_meta iface_responder_ncache__methods[] = { ++ { ++ "ResetUsers", /* name */ ++ NULL, /* no in_args */ ++ NULL, /* no out_args */ ++ offsetof(struct iface_responder_ncache, ResetUsers), ++ NULL, /* no invoker */ ++ }, ++ { ++ "ResetGroups", /* name */ ++ NULL, /* no in_args */ ++ NULL, /* no out_args */ ++ offsetof(struct iface_responder_ncache, ResetGroups), ++ NULL, /* no invoker */ ++ }, ++ { NULL, } ++}; ++ ++/* interface info for org.freedesktop.sssd.Responder.NegativeCache */ ++const struct sbus_interface_meta iface_responder_ncache_meta = { ++ "org.freedesktop.sssd.Responder.NegativeCache", /* name */ ++ iface_responder_ncache__methods, ++ NULL, /* no signals */ ++ NULL, /* no properties */ ++ sbus_invoke_get_all, /* GetAll invoker */ ++}; ++ + /* invokes a handler with a 's' DBus signature */ + static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr) + { +diff --git a/src/responder/common/iface/responder_iface_generated.h b/src/responder/common/iface/responder_iface_generated.h +index e7f5c64feb062e13dc04352128cada6883f6f4fa..964f19b732595c261e84f857497678490a113412 100644 +--- a/src/responder/common/iface/responder_iface_generated.h ++++ b/src/responder/common/iface/responder_iface_generated.h +@@ -16,6 +16,11 @@ + #define IFACE_RESPONDER_DOMAIN_SETACTIVE "SetActive" + #define IFACE_RESPONDER_DOMAIN_SETINCONSISTENT "SetInconsistent" + ++/* constants for org.freedesktop.sssd.Responder.NegativeCache */ ++#define IFACE_RESPONDER_NCACHE "org.freedesktop.sssd.Responder.NegativeCache" ++#define IFACE_RESPONDER_NCACHE_RESETUSERS "ResetUsers" ++#define IFACE_RESPONDER_NCACHE_RESETGROUPS "ResetGroups" ++ + /* ------------------------------------------------------------------------ + * DBus handlers + * +@@ -47,6 +52,19 @@ int iface_responder_domain_SetActive_finish(struct sbus_request *req); + /* finish function for SetInconsistent */ + int iface_responder_domain_SetInconsistent_finish(struct sbus_request *req); + ++/* vtable for org.freedesktop.sssd.Responder.NegativeCache */ ++struct iface_responder_ncache { ++ struct sbus_vtable vtable; /* derive from sbus_vtable */ ++ int (*ResetUsers)(struct sbus_request *req, void *data); ++ int (*ResetGroups)(struct sbus_request *req, void *data); ++}; ++ ++/* finish function for ResetUsers */ ++int iface_responder_ncache_ResetUsers_finish(struct sbus_request *req); ++ ++/* finish function for ResetGroups */ ++int iface_responder_ncache_ResetGroups_finish(struct sbus_request *req); ++ + /* ------------------------------------------------------------------------ + * DBus Interface Metadata + * +@@ -60,4 +78,7 @@ int iface_responder_domain_SetInconsistent_finish(struct sbus_request *req); + /* interface info for org.freedesktop.sssd.Responder.Domain */ + extern const struct sbus_interface_meta iface_responder_domain_meta; + ++/* interface info for org.freedesktop.sssd.Responder.NegativeCache */ ++extern const struct sbus_interface_meta iface_responder_ncache_meta; ++ + #endif /* __RESPONDER_IFACE_XML__ */ +diff --git a/src/responder/common/iface/responder_iface.c b/src/responder/common/iface/responder_ncache.c +similarity index 55% +copy from src/responder/common/iface/responder_iface.c +copy to src/responder/common/iface/responder_ncache.c +index f1e618b659af3e7a5ffa1b7307f3d61124180f0c..c7aa0a3a40f386aa2d2f0d0a00a4fa90a59ffb34 100644 +--- a/src/responder/common/iface/responder_iface.c ++++ b/src/responder/common/iface/responder_ncache.c +@@ -1,5 +1,8 @@ + /* +- Copyright (C) 2016 Red Hat ++ Authors: ++ Pavel Březina ++ ++ 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 +@@ -15,22 +18,24 @@ + along with this program. If not, see . + */ + ++#include "util/util.h" + #include "sbus/sssd_dbus.h" +-#include "responder/common/iface/responder_iface.h" + #include "responder/common/responder.h" ++#include "responder/common/negcache.h" ++#include "responder/common/iface/responder_iface.h" + +-struct iface_responder_domain iface_responder_domain = { +- { &iface_responder_domain_meta, 0 }, +- .SetActive = sss_resp_domain_active, +- .SetInconsistent = sss_resp_domain_inconsistent, +-}; ++int sss_resp_reset_ncache_users(struct sbus_request *req, void *data) ++{ ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); + +-static struct sbus_iface_map iface_map[] = { +- { RESPONDER_PATH, &iface_responder_domain.vtable }, +- { NULL, NULL } +-}; ++ sss_ncache_reset_users(rctx->ncache); ++ return iface_responder_ncache_ResetUsers_finish(req); ++} + +-struct sbus_iface_map *responder_get_sbus_interface() ++int sss_resp_reset_ncache_groups(struct sbus_request *req, void *data) + { +- return iface_map; ++ struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx); ++ ++ sss_ncache_reset_groups(rctx->ncache); ++ return iface_responder_ncache_ResetGroups_finish(req); + } +diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am +index f50e9aa58fa5f2b0b8aa144582500d925a0a6438..b4bef8fee1830c1d6798dde50f114ecb4608c645 100644 +--- a/src/tests/cwrap/Makefile.am ++++ b/src/tests/cwrap/Makefile.am +@@ -66,6 +66,7 @@ SSSD_CACHE_REQ_OBJ = \ + SSSD_RESPONDER_IFACE_OBJ = \ + ../../../src/responder/common/iface/responder_iface.c \ + ../../../src/responder/common/iface/responder_domain.c \ ++ ../../../src/responder/common/iface/responder_ncache.c \ + ../../../src/responder/common/iface/responder_iface_generated.c \ + $(NULL) + +@@ -167,6 +168,7 @@ responder_common_tests_SOURCES =\ + test_responder_common.c \ + ../../../src/responder/common/iface/responder_iface.c \ + ../../../src/responder/common/iface/responder_domain.c \ ++ ../../../src/responder/common/iface/responder_ncache.c \ + ../../../src/responder/common/iface/responder_iface_generated.c \ + ../../../src/responder/common/negcache_files.c \ + ../../../src/responder/common/negcache.c \ +-- +2.9.3 + diff --git a/0044-DP-Add-internal-DP-interface-to-set-domain-state.patch b/0044-DP-Add-internal-DP-interface-to-set-domain-state.patch new file mode 100644 index 0000000..7af7a93 --- /dev/null +++ b/0044-DP-Add-internal-DP-interface-to-set-domain-state.patch @@ -0,0 +1,148 @@ +From b3ee4be9e1794fa823696d70d4958f3b0269939c Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 2 Nov 2016 17:18:07 +0100 +Subject: [PATCH 44/79] DP: Add internal DP interface to set domain state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds functions to the interface Data Provider publishes towards back +ends that allows the back ends to notify responders that a domain has +been enabled or disabled. + +Reviewed-by: Pavel Březina +--- + Makefile.am | 1 + + src/providers/data_provider/dp.h | 5 ++ + src/providers/data_provider/dp_resp_client.c | 93 ++++++++++++++++++++++++++++ + 3 files changed, 99 insertions(+) + create mode 100644 src/providers/data_provider/dp_resp_client.c + +diff --git a/Makefile.am b/Makefile.am +index aa28a27f992f9a42b78d37d6de8fd8271c99afef..5cf496002ff54b8df1c0fdf29179a5b69e4b62c0 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1464,6 +1464,7 @@ sssd_be_SOURCES = \ + src/providers/data_provider/dp_iface_backend.c \ + src/providers/data_provider/dp_iface_failover.c \ + src/providers/data_provider/dp_client.c \ ++ src/providers/data_provider/dp_resp_client.c \ + src/providers/data_provider/dp_iface_generated.c \ + src/providers/data_provider/dp_request.c \ + src/providers/data_provider/dp_request_reply.c \ +diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h +index 5b36baf3489be4cce463dfb42c65a0b7f7ece9ef..68db75521bd9d78eb6e7944746ea2054918e298d 100644 +--- a/src/providers/data_provider/dp.h ++++ b/src/providers/data_provider/dp.h +@@ -161,4 +161,9 @@ bool dp_method_enabled(struct data_provider *provider, + void dp_terminate_domain_requests(struct data_provider *provider, + const char *domain); + ++void dp_sbus_domain_active(struct data_provider *provider, ++ struct sss_domain_info *dom); ++void dp_sbus_domain_inconsistent(struct data_provider *provider, ++ struct sss_domain_info *dom); ++ + #endif /* _DP_H_ */ +diff --git a/src/providers/data_provider/dp_resp_client.c b/src/providers/data_provider/dp_resp_client.c +new file mode 100644 +index 0000000000000000000000000000000000000000..3d386eac1cd779e2776e23745a18292c5ce835cd +--- /dev/null ++++ b/src/providers/data_provider/dp_resp_client.c +@@ -0,0 +1,93 @@ ++/* ++ SSSD ++ ++ Data Provider Responder client - DP calls responder interface ++ ++ 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 "confdb/confdb.h" ++#include "sbus/sssd_dbus.h" ++#include "providers/data_provider.h" ++#include "providers/data_provider/dp_private.h" ++#include "responder/common/iface/responder_iface.h" ++#include "src/responder/nss/nss_iface.h" ++ ++static void send_msg_to_all_clients(struct data_provider *provider, ++ struct DBusMessage *msg) ++{ ++ struct dp_client *cli; ++ int i; ++ ++ for (i = 0; provider->clients[i] != NULL; i++) { ++ cli = provider->clients[i]; ++ if (cli != NULL) { ++ sbus_conn_send_reply(dp_client_conn(cli), msg); ++ } ++ } ++} ++ ++static void dp_sbus_set_domain_state(struct data_provider *provider, ++ struct sss_domain_info *dom, ++ enum sss_domain_state state) ++{ ++ DBusMessage *msg; ++ const char *method = NULL; ++ ++ switch (state) { ++ case DOM_ACTIVE: ++ DEBUG(SSSDBG_TRACE_FUNC, "Ordering responders to enable domain %s\n", ++ dom->name); ++ method = IFACE_RESPONDER_DOMAIN_SETACTIVE; ++ break; ++ case DOM_INCONSISTENT: ++ DEBUG(SSSDBG_TRACE_FUNC, "Ordering responders to disable domain %s\n", ++ dom->name); ++ method = IFACE_RESPONDER_DOMAIN_SETINCONSISTENT; ++ break; ++ default: ++ /* No other methods provided at the moment */ ++ return; ++ } ++ ++ sss_domain_set_state(dom, state); ++ ++ msg = sbus_create_message(NULL, NULL, RESPONDER_PATH, ++ IFACE_RESPONDER_DOMAIN, method, ++ DBUS_TYPE_STRING, &dom->name); ++ if (msg == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); ++ return; ++ } ++ ++ send_msg_to_all_clients(provider, msg); ++ dbus_message_unref(msg); ++ return; ++} ++ ++void dp_sbus_domain_active(struct data_provider *provider, ++ struct sss_domain_info *dom) ++{ ++ return dp_sbus_set_domain_state(provider, dom, DOM_ACTIVE); ++} ++ ++void dp_sbus_domain_inconsistent(struct data_provider *provider, ++ struct sss_domain_info *dom) ++{ ++ return dp_sbus_set_domain_state(provider, dom, DOM_INCONSISTENT); ++} +-- +2.9.3 + diff --git a/0045-DP-Add-internal-interface-to-reset-negative-cache-fr.patch b/0045-DP-Add-internal-interface-to-reset-negative-cache-fr.patch new file mode 100644 index 0000000..de0745a --- /dev/null +++ b/0045-DP-Add-internal-interface-to-reset-negative-cache-fr.patch @@ -0,0 +1,122 @@ +From af28fa659f7ffcd12ecf8bda64e79cf5dd225651 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 16 Nov 2016 17:00:57 +0100 +Subject: [PATCH 45/79] DP: Add internal interface to reset negative cache from + DP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a an interface that allows the Data Provider to notify responders +to drop their negative cache. + +Reviewed-by: Pavel Březina +--- + src/providers/data_provider/dp.h | 5 +++ + src/providers/data_provider/dp_resp_client.c | 65 +++++++++++++++++++++++++++- + 2 files changed, 69 insertions(+), 1 deletion(-) + +diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h +index 68db75521bd9d78eb6e7944746ea2054918e298d..79d02d469c5eb04d5e27b27af48b77f72d132416 100644 +--- a/src/providers/data_provider/dp.h ++++ b/src/providers/data_provider/dp.h +@@ -166,4 +166,9 @@ void dp_sbus_domain_active(struct data_provider *provider, + void dp_sbus_domain_inconsistent(struct data_provider *provider, + struct sss_domain_info *dom); + ++void dp_sbus_reset_users_ncache(struct data_provider *provider, ++ struct sss_domain_info *dom); ++void dp_sbus_reset_groups_ncache(struct data_provider *provider, ++ struct sss_domain_info *dom); ++ + #endif /* _DP_H_ */ +diff --git a/src/providers/data_provider/dp_resp_client.c b/src/providers/data_provider/dp_resp_client.c +index 3d386eac1cd779e2776e23745a18292c5ce835cd..6828610acce3771f2b628c877a1d463c3f635015 100644 +--- a/src/providers/data_provider/dp_resp_client.c ++++ b/src/providers/data_provider/dp_resp_client.c +@@ -26,7 +26,23 @@ + #include "providers/data_provider.h" + #include "providers/data_provider/dp_private.h" + #include "responder/common/iface/responder_iface.h" +-#include "src/responder/nss/nss_iface.h" ++#include "responder/nss/nss_iface.h" ++ ++/* List of DP clients that deal with users or groups */ ++/* FIXME - it would be much cleaner to implement sbus signals ++ * and let the responder subscribe to these messages rather than ++ * keep a list here.. ++ * https://fedorahosted.org/sssd/ticket/2233 ++ */ ++static enum dp_clients user_clients[] = { ++ DPC_NSS, ++ DPC_PAM, ++ DPC_IFP, ++ DPC_PAC, ++ DPC_SUDO, ++ ++ DP_CLIENT_SENTINEL ++}; + + static void send_msg_to_all_clients(struct data_provider *provider, + struct DBusMessage *msg) +@@ -42,6 +58,21 @@ static void send_msg_to_all_clients(struct data_provider *provider, + } + } + ++static void send_msg_to_selected_clients(struct data_provider *provider, ++ struct DBusMessage *msg, ++ enum dp_clients *clients) ++{ ++ struct dp_client *cli; ++ int i; ++ ++ for (i = 0; clients[i] != DP_CLIENT_SENTINEL; i++) { ++ cli = provider->clients[clients[i]]; ++ if (cli != NULL) { ++ sbus_conn_send_reply(dp_client_conn(cli), msg); ++ } ++ } ++} ++ + static void dp_sbus_set_domain_state(struct data_provider *provider, + struct sss_domain_info *dom, + enum sss_domain_state state) +@@ -91,3 +122,35 @@ void dp_sbus_domain_inconsistent(struct data_provider *provider, + { + return dp_sbus_set_domain_state(provider, dom, DOM_INCONSISTENT); + } ++ ++static void dp_sbus_reset_ncache(struct data_provider *provider, ++ struct sss_domain_info *dom, ++ const char *method) ++{ ++ DBusMessage *msg; ++ ++ msg = sbus_create_message(NULL, NULL, RESPONDER_PATH, ++ IFACE_RESPONDER_NCACHE, method); ++ if (msg == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); ++ return; ++ } ++ ++ send_msg_to_selected_clients(provider, msg, user_clients); ++ dbus_message_unref(msg); ++ return; ++} ++ ++void dp_sbus_reset_users_ncache(struct data_provider *provider, ++ struct sss_domain_info *dom) ++{ ++ return dp_sbus_reset_ncache(provider, dom, ++ IFACE_RESPONDER_NCACHE_RESETUSERS); ++} ++ ++void dp_sbus_reset_groups_ncache(struct data_provider *provider, ++ struct sss_domain_info *dom) ++{ ++ return dp_sbus_reset_ncache(provider, dom, ++ IFACE_RESPONDER_NCACHE_RESETGROUPS); ++} +-- +2.9.3 + diff --git a/0046-DP-Add-internal-interface-to-invalidate-memory-cache.patch b/0046-DP-Add-internal-interface-to-invalidate-memory-cache.patch new file mode 100644 index 0000000..2eddb82 --- /dev/null +++ b/0046-DP-Add-internal-interface-to-invalidate-memory-cache.patch @@ -0,0 +1,77 @@ +From 5007103e82f34e64a0ff3b278797b9fa42ba1dda Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 14 Feb 2017 20:37:58 +0100 +Subject: [PATCH 46/79] DP: Add internal interface to invalidate memory cache + from DP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds an interfae to the Data Provider that allows the DP to notify the +NSS responder to invalidate its memory cache records. + +Reviewed-by: Pavel Březina +--- + src/providers/data_provider/dp.h | 4 ++++ + src/providers/data_provider/dp_resp_client.c | 35 ++++++++++++++++++++++++++++ + 2 files changed, 39 insertions(+) + +diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h +index 79d02d469c5eb04d5e27b27af48b77f72d132416..e80a6c3398784dfc176baeff2daf7203c52fc072 100644 +--- a/src/providers/data_provider/dp.h ++++ b/src/providers/data_provider/dp.h +@@ -171,4 +171,8 @@ void dp_sbus_reset_users_ncache(struct data_provider *provider, + void dp_sbus_reset_groups_ncache(struct data_provider *provider, + struct sss_domain_info *dom); + ++void dp_sbus_reset_users_memcache(struct data_provider *provider); ++void dp_sbus_reset_groups_memcache(struct data_provider *provider); ++void dp_sbus_reset_initgr_memcache(struct data_provider *provider); ++ + #endif /* _DP_H_ */ +diff --git a/src/providers/data_provider/dp_resp_client.c b/src/providers/data_provider/dp_resp_client.c +index 6828610acce3771f2b628c877a1d463c3f635015..5735188a603b16c35ad6e1050c06a685fdf7ed8d 100644 +--- a/src/providers/data_provider/dp_resp_client.c ++++ b/src/providers/data_provider/dp_resp_client.c +@@ -154,3 +154,38 @@ void dp_sbus_reset_groups_ncache(struct data_provider *provider, + return dp_sbus_reset_ncache(provider, dom, + IFACE_RESPONDER_NCACHE_RESETGROUPS); + } ++ ++static void dp_sbus_reset_memcache(struct data_provider *provider, ++ const char *method) ++{ ++ DBusMessage *msg; ++ ++ msg = sbus_create_message(NULL, NULL, NSS_MEMORYCACHE_PATH, ++ IFACE_NSS_MEMORYCACHE, method); ++ if (msg == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n"); ++ return; ++ } ++ ++ send_msg_to_selected_clients(provider, msg, user_clients); ++ dbus_message_unref(msg); ++ return; ++} ++ ++void dp_sbus_reset_users_memcache(struct data_provider *provider) ++{ ++ return dp_sbus_reset_memcache(provider, ++ IFACE_NSS_MEMORYCACHE_INVALIDATEALLUSERS); ++} ++ ++void dp_sbus_reset_groups_memcache(struct data_provider *provider) ++{ ++ return dp_sbus_reset_memcache(provider, ++ IFACE_NSS_MEMORYCACHE_INVALIDATEALLGROUPS); ++} ++ ++void dp_sbus_reset_initgr_memcache(struct data_provider *provider) ++{ ++ return dp_sbus_reset_memcache(provider, ++ IFACE_NSS_MEMORYCACHE_INVALIDATEALLINITGROUPS); ++} +-- +2.9.3 + diff --git a/0047-RESPONDER-Use-the-NEED_CHECK_DOMAIN-macro.patch b/0047-RESPONDER-Use-the-NEED_CHECK_DOMAIN-macro.patch new file mode 100644 index 0000000..fa7f137 --- /dev/null +++ b/0047-RESPONDER-Use-the-NEED_CHECK_DOMAIN-macro.patch @@ -0,0 +1,50 @@ +From 2c61b6eee24d90b11f3d2cab7b9cd8690df29f34 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 2 Jan 2017 16:41:31 +0100 +Subject: [PATCH 47/79] RESPONDER: Use the NEED_CHECK_DOMAIN macro +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is to avoid a needless round-trip between the responder and the +back end for domains that do not have a traditional back end such as +local or files. + +Reviewed-by: Pavel Březina +--- + src/responder/common/responder_dp.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c +index 11eb47ce1d41027f36998aba7b9fbca5fb4c7910..cfd12569a5068d0ffaa7fee5a35e12fe4512fb50 100644 +--- a/src/responder/common/responder_dp.c ++++ b/src/responder/common/responder_dp.c +@@ -495,6 +495,12 @@ sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + goto error; + } + ++ if (NEED_CHECK_PROVIDER(dom->provider) == false) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n", dom->name); ++ ret = EOK; ++ goto error; ++ } ++ + info = talloc_zero(state, struct sss_dp_account_info); + info->fast_reply = fast_reply; + info->type = type; +@@ -539,7 +545,11 @@ sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + return req; + + error: +- tevent_req_error(req, ret); ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } + tevent_req_post(req, rctx->ev); + return req; + } +-- +2.9.3 + diff --git a/0048-RESPONDER-Include-the-files-provider-in-NEEDS_CHECK_.patch b/0048-RESPONDER-Include-the-files-provider-in-NEEDS_CHECK_.patch new file mode 100644 index 0000000..174a82d --- /dev/null +++ b/0048-RESPONDER-Include-the-files-provider-in-NEEDS_CHECK_.patch @@ -0,0 +1,41 @@ +From 26866484a985adbc7edf2e79a1e95b3bb6b8624c Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 2 Dec 2016 17:51:54 +0100 +Subject: [PATCH 48/79] RESPONDER: Include the files provider in + NEEDS_CHECK_PROVIDER +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It makes no sense to contact the Data Provider with the files provider +except when the files provider is updating itself. + +Reviewed-by: Pavel Březina +--- + src/responder/common/responder.h | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h +index 748dec4301b4a018691d9b8c8fca0193d18167a5..3515f76d2bd0a553e7bf6b089b6d511255cf1e93 100644 +--- a/src/responder/common/responder.h ++++ b/src/responder/common/responder.h +@@ -48,9 +48,14 @@ extern hash_table_t *dp_requests; + * So we set umask to 0111. */ + #define SCKT_RSP_UMASK 0111 + +-/* if there is a provider other than the special local */ ++/* Neither the local provider nor the files provider have a back ++ * end in the traditional sense and can always just consult ++ * the responder's cache ++ */ + #define NEED_CHECK_PROVIDER(provider) \ +- (provider != NULL && strcmp(provider, "local") != 0) ++ (provider != NULL && \ ++ (strcmp(provider, "local") != 0 && \ ++ strcmp(provider, "files") != 0)) + + /* needed until nsssrv.h is updated */ + struct cli_request { +-- +2.9.3 + diff --git a/0049-RESPONDER-Contact-inconsistent-domains.patch b/0049-RESPONDER-Contact-inconsistent-domains.patch new file mode 100644 index 0000000..fb9eb54 --- /dev/null +++ b/0049-RESPONDER-Contact-inconsistent-domains.patch @@ -0,0 +1,132 @@ +From 50c740cbc2bb27cbe488fa8587e2901b8b85cf87 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 10 Feb 2017 14:39:43 +0100 +Subject: [PATCH 49/79] RESPONDER: Contact inconsistent domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Pavel Březina +--- + src/providers/data_provider.h | 5 +++ + src/responder/common/responder_dp.c | 74 +++++++++++++++++++++++++++++++++++-- + 2 files changed, 76 insertions(+), 3 deletions(-) + +diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h +index 46d9910ddf9ef1c37da585bc33cf314341860332..5ccc0adbaffc6d50f128ae02fe6e5c77743f626b 100644 +--- a/src/providers/data_provider.h ++++ b/src/providers/data_provider.h +@@ -229,6 +229,11 @@ int dp_get_sbus_address(TALLOC_CTX *mem_ctx, + char **address, const char *domain_name); + + ++/* Reserved filter name for request which waits until the files provider finishes mirroring ++ * the file content ++ */ ++#define DP_REQ_OPT_FILES_INITGR "files_initgr_request" ++ + /* Helpers */ + + #define NULL_STRING { .string = NULL } +diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c +index cfd12569a5068d0ffaa7fee5a35e12fe4512fb50..080f70fd5945ffd234e0ef226d8139df071c4752 100644 +--- a/src/responder/common/responder_dp.c ++++ b/src/responder/common/responder_dp.c +@@ -453,6 +453,12 @@ sss_dp_req_recv(TALLOC_CTX *mem_ctx, + */ + static DBusMessage *sss_dp_get_account_msg(void *pvt); + ++static int sss_dp_account_files_params(struct sss_domain_info *dom, ++ enum sss_dp_acct_type type_in, ++ const char *opt_name_in, ++ enum sss_dp_acct_type *_type_out, ++ const char **_opt_name_out); ++ + struct sss_dp_account_info { + struct sss_domain_info *dom; + +@@ -496,9 +502,28 @@ sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + } + + if (NEED_CHECK_PROVIDER(dom->provider) == false) { +- DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n", dom->name); +- ret = EOK; +- goto error; ++ if (strcmp(dom->provider, "files") == 0) { ++ /* This is a special case. If the files provider is just being updated, ++ * we issue an enumeration request. We always use the same request type ++ * (user enumeration) to make sure concurrent requests are just chained ++ * in the Data Provider ++ */ ++ ret = sss_dp_account_files_params(dom, type, opt_name, ++ &type, &opt_name); ++ if (ret == EOK) { ++ goto error; ++ } else if (ret != EAGAIN) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to set files provider update: %d: %s\n", ++ ret, sss_strerror(ret)); ++ goto error; ++ } ++ /* EAGAIN, fall through to issuing the request */ ++ } else { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n", dom->name); ++ ret = EOK; ++ goto error; ++ } + } + + info = talloc_zero(state, struct sss_dp_account_info); +@@ -554,6 +579,49 @@ error: + return req; + } + ++static int sss_dp_account_files_params(struct sss_domain_info *dom, ++ enum sss_dp_acct_type type_in, ++ const char *opt_name_in, ++ enum sss_dp_acct_type *_type_out, ++ const char **_opt_name_out) ++{ ++#if 0 ++ if (sss_domain_get_state(dom) != DOM_INCONSISTENT) { ++ return EOK; ++ } ++#endif ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Domain files is not consistent, issuing update\n"); ++ ++ switch(type_in) { ++ case SSS_DP_USER: ++ case SSS_DP_GROUP: ++ *_type_out = type_in; ++ *_opt_name_out = NULL; ++ return EAGAIN; ++ case SSS_DP_INITGROUPS: ++ /* There is no initgroups enumeration so let's use a dummy ++ * name to let the DP chain the requests ++ */ ++ *_type_out = type_in; ++ *_opt_name_out = DP_REQ_OPT_FILES_INITGR; ++ return EAGAIN; ++ /* These are not handled by the files provider, just fall back */ ++ case SSS_DP_NETGR: ++ case SSS_DP_SERVICES: ++ case SSS_DP_SECID: ++ case SSS_DP_USER_AND_GROUP: ++ case SSS_DP_CERT: ++ case SSS_DP_WILDCARD_USER: ++ case SSS_DP_WILDCARD_GROUP: ++ return EOK; ++ } ++ ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unhandled type %d\n", type_in); ++ return EINVAL; ++} ++ + static DBusMessage * + sss_dp_get_account_msg(void *pvt) + { +-- +2.9.3 + diff --git a/0050-UTIL-Add-a-generic-inotify-module.patch b/0050-UTIL-Add-a-generic-inotify-module.patch new file mode 100644 index 0000000..56a1b2b --- /dev/null +++ b/0050-UTIL-Add-a-generic-inotify-module.patch @@ -0,0 +1,1312 @@ +From 8cfb42e1985550e99585d311f68087d414932806 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 27 May 2016 18:19:59 +0200 +Subject: [PATCH 50/79] UTIL: Add a generic inotify module +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a reusable module for watching files using the Linux-specific +inotify(7) interface. Adds the possibility to watch the file's parent +directory as well to make it possible to watch moves into the directory +and allow watching file that doesn't exist at the time the watch is +created. + +This interface is needed to implement the files provider, so this commit +is related to: + https://fedorahosted.org/sssd/ticket/2228 + +Reviewed-by: Pavel Březina +--- + Makefile.am | 20 ++ + src/external/inotify.m4 | 2 + + src/tests/cmocka/test_inotify.c | 582 ++++++++++++++++++++++++++++++++++++++++ + src/util/inotify.c | 562 ++++++++++++++++++++++++++++++++++++++ + src/util/inotify.h | 61 +++++ + 5 files changed, 1227 insertions(+) + create mode 100644 src/tests/cmocka/test_inotify.c + create mode 100644 src/util/inotify.c + create mode 100644 src/util/inotify.h + +diff --git a/Makefile.am b/Makefile.am +index 5cf496002ff54b8df1c0fdf29179a5b69e4b62c0..09c021d22c85ae254639b3b6e43e453cbc40ecfa 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -284,6 +284,10 @@ if BUILD_IFP + non_interactive_cmocka_based_tests += ifp_tests + endif # BUILD_IFP + ++if HAVE_INOTIFY ++non_interactive_cmocka_based_tests += test_inotify ++endif # HAVE_INOTIFY ++ + if BUILD_SAMBA + non_interactive_cmocka_based_tests += \ + ad_access_filter_tests \ +@@ -642,6 +646,7 @@ dist_noinst_HEADERS = \ + src/util/util_safealign.h \ + src/util/util_sss_idmap.h \ + src/util/util_creds.h \ ++ src/util/inotify.h \ + src/monitor/monitor.h \ + src/monitor/monitor_interfaces.h \ + src/monitor/monitor_iface_generated.h \ +@@ -3167,6 +3172,21 @@ krb5_common_test_LDADD = \ + libdlopen_test_providers.la \ + $(NULL) + ++test_inotify_SOURCES = \ ++ src/util/inotify.c \ ++ src/tests/cmocka/test_inotify.c \ ++ $(NULL) ++test_inotify_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++test_inotify_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(SSSD_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ $(LIBADD_DL) \ ++ libsss_test_common.la \ ++ $(NULL) ++ + endif # HAVE_CMOCKA + + noinst_PROGRAMS = pam_test_client +diff --git a/src/external/inotify.m4 b/src/external/inotify.m4 +index 25259a817ae1faad6e6976aa5fd3623f02ceb743..3ae5ae3141d40df480f87b2205673e91eefac7af 100644 +--- a/src/external/inotify.m4 ++++ b/src/external/inotify.m4 +@@ -29,4 +29,6 @@ int main () { + AS_IF([test x"$inotify_works" = xyes], + [AC_DEFINE_UNQUOTED([HAVE_INOTIFY], [1], [Inotify works])]) + AC_SUBST(INOTIFY_LIBS) ++ ++ AM_CONDITIONAL([HAVE_INOTIFY], [test x"$inotify_works" = xyes]) + ]) +diff --git a/src/tests/cmocka/test_inotify.c b/src/tests/cmocka/test_inotify.c +new file mode 100644 +index 0000000000000000000000000000000000000000..1f8561df1d1254a1535151f09986a49338d8e846 +--- /dev/null ++++ b/src/tests/cmocka/test_inotify.c +@@ -0,0 +1,582 @@ ++/* ++ Copyright (C) 2016 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 ++#include ++#include ++#include ++ ++#include "limits.h" ++#include "util/io.h" ++#include "util/inotify.h" ++#include "util/util.h" ++#include "tests/common.h" ++ ++struct inotify_test_ctx { ++ char *filename; ++ char *dirname; ++ ++ int ncb; ++ int threshold; ++ /* if the cb receives flags not in this set, test fails */ ++ uint32_t exp_flags; ++ ++ struct sss_test_ctx *tctx; ++ struct tevent_timer *fail_te; ++}; ++ ++static void test_timeout(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ DEBUG(SSSDBG_FATAL_FAILURE, "The test timed out!\n"); ++ talloc_free(te); ++ fail(); ++} ++ ++static struct inotify_test_ctx *common_setup(TALLOC_CTX *mem_ctx) ++{ ++ struct inotify_test_ctx *ctx; ++ struct timeval tv; ++ ++ ctx = talloc_zero(mem_ctx, struct inotify_test_ctx); ++ if (ctx == NULL) { ++ return NULL; ++ } ++ ++ ctx->tctx = create_ev_test_ctx(ctx); ++ if (ctx->tctx == NULL) { ++ talloc_free(ctx); ++ return NULL; ++ } ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_sec += 5; ++ ctx->fail_te = tevent_add_timer(ctx->tctx->ev, ctx, ++ tv, test_timeout, ctx); ++ if (ctx->fail_te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue fallback timer!\n"); ++ talloc_free(ctx); ++ return NULL; ++ } ++ ++ return ctx; ++} ++ ++static int inotify_test_setup(void **state) ++{ ++ struct inotify_test_ctx *ctx; ++ int fd; ++ ++ ctx = common_setup(NULL); ++ if (ctx == NULL) { ++ return 1; ++ } ++ ++ ctx->filename = talloc_strdup(ctx, "test_inotify.XXXXXX"); ++ if (ctx->filename == NULL) { ++ talloc_free(ctx); ++ return 1; ++ } ++ ++ fd = mkstemp(ctx->filename); ++ if (fd == -1) { ++ talloc_free(ctx); ++ return 1; ++ } ++ close(fd); ++ ++ *state = ctx; ++ return 0; ++} ++ ++static int inotify_test_dir_setup(void **state) ++{ ++ struct inotify_test_ctx *ctx; ++ ++ ctx = common_setup(NULL); ++ if (ctx == NULL) { ++ return 1; ++ } ++ ++ ctx->dirname = talloc_strdup(ctx, "test_inotify_dir.XXXXXX"); ++ if (ctx->dirname == NULL) { ++ talloc_free(ctx); ++ return 1; ++ } ++ ++ ctx->dirname = mkdtemp(ctx->dirname); ++ if (ctx->dirname == NULL) { ++ talloc_free(ctx); ++ return 1; ++ } ++ ++ ctx->filename = talloc_asprintf(ctx, "%s/testfile", ctx->dirname); ++ if (ctx->filename == NULL) { ++ talloc_free(ctx); ++ return 1; ++ } ++ ++ *state = ctx; ++ return 0; ++} ++ ++static int inotify_test_teardown(void **state) ++{ ++ struct inotify_test_ctx *ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ int ret; ++ ++ ret = unlink(ctx->filename); ++ if (ret == -1 && errno != ENOENT) { ++ return 1; ++ } ++ ++ talloc_free(ctx); ++ return 0; ++} ++ ++static int inotify_test_dir_teardown(void **state) ++{ ++ struct inotify_test_ctx *ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ int ret; ++ ++ ret = unlink(ctx->filename); ++ if (ret == -1 && errno != ENOENT) { ++ return 1; ++ } ++ ++ ret = rmdir(ctx->dirname); ++ if (ret == -1 && errno != ENOENT) { ++ return 1; ++ } ++ ++ talloc_free(ctx); ++ return 0; ++} ++ ++static void file_mod_op(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, ++ struct inotify_test_ctx); ++ FILE *f; ++ ++ talloc_free(te); ++ ++ f = fopen(test_ctx->filename, "w"); ++ if (f == NULL) { ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++ ++ fprintf(f, "%s\n", test_ctx->filename); ++ fflush(f); ++ fclose(f); ++} ++ ++static void check_and_set_threshold(struct inotify_test_ctx *test_ctx, ++ uint32_t flags) ++{ ++ if (test_ctx->exp_flags != 0 && !(test_ctx->exp_flags & flags)) { ++ fail(); ++ } ++ ++ test_ctx->ncb++; ++} ++ ++static int inotify_set_threshold_cb(const char *filename, ++ uint32_t flags, ++ void *pvt) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(pvt, ++ struct inotify_test_ctx); ++ ++ check_and_set_threshold(test_ctx, flags); ++ return EOK; ++} ++ ++static int inotify_threshold_cb(const char *filename, ++ uint32_t flags, ++ void *pvt) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(pvt, ++ struct inotify_test_ctx); ++ ++ check_and_set_threshold(test_ctx, flags); ++ if (test_ctx->ncb == test_ctx->threshold) { ++ test_ctx->tctx->done = true; ++ return EOK; ++ } ++ ++ return EOK; ++} ++ ++/* Test that running two modifications fires the callback twice */ ++static void test_inotify_mod(void **state) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ struct snotify_ctx *ctx; ++ struct timeval tv; ++ struct tevent_timer *te; ++ errno_t ret; ++ ++ ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, ++ test_ctx->filename, NULL, IN_MODIFY, ++ inotify_threshold_cb, test_ctx); ++ assert_non_null(ctx); ++ ++ test_ctx->threshold = 2; ++ test_ctx->exp_flags = IN_MODIFY; ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_usec += 500; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_mod_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_sec += 1; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_mod_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ ret = test_ev_loop(test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++ ++ talloc_free(ctx); ++} ++ ++static void file_mv_op(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, ++ struct inotify_test_ctx); ++ FILE *f; ++ int fd; ++ char src_tmp_file[] = "test_inotify_src.XXXXXX"; ++ int ret; ++ ++ talloc_free(te); ++ ++ fd = mkstemp(src_tmp_file); ++ if (fd == -1) { ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++ ++ f = fdopen(fd, "w"); ++ if (f == NULL) { ++ close(fd); ++ unlink(src_tmp_file); ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++ ++ fprintf(f, "%s\n", test_ctx->filename); ++ fflush(f); ++ fclose(f); ++ ++ ret = rename(src_tmp_file, test_ctx->filename); ++ if (ret == -1) { ++ unlink(src_tmp_file); ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++} ++ ++static void test_inotify_mv(void **state) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ struct snotify_ctx *ctx; ++ struct timeval tv; ++ struct tevent_timer *te; ++ errno_t ret; ++ ++ ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, ++ test_ctx->filename, NULL, IN_MOVED_TO, ++ inotify_threshold_cb, test_ctx); ++ assert_non_null(ctx); ++ ++ test_ctx->threshold = 1; ++ test_ctx->exp_flags = IN_MOVED_TO; ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_usec += 200; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_mv_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ ret = test_ev_loop(test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++static void file_del_add_op(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, ++ struct inotify_test_ctx); ++ FILE *f; ++ int ret; ++ ++ talloc_free(te); ++ ++ ret = unlink(test_ctx->filename); ++ if (ret == -1) { ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++ ++ f = fopen(test_ctx->filename, "w"); ++ if (f == NULL) { ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++ ++ fprintf(f, "%s\n", test_ctx->filename); ++ fflush(f); ++ fclose(f); ++} ++ ++static void test_inotify_del_add(void **state) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ struct snotify_ctx *ctx; ++ struct timeval tv; ++ struct tevent_timer *te; ++ errno_t ret; ++ ++ test_ctx->threshold = 1; ++ test_ctx->exp_flags = IN_CREATE; ++ ++ ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, ++ test_ctx->filename, NULL, ++ IN_CREATE, ++ inotify_threshold_cb, test_ctx); ++ assert_non_null(ctx); ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_usec += 200; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_del_add_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ ret = test_ev_loop(test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++static void test_inotify_file_moved_in(void **state) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ struct snotify_ctx *ctx; ++ struct timeval tv; ++ struct tevent_timer *te; ++ errno_t ret; ++ ++ test_ctx->threshold = 1; ++ test_ctx->exp_flags = IN_CREATE; ++ ++ ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, ++ test_ctx->filename, NULL, ++ IN_CREATE | IN_CLOSE_WRITE, ++ inotify_threshold_cb, test_ctx); ++ assert_non_null(ctx); ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_usec += 200; ++ ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_mod_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ ret = test_ev_loop(test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++static void file_del_op(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, ++ struct inotify_test_ctx); ++ int ret; ++ ++ talloc_free(te); ++ ++ ret = unlink(test_ctx->filename); ++ if (ret == -1) { ++ test_ctx->tctx->error = errno; ++ test_ctx->tctx->done = true; ++ return; ++ } ++} ++ ++static void check_threshold_cb(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, ++ struct inotify_test_ctx); ++ ++ /* tests that no more callbacks were issued and exactly one ++ * was caught for both requests ++ */ ++ if (test_ctx->ncb == test_ctx->threshold) { ++ test_ctx->tctx->done = true; ++ return; ++ } ++ ++ fail(); ++} ++ ++static void test_inotify_delay(void **state) ++{ ++ struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct inotify_test_ctx); ++ struct snotify_ctx *ctx; ++ struct timeval tv; ++ struct tevent_timer *te; ++ errno_t ret; ++ struct timeval delay = { .tv_sec = 1, .tv_usec = 0 }; ++ ++ test_ctx->threshold = 1; ++ test_ctx->exp_flags = IN_CREATE | IN_DELETE; ++ ++ ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, ++ test_ctx->filename, &delay, ++ IN_CREATE | IN_DELETE, ++ inotify_set_threshold_cb, test_ctx); ++ assert_non_null(ctx); ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_usec += 100; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_mod_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_usec += 200; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, file_del_op, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_sec += 2; ++ te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, ++ tv, check_threshold_cb, test_ctx); ++ if (te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ return; ++ } ++ ++ ret = test_ev_loop(test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++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_setup_teardown(test_inotify_mv, ++ inotify_test_setup, ++ inotify_test_teardown), ++ cmocka_unit_test_setup_teardown(test_inotify_mod, ++ inotify_test_setup, ++ inotify_test_teardown), ++ cmocka_unit_test_setup_teardown(test_inotify_del_add, ++ inotify_test_setup, ++ inotify_test_teardown), ++ cmocka_unit_test_setup_teardown(test_inotify_file_moved_in, ++ inotify_test_dir_setup, ++ inotify_test_dir_teardown), ++ cmocka_unit_test_setup_teardown(test_inotify_delay, ++ inotify_test_dir_setup, ++ inotify_test_dir_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); ++ ++ return cmocka_run_group_tests(tests, NULL, NULL); ++} +diff --git a/src/util/inotify.c b/src/util/inotify.c +new file mode 100644 +index 0000000000000000000000000000000000000000..41466520d91a56e46690795f07bcd4cb0b16954a +--- /dev/null ++++ b/src/util/inotify.c +@@ -0,0 +1,562 @@ ++/* ++ Copyright (C) 2016 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 "util/inotify.h" ++#include "util/util.h" ++ ++/* For parent directories, we want to know if a file was moved there or ++ * created there ++ */ ++#define PARENT_DIR_MASK (IN_CREATE | IN_MOVED_TO) ++ ++/* This structure is recreated if we need to rewatch the file and/or ++ * directory ++ */ ++struct snotify_watch_ctx { ++ int inotify_fd; /* The inotify_fd */ ++ struct tevent_fd *tfd; /* Activity on the fd */ ++ ++ struct snotify_ctx *snctx; /* Pointer up to the main snotify struct */ ++ ++ /* In case we're also watching the parent directory, otherwise -1. ++ * We keep the variable here and not in snctx so that we're able ++ * to catch even changes to the parent directory ++ */ ++ int dir_wd; ++ /* The file watch */ ++ int file_wd; ++}; ++ ++/* This is what we call when an event we're interested in arrives */ ++struct snotify_cb_ctx { ++ snotify_cb_fn fn; ++ const char *fn_name; ++ uint32_t mask; ++ void *pvt; ++}; ++ ++/* One instance of a callback. We hoard the inotify notifications ++ * until timer fires in caught_flags ++ */ ++struct snotify_dispatcher { ++ struct tevent_timer *te; ++ uint32_t caught_flags; ++}; ++ ++struct snotify_ctx { ++ struct tevent_context *ev; ++ ++ /* The full path of the file we're watching, ++ * its file and directory components */ ++ const char *filename; ++ const char *dir_name; ++ const char *base_name; ++ ++ /* Private pointer passed to the callback */ ++ struct snotify_cb_ctx cb; ++ /* A singleton callback dispatcher */ ++ struct snotify_dispatcher *disp; ++ ++ /* Internal snotify flags */ ++ uint16_t snotify_flags; ++ /* The caller might decide to batch the updates and receive ++ * them all together with a delay ++ */ ++ struct timeval delay; ++ /* We keep the structure that actually does the work ++ * separately to be able to reinitialize it when the ++ * file is recrated or moved to the directory ++ */ ++ struct snotify_watch_ctx *wctx; ++}; ++ ++struct flg2str { ++ uint32_t flg; ++ const char *str; ++} flg_table[] = { ++ { 0x00000001, "IN_ACCESS" }, ++ { 0x00000002, "IN_MODIFY" }, ++ { 0x00000004, "IN_ATTRIB" }, ++ { 0x00000008, "IN_CLOSE_WRITE" }, ++ { 0x00000010, "IN_CLOSE_NOWRITE" }, ++ { 0x00000020, "IN_OPEN" }, ++ { 0x00000040, "IN_MOVED_FROM" }, ++ { 0x00000080, "IN_MOVED_TO" }, ++ { 0x00000100, "IN_CREATE" }, ++ { 0x00000200, "IN_DELETE" }, ++ { 0x00000400, "IN_DELETE_SELF" }, ++ { 0x00000800, "IN_MOVE_SELF" }, ++ { 0x00002000, "IN_UNMOUNT" }, ++ { 0x00004000, "IN_Q_OVERFLOW" }, ++ { 0x00008000, "IN_IGNORED" }, ++ { 0x01000000, "IN_ONLYDIR" }, ++ { 0x02000000, "IN_DONT_FOLLOW" }, ++ { 0x04000000, "IN_EXCL_UNLINK" }, ++ { 0x20000000, "IN_MASK_ADD" }, ++ { 0x40000000, "IN_ISDIR" }, ++ { 0x80000000, "IN_ONESHOT" }, ++ { 0, NULL }, ++}; ++ ++#if 0 ++static void debug_flags(uint32_t flags, const char *file) ++{ ++ char msgbuf[1024]; ++ size_t total = 0; ++ ++ if (!DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) { ++ return; ++ } ++ ++ for (int i = 0; flg_table[i].flg != 0; i++) { ++ if (flags & flg_table[i].flg) { ++ total += snprintf(msgbuf+total, ++ sizeof(msgbuf)-total, ++ "%s ", flg_table[i].str); ++ } ++ } ++ ++ if (total == 0) { ++ snprintf(msgbuf, sizeof(msgbuf), "NONE\n"); ++ } ++ DEBUG(SSSDBG_TRACE_LIBS, "Inotify event: %s on %s\n", msgbuf, file); ++} ++#endif ++ ++static void snotify_process_callbacks(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, ++ void *ptr) ++{ ++ struct snotify_ctx *snctx; ++ ++ snctx = talloc_get_type(ptr, struct snotify_ctx); ++ if (snctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Bad pointer\n"); ++ return; ++ } ++ ++ snctx->cb.fn(snctx->filename, ++ snctx->disp->caught_flags, ++ snctx->cb.pvt); ++ ++ talloc_zfree(snctx->disp); ++} ++ ++static struct snotify_dispatcher *create_dispatcher(struct snotify_ctx *snctx) ++{ ++ struct snotify_dispatcher *disp; ++ struct timeval tv; ++ ++ disp = talloc_zero(snctx, struct snotify_dispatcher); ++ if (disp == NULL) { ++ return NULL; ++ } ++ ++ gettimeofday(&tv, NULL); ++ tv.tv_sec += snctx->delay.tv_sec; ++ tv.tv_usec += snctx->delay.tv_usec; ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Running a timer with delay %ld.%ld\n", ++ (unsigned long) snctx->delay.tv_sec, ++ (unsigned long) snctx->delay.tv_usec); ++ ++ disp->te = tevent_add_timer(snctx->ev, disp, tv, ++ snotify_process_callbacks, ++ snctx); ++ if (disp->te == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); ++ talloc_free(disp); ++ return NULL; ++ } ++ ++ return disp; ++} ++ ++static struct snotify_dispatcher *get_dispatcher(struct snotify_ctx *snctx) ++{ ++ if (snctx->disp != NULL) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Reusing existing dispatcher\n"); ++ return snctx->disp; ++ } ++ ++ return create_dispatcher(snctx); ++} ++ ++static errno_t dispatch_event(struct snotify_ctx *snctx, ++ uint32_t ev_flags) ++{ ++ struct snotify_dispatcher *disp; ++ ++ if ((snctx->cb.mask & ev_flags) == 0) { ++ return EOK; ++ } ++ ++ disp = get_dispatcher(snctx); ++ if (disp == NULL) { ++ return ENOMEM; ++ } ++ ++ disp->caught_flags |= ev_flags; ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Dispatched an event with combined flags 0x%X\n", ++ disp->caught_flags); ++ ++ snctx->disp = disp; ++ return EOK; ++} ++ ++static errno_t process_dir_event(struct snotify_ctx *snctx, ++ const struct inotify_event *in_event) ++{ ++ errno_t ret; ++ ++ DEBUG(SSSDBG_TRACE_ALL, "inotify name: %s\n", in_event->name); ++ if (in_event->len == 0 \ ++ || strcmp(in_event->name, snctx->base_name) != 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Not interested in %s\n", in_event->name); ++ return EOK; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "received notification for watched file [%s] under %s\n", ++ in_event->name, snctx->dir_name); ++ ++ /* file the event for the file to see if the caller is interested in it */ ++ ret = dispatch_event(snctx, in_event->mask); ++ if (ret == EOK) { ++ /* Tells the outer loop to re-initialize flags once the loop is finished. ++ * However, finish reading all the events first to make sure we don't ++ * miss any ++ */ ++ return EAGAIN; ++ } ++ ++ return ret; ++} ++ ++static errno_t process_file_event(struct snotify_ctx *snctx, ++ const struct inotify_event *in_event) ++{ ++ if (in_event->mask & IN_IGNORED) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Will reopen moved or deleted file %s\n", snctx->filename); ++ /* Notify caller of the event, don't quit */ ++ return EAGAIN; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "received notification for watched file %s\n", snctx->filename); ++ ++ return dispatch_event(snctx, in_event->mask); ++} ++ ++static errno_t snotify_rewatch(struct snotify_ctx *snctx); ++ ++static void snotify_internal_cb(struct tevent_context *ev, ++ struct tevent_fd *fde, ++ uint16_t flags, ++ void *data) ++{ ++ char ev_buf[sizeof(struct inotify_event) + PATH_MAX]; ++ const char *ptr; ++ const struct inotify_event *in_event; ++ struct snotify_ctx *snctx; ++ ssize_t len; ++ errno_t ret; ++ bool rewatch; ++ ++ snctx = talloc_get_type(data, struct snotify_ctx); ++ if (snctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Bad pointer\n"); ++ return; ++ } ++ ++ while (1) { ++ len = read(snctx->wctx->inotify_fd, ev_buf, sizeof(ev_buf)); ++ if (len == -1) { ++ ret = errno; ++ if (ret != EAGAIN) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Cannot read inotify_event [%d]: %s\n", ++ ret, strerror(ret)); ++ } else { ++ DEBUG(SSSDBG_TRACE_INTERNAL, "All inotify events processed\n"); ++ } ++ return; ++ } ++ ++ if ((size_t) len < sizeof(struct inotify_event)) { ++ /* Did not even read the required amount of data, move on.. */ ++ continue; ++ } ++ ++ for (ptr = ev_buf; ++ ptr < ev_buf + len; ++ ptr += sizeof(struct inotify_event) + in_event->len) { ++ ++ in_event = (const struct inotify_event *) ptr; ++ ++ //debug_flags(in_event->mask, in_event->name); ++ ++ if (snctx->wctx->dir_wd == in_event->wd) { ++ ret = process_dir_event(snctx, in_event); ++ if (ret == EAGAIN) { ++ rewatch = true; ++ /* Continue with the loop and read all the events from ++ * this descriptor first, then rewatch when done ++ */ ++ } else if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Failed to process inotify event\n"); ++ continue; ++ } ++ } else if (snctx->wctx->file_wd == in_event->wd) { ++ ret = process_file_event(snctx, in_event); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Failed to process inotify event\n"); ++ continue; ++ } ++ } else { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Uknown watch %d\n", in_event->wd); ++ ret = EOK; ++ } ++ } ++ } ++ ++ if (rewatch) { ++ ret = snotify_rewatch(snctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to re-set watch"); ++ } ++ } ++} ++ ++static int watch_ctx_destructor(void *memptr) ++{ ++ struct snotify_watch_ctx *wctx; ++ ++ wctx = talloc_get_type(memptr, struct snotify_watch_ctx); ++ if (wctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Bad pointer\n"); ++ return 1; ++ } ++ ++ /* We don't need to close the watches explicitly. man 7 inotify says: ++ * When all file descriptors referring to an inotify instance ++ * have been closed (using close(2)), the underlying object ++ * and its resources are freed for reuse by the kernel; all ++ * associated watches are automatically freed. ++ */ ++ if (wctx->inotify_fd != -1) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Closing inotify fd %d\n", wctx->inotify_fd); ++ close(wctx->inotify_fd); ++ } ++ ++ return 0; ++} ++ ++static errno_t copy_filenames(struct snotify_ctx *snctx, ++ const char *filename) ++{ ++ char *p; ++ char fcopy[PATH_MAX]; ++ ++ strncpy(fcopy, filename, sizeof(fcopy)); ++ fcopy[PATH_MAX-1] = '\0'; ++ ++ p = dirname(fcopy); ++ if (p == NULL) { ++ return EIO; ++ } ++ ++ snctx->dir_name = talloc_strdup(snctx, p); ++ if (snctx->dir_name == NULL) { ++ return ENOMEM; ++ } ++ ++ strncpy(fcopy, filename, sizeof(fcopy)); ++ fcopy[PATH_MAX-1] = '\0'; ++ ++ p = basename(fcopy); ++ if (p == NULL) { ++ return EIO; ++ } ++ ++ snctx->base_name = talloc_strdup(snctx, p); ++ if (snctx->base_name == NULL) { ++ return ENOMEM; ++ } ++ ++ snctx->filename = talloc_strdup(snctx, filename); ++ if (snctx->filename == NULL) { ++ return ENOMEM; ++ } ++ ++ return EOK; ++} ++ ++static struct snotify_watch_ctx *snotify_watch(struct snotify_ctx *snctx, ++ uint32_t mask) ++{ ++ struct snotify_watch_ctx *wctx; ++ errno_t ret; ++ ++ wctx = talloc_zero(snctx, struct snotify_watch_ctx); ++ if (wctx == NULL) { ++ return NULL; ++ } ++ wctx->inotify_fd = -1; ++ wctx->dir_wd = -1; ++ wctx->file_wd = -1; ++ wctx->snctx = snctx; ++ talloc_set_destructor((TALLOC_CTX *)wctx, watch_ctx_destructor); ++ ++ wctx->inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); ++ if (wctx->inotify_fd == -1) { ++ ret = errno; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "inotify_init1 failed: %d: %s\n", ret, strerror(ret)); ++ goto fail; ++ } ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Opened inotify fd %d\n", wctx->inotify_fd); ++ ++ wctx->tfd = tevent_add_fd(snctx->ev, wctx, wctx->inotify_fd, ++ TEVENT_FD_READ, snotify_internal_cb, ++ snctx); ++ if (wctx->tfd == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot add tevent fd watch for %s\n", ++ snctx->filename); ++ goto fail; ++ } ++ ++ wctx->file_wd = inotify_add_watch(wctx->inotify_fd, snctx->filename, mask); ++ if (wctx->file_wd == -1) { ++ ret = errno; ++ if (ret != ENOENT || (!(snctx->snotify_flags & SNOTIFY_WATCH_DIR))) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "inotify_add_watch failed [%d]: %s\n", ++ ret, strerror(ret)); ++ goto fail; ++ } ++ } ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Opened file watch %d\n", wctx->file_wd); ++ ++ if (snctx->snotify_flags & SNOTIFY_WATCH_DIR) { ++ /* Create a watch for the parent directory. This is useful for cases ++ * where we start watching a file before it's created, but still want ++ * a notification when the file is moved in ++ */ ++ wctx->dir_wd = inotify_add_watch(wctx->inotify_fd, ++ snctx->dir_name, PARENT_DIR_MASK); ++ if (wctx->dir_wd == -1) { ++ ret = errno; ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "inotify_add_watch failed [%d]: %s\n", ++ ret, strerror(ret)); ++ goto fail; ++ } ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Opened directory watch %d\n", wctx->dir_wd); ++ } ++ ++ return wctx; ++ ++fail: ++ talloc_free(wctx); ++ return NULL; ++} ++ ++static errno_t snotify_rewatch(struct snotify_ctx *snctx) ++{ ++ talloc_free(snctx->wctx); ++ ++ snctx->wctx = snotify_watch(snctx, snctx->cb.mask); ++ if (snctx->wctx == NULL) { ++ return ENOMEM; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Recreated watch\n"); ++ return EOK; ++} ++ ++struct snotify_ctx *_snotify_create(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ uint16_t snotify_flags, ++ const char *filename, ++ struct timeval *delay, ++ uint32_t mask, ++ snotify_cb_fn fn, ++ const char *fn_name, ++ void *pvt) ++{ ++ errno_t ret; ++ struct snotify_ctx *snctx; ++ ++ snctx = talloc_zero(mem_ctx, struct snotify_ctx); ++ if (snctx == NULL) { ++ return NULL; ++ } ++ ++ snctx->ev = ev; ++ snctx->snotify_flags = snotify_flags; ++ if (delay) { ++ snctx->delay.tv_sec = delay->tv_sec; ++ snctx->delay.tv_usec = delay->tv_usec; ++ } ++ ++ snctx->cb.fn = fn; ++ snctx->cb.fn_name = fn_name; ++ snctx->cb.mask = mask; ++ snctx->cb.pvt = pvt; ++ ++ ret = copy_filenames(snctx, filename); ++ if (ret != EOK) { ++ talloc_free(snctx); ++ return NULL; ++ } ++ ++ snctx->wctx = snotify_watch(snctx, mask); ++ if (snctx->wctx == NULL) { ++ talloc_free(snctx); ++ return NULL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Added a watch for %s with inotify flags 0x%X " ++ "internal flags 0x%X " ++ "using function %s after delay %ld.%ld\n", ++ snctx->filename, ++ mask, ++ snotify_flags, ++ fn_name, ++ (unsigned long) snctx->delay.tv_sec, ++ (unsigned long) snctx->delay.tv_usec); ++ ++ return snctx; ++} +diff --git a/src/util/inotify.h b/src/util/inotify.h +new file mode 100644 +index 0000000000000000000000000000000000000000..3592944522c78b96cb8e0777c2937f9f2f01758c +--- /dev/null ++++ b/src/util/inotify.h +@@ -0,0 +1,61 @@ ++/* ++ Copyright (C) 2016 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 __INOTIFY_H_ ++#define __INOTIFY_H_ ++ ++#include ++#include ++#include ++ ++ ++typedef int (*snotify_cb_fn)(const char *filename, ++ uint32_t caught_flags, ++ void *pvt); ++ ++#define SNOTIFY_WATCH_DIR 0x0001 ++ ++/* ++ * Set up an inotify watch for file at filename. When an inotify ++ * event is caught, it must match the "mask" parameter. The watch ++ * would then call snotify_cb_fn() and include the caught flags. ++ * ++ * If snotify_flags includes SNOTIFY_WATCH_DIR, also the parent directory ++ * of this file would be watched to cover cases where the file might not ++ * exist when the watch is created. ++ * ++ * If you wish to batch inotify requests to avoid hammering the caller ++ * with several successive requests, use the delay parameter. The function ++ * would then only send invoke the callback after the delay and the caught ++ * flags would be OR-ed. By default, the callback is invoked immediately. ++ * ++ * Use the pvt parameter to pass a private context to the function ++ */ ++struct snotify_ctx *_snotify_create(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ uint16_t snotify_flags, ++ const char *filename, ++ struct timeval *delay, ++ uint32_t mask, ++ snotify_cb_fn fn, ++ const char *fn_name, ++ void *pvt); ++ ++#define snotify_create(mem_ctx, ev, snotify_flags, filename, delay, mask, fn, pvt) \ ++ _snotify_create(mem_ctx, ev, snotify_flags, filename, delay, mask, fn, #fn, pvt); ++ ++#endif /* __INOTIFY_H_ */ +-- +2.9.3 + diff --git a/0051-CONFDB-Re-enable-the-files-provider.patch b/0051-CONFDB-Re-enable-the-files-provider.patch new file mode 100644 index 0000000..2c5df7f --- /dev/null +++ b/0051-CONFDB-Re-enable-the-files-provider.patch @@ -0,0 +1,42 @@ +From 90a103d6050b266fd8fc8fd0636be32de5885dec Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 25 Oct 2016 16:15:06 +0200 +Subject: [PATCH 51/79] CONFDB: Re-enable the files provider +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The files provider was "blacklisted" for a long time, because very old +(pre-1.0) versions of sssd had the capability to create users and groups +by calling into the shadow-utils binaries directly which was later +removed. + +Since nobody is (hopefully) running these ancient versions anymore and +we are about to re-enable the files provider, we can remove this check. + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index 69200f5cd4dded1c25dc34e06595809c65670205..b0a75867c61fa3af21990d92419f17baa34864ae 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -900,13 +900,6 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + goto done; + } + +- if (strcasecmp(domain->provider, "files") == 0) { +- /* The files provider is not valid anymore */ +- DEBUG(SSSDBG_FATAL_FAILURE, "The \"files\" provider is invalid\n"); +- ret = EINVAL; +- goto done; +- } +- + if (strcasecmp(domain->provider, "local") == 0) { + /* If this is the local provider, we need to ensure that + * no other provider was specified for other types, since +-- +2.9.3 + diff --git a/0052-FILES-Add-the-files-provider.patch b/0052-FILES-Add-the-files-provider.patch new file mode 100644 index 0000000..b7b44b7 --- /dev/null +++ b/0052-FILES-Add-the-files-provider.patch @@ -0,0 +1,1329 @@ +From c71e0a6710418991d759a329b8dcb77c7ad3e16e Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 25 Oct 2016 15:58:27 +0200 +Subject: [PATCH 52/79] FILES: Add the files provider +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds a new provider type "files". The provider watches the UNIX password +and group databases for changes using inotify and propagates its +contents to the sysdb. + +The files provider is only built on platforms that support the inotify +interface, polling or loading the entries on-deman is not supported. + +During initialization, the files are loaded from the environment +variables SSS_FILES_PASSWD and SSS_FILES_GROUP, defaulting to +/etc/passwd and /etc/group respectively. Loading the files from +environment variables is mostly implemented for tests that need to load +nss_wrapped files. + +The files provider is a bit different from other provider types in the +sense that it always enumerates full contents of the database. +Therefore, the requests from Data Provider are always just replied to +with success. Enumerating the contents is done in full at the moment, +all users and all groups are removed and added anew. Modifying the +passwd and group databses should be rare enough for this to be +justified and we can optimize the code later. + +Since with large databases, the cache update might take a bit of time, +we signal the responders to disable the files domain once we receive the +inotify notification and re-enable the files domain after the update is +finished. The idea is that the NSS configuration would still contain +"files" after "sss" so that if the domain is disabled, libc would fall +back to a direct "files" lookup. + +Resolves: +https://fedorahosted.org/sssd/ticket/3262 + +Reviewed-by: Pavel Březina +--- + Makefile.am | 29 +- + contrib/sssd.spec.in | 3 + + src/providers/files/files_id.c | 179 ++++++++ + src/providers/files/files_init.c | 92 +++++ + src/providers/files/files_ops.c | 801 ++++++++++++++++++++++++++++++++++++ + src/providers/files/files_private.h | 74 ++++ + src/tests/dlopen-tests.c | 2 + + 7 files changed, 1179 insertions(+), 1 deletion(-) + create mode 100644 src/providers/files/files_id.c + create mode 100644 src/providers/files/files_init.c + create mode 100644 src/providers/files/files_ops.c + create mode 100644 src/providers/files/files_private.h + +diff --git a/Makefile.am b/Makefile.am +index 09c021d22c85ae254639b3b6e43e453cbc40ecfa..e6d3530d003fff9e92ff6520043f6906d4200bd7 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -339,7 +339,8 @@ sssdlib_LTLIBRARIES = \ + libsss_ldap.la \ + libsss_krb5.la \ + libsss_proxy.la \ +- libsss_simple.la ++ libsss_simple.la \ ++ $(NULL) + + if BUILD_SAMBA + sssdlib_LTLIBRARIES += \ +@@ -347,6 +348,12 @@ sssdlib_LTLIBRARIES += \ + libsss_ad.la + endif + ++if HAVE_INOTIFY ++sssdlib_LTLIBRARIES += \ ++ libsss_files.la \ ++ $(NULL) ++endif # HAVE_INOTIFY ++ + ldblib_LTLIBRARIES = \ + memberof.la + +@@ -771,6 +778,7 @@ dist_noinst_HEADERS = \ + src/providers/ad/ad_subdomains.h \ + src/providers/proxy/proxy.h \ + src/providers/proxy/proxy_iface_generated.h \ ++ src/providers/files/files_private.h \ + src/tools/tools_util.h \ + src/tools/sss_sync_ops.h \ + src/resolv/async_resolv.h \ +@@ -1853,6 +1861,7 @@ if BUILD_SEMANAGE + FILES_TESTS_LIBS += $(SEMANAGE_LIBS) + endif + ++if HAVE_INOTIFY + files_tests_SOURCES = \ + src/tests/files-tests.c \ + src/util/check_and_open.c \ +@@ -1866,6 +1875,7 @@ files_tests_LDADD = \ + $(FILES_TESTS_LIBS) \ + libsss_test_common.la \ + $(SSSD_INTERNAL_LTLIBS) ++endif # HAVE_INOTIFY + + SSSD_RESOLV_TESTS_OBJ = \ + $(SSSD_RESOLV_OBJ) +@@ -3510,6 +3520,23 @@ libsss_proxy_la_LDFLAGS = \ + -avoid-version \ + -module + ++libsss_files_la_SOURCES = \ ++ src/providers/files/files_init.c \ ++ src/providers/files/files_id.c \ ++ src/providers/files/files_ops.c \ ++ src/util/inotify.c \ ++ $(NULL) ++libsss_files_la_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++libsss_files_la_LIBADD = \ ++ $(PAM_LIBS) \ ++ $(NULL) ++libsss_files_la_LDFLAGS = \ ++ -avoid-version \ ++ -module \ ++ $(NULL) ++ + libsss_simple_la_SOURCES = \ + src/providers/simple/simple_access_check.c \ + src/providers/simple/simple_access.c +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index c0c9e364b781586f0e2ad68a999c310135574d46..fe51f9998aedbc7c157e8897a0dae67af037cec4 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -848,6 +848,9 @@ done + %{_sbindir}/sss_cache + %{_libexecdir}/%{servicename}/sss_signal + ++# The files provider is intentionally packaged in -common ++%{_libdir}/%{name}/libsss_files.so ++ + %dir %{sssdstatedir} + %dir %{_localstatedir}/cache/krb5rcache + %attr(700,sssd,sssd) %dir %{dbpath} +diff --git a/src/providers/files/files_id.c b/src/providers/files/files_id.c +new file mode 100644 +index 0000000000000000000000000000000000000000..41314c66b1e435b51fe0b9bc18779c11ad261773 +--- /dev/null ++++ b/src/providers/files/files_id.c +@@ -0,0 +1,179 @@ ++/* ++ SSSD ++ ++ files_id.c - Identity operaions on the files provider ++ ++ Copyright (C) 2016 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 "providers/data_provider/dp.h" ++#include "providers/files/files_private.h" ++ ++struct files_account_info_handler_state { ++ struct dp_reply_std reply; ++ ++ struct files_id_ctx *id_ctx; ++}; ++ ++struct tevent_req * ++files_account_info_handler_send(TALLOC_CTX *mem_ctx, ++ struct files_id_ctx *id_ctx, ++ struct dp_id_data *data, ++ struct dp_req_params *params) ++{ ++ struct files_account_info_handler_state *state; ++ struct tevent_req *req; ++ struct tevent_req **update_req = NULL; ++ bool needs_update; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct files_account_info_handler_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ state->id_ctx = id_ctx; ++ ++ switch (data->entry_type & BE_REQ_TYPE_MASK) { ++ case BE_REQ_USER: ++ if (data->filter_type != BE_FILTER_ENUM) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected user filter type: %d\n", data->filter_type); ++ ret = EINVAL; ++ goto immediate; ++ } ++ update_req = &id_ctx->users_req; ++ needs_update = id_ctx->updating_passwd ? true : false; ++ break; ++ case BE_REQ_GROUP: ++ if (data->filter_type != BE_FILTER_ENUM) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected group filter type: %d\n", data->filter_type); ++ ret = EINVAL; ++ goto immediate; ++ } ++ update_req = &id_ctx->groups_req; ++ needs_update = id_ctx->updating_groups ? true : false; ++ break; ++ case BE_REQ_INITGROUPS: ++ if (data->filter_type != BE_FILTER_NAME) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected initgr filter type: %d\n", data->filter_type); ++ ret = EINVAL; ++ goto immediate; ++ } ++ if (strcmp(data->filter_value, DP_REQ_OPT_FILES_INITGR) != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected initgr filter value: %d\n", data->filter_type); ++ ret = EINVAL; ++ goto immediate; ++ } ++ update_req = &id_ctx->initgroups_req; ++ needs_update = id_ctx->updating_groups || id_ctx->updating_passwd \ ++ ? true \ ++ : false; ++ break; ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected entry type: %d\n", data->entry_type & BE_REQ_TYPE_MASK); ++ ret = EINVAL; ++ goto immediate; ++ } ++ ++ if (needs_update == false) { ++ DEBUG(SSSDBG_TRACE_LIBS, "The files domain no longer needs an update\n"); ++ ret = EOK; ++ goto immediate; ++ } ++ ++ if (*update_req != NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Received a concurrent update!\n"); ++ ret = EAGAIN; ++ goto immediate; ++ } ++ ++ /* id_ctx now must mark the requests as updated when the inotify-induced ++ * update finishes ++ */ ++ *update_req = req; ++ return req; ++ ++immediate: ++ dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL); ++ ++ if (ret == EOK) { ++ tevent_req_done(req); ++ } else { ++ tevent_req_error(req, ret); ++ } ++ ++ tevent_req_post(req, params->ev); ++ return req; ++} ++ ++static void finish_update_req(struct tevent_req **update_req, ++ errno_t ret) ++{ ++ if (*update_req == NULL) { ++ return; ++ } ++ ++ if (ret != EOK) { ++ tevent_req_error(*update_req, ret); ++ } else { ++ tevent_req_done(*update_req); ++ } ++ *update_req = NULL; ++} ++ ++void files_account_info_finished(struct files_id_ctx *id_ctx, ++ int req_type, ++ errno_t ret) ++{ ++ switch (req_type) { ++ case BE_REQ_USER: ++ finish_update_req(&id_ctx->users_req, ret); ++ if (id_ctx->updating_groups == false) { ++ finish_update_req(&id_ctx->initgroups_req, ret); ++ } ++ break; ++ case BE_REQ_GROUP: ++ finish_update_req(&id_ctx->groups_req, ret); ++ if (id_ctx->updating_passwd == false) { ++ finish_update_req(&id_ctx->initgroups_req, ret); ++ } ++ break; ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected req_type %d\n", req_type); ++ return; ++ } ++} ++ ++errno_t files_account_info_handler_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct dp_reply_std *data) ++{ ++ struct files_account_info_handler_state *state = NULL; ++ ++ state = tevent_req_data(req, struct files_account_info_handler_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *data = state->reply; ++ return EOK; ++} +diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c +new file mode 100644 +index 0000000000000000000000000000000000000000..b91dfbac9bf9d4b678ebdfa6b1cb0971b4477dd9 +--- /dev/null ++++ b/src/providers/files/files_init.c +@@ -0,0 +1,92 @@ ++/* ++ SSSD ++ ++ files_init.c - Initialization of the files provider ++ ++ Copyright (C) 2016 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 "providers/data_provider/dp.h" ++#include "providers/files/files_private.h" ++ ++int sssm_files_init(TALLOC_CTX *mem_ctx, ++ struct be_ctx *be_ctx, ++ struct data_provider *provider, ++ const char *module_name, ++ void **_module_data) ++{ ++ struct files_id_ctx *ctx; ++ int ret; ++ const char *passwd_file = NULL; ++ const char *group_file = NULL; ++ ++ /* So far this is mostly useful for tests */ ++ passwd_file = getenv("SSS_FILES_PASSWD"); ++ if (passwd_file == NULL) { ++ passwd_file = "/etc/passwd"; ++ } ++ ++ group_file = getenv("SSS_FILES_GROUP"); ++ if (group_file == NULL) { ++ group_file = "/etc/group"; ++ } ++ ++ ctx = talloc_zero(mem_ctx, struct files_id_ctx); ++ if (ctx == NULL) { ++ return ENOMEM; ++ } ++ ctx->be = be_ctx; ++ ctx->domain = be_ctx->domain; ++ ctx->passwd_file = passwd_file; ++ ctx->group_file = group_file; ++ ++ ctx->fctx = sf_init(ctx, be_ctx->ev, ++ ctx->passwd_file, ctx->group_file, ++ ctx); ++ if (ctx->fctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ *_module_data = ctx; ++ ret = EOK; ++done: ++ if (ret != EOK) { ++ talloc_free(ctx); ++ } ++ return ret; ++} ++ ++int sssm_files_id_init(TALLOC_CTX *mem_ctx, ++ struct be_ctx *be_ctx, ++ void *module_data, ++ struct dp_method *dp_methods) ++{ ++ struct files_id_ctx *ctx; ++ ++ ctx = talloc_get_type(module_data, struct files_id_ctx); ++ if (ctx == NULL) { ++ return EINVAL; ++ } ++ ++ dp_set_method(dp_methods, DPM_ACCOUNT_HANDLER, ++ files_account_info_handler_send, ++ files_account_info_handler_recv, ++ ctx, struct files_id_ctx, ++ struct dp_id_data, struct dp_reply_std); ++ ++ return EOK; ++} +diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c +new file mode 100644 +index 0000000000000000000000000000000000000000..beda47abd75efd1d399ce074cceb9ea9f4b1399f +--- /dev/null ++++ b/src/providers/files/files_ops.c +@@ -0,0 +1,801 @@ ++/* ++ SSSD ++ ++ Files provider operations ++ ++ Copyright (C) 2016 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 "config.h" ++ ++#include "providers/files/files_private.h" ++#include "db/sysdb.h" ++#include "util/inotify.h" ++#include "util/util.h" ++ ++#define FILES_REALLOC_CHUNK 64 ++ ++#define PWD_MAXSIZE 1024 ++#define GRP_MAXSIZE 2048 ++ ++struct files_ctx { ++ struct snotify_ctx *pwd_watch; ++ struct snotify_ctx *grp_watch; ++ ++ struct files_ops_ctx *ops; ++}; ++ ++static errno_t enum_files_users(TALLOC_CTX *mem_ctx, ++ struct files_id_ctx *id_ctx, ++ struct passwd ***_users) ++{ ++ errno_t ret, close_ret; ++ struct passwd *pwd_iter = NULL; ++ struct passwd *pwd = NULL; ++ struct passwd **users = NULL; ++ FILE *pwd_handle = NULL; ++ size_t n_users = 0; ++ ++ pwd_handle = fopen(id_ctx->passwd_file, "r"); ++ if (pwd_handle == NULL) { ++ ret = errno; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot open passwd file %s [%d]\n", ++ id_ctx->passwd_file, ret); ++ goto done; ++ } ++ ++ users = talloc_zero_array(mem_ctx, struct passwd *, ++ FILES_REALLOC_CHUNK); ++ if (users == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ while ((pwd_iter = fgetpwent(pwd_handle)) != NULL) { ++ /* FIXME - we might want to support paging of sorts to avoid allocating ++ * all users atop a memory context or only return users that differ from ++ * the local storage as a diff to minimize memory spikes ++ */ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "User found (%s, %s, %"SPRIuid", %"SPRIgid", %s, %s, %s)\n", ++ pwd_iter->pw_name, pwd_iter->pw_passwd, ++ pwd_iter->pw_uid, pwd_iter->pw_gid, ++ pwd_iter->pw_gecos, pwd_iter->pw_dir, ++ pwd_iter->pw_shell); ++ ++ pwd = talloc_zero(users, struct passwd); ++ if (pwd == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ pwd->pw_uid = pwd_iter->pw_uid; ++ pwd->pw_gid = pwd_iter->pw_gid; ++ ++ pwd->pw_name = talloc_strdup(pwd, pwd_iter->pw_name); ++ if (pwd->pw_name == NULL) { ++ /* We only check pw_name here on purpose to allow broken ++ * records to be optionally rejected when saving them ++ * or fallback values to be used. ++ */ ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ pwd->pw_dir = talloc_strdup(pwd, pwd_iter->pw_dir); ++ pwd->pw_gecos = talloc_strdup(pwd, pwd_iter->pw_gecos); ++ pwd->pw_shell = talloc_strdup(pwd, pwd_iter->pw_shell); ++ pwd->pw_passwd = talloc_strdup(pwd, pwd_iter->pw_passwd); ++ ++ users[n_users] = pwd; ++ n_users++; ++ if (n_users % FILES_REALLOC_CHUNK == 0) { ++ users = talloc_realloc(mem_ctx, ++ users, ++ struct passwd *, ++ talloc_get_size(users) + FILES_REALLOC_CHUNK); ++ if (users == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ ++ ret = EOK; ++ *_users = users; ++done: ++ if (ret != EOK) { ++ talloc_free(users); ++ } ++ ++ if (pwd_handle) { ++ close_ret = fclose(pwd_handle); ++ if (close_ret != 0) { ++ close_ret = errno; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot close passwd file %s [%d]\n", ++ id_ctx->passwd_file, close_ret); ++ } ++ } ++ return ret; ++} ++ ++static errno_t enum_files_groups(TALLOC_CTX *mem_ctx, ++ struct files_id_ctx *id_ctx, ++ struct group ***_groups) ++{ ++ errno_t ret, close_ret; ++ struct group *grp_iter = NULL; ++ struct group *grp = NULL; ++ struct group **groups = NULL; ++ size_t n_groups = 0; ++ FILE *grp_handle = NULL; ++ ++ grp_handle = fopen(id_ctx->group_file, "r"); ++ if (grp_handle == NULL) { ++ ret = errno; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot open group file %s [%d]\n", ++ id_ctx->group_file, ret); ++ goto done; ++ } ++ ++ groups = talloc_zero_array(mem_ctx, struct group *, ++ FILES_REALLOC_CHUNK); ++ if (groups == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ while ((grp_iter = fgetgrent(grp_handle)) != NULL) { ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "Group found (%s, %"SPRIgid")\n", ++ grp_iter->gr_name, grp_iter->gr_gid); ++ ++ grp = talloc_zero(groups, struct group); ++ if (grp == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ grp->gr_gid = grp_iter->gr_gid; ++ grp->gr_name = talloc_strdup(grp, grp_iter->gr_name); ++ if (grp->gr_name == NULL) { ++ /* We only check gr_name here on purpose to allow broken ++ * records to be optionally rejected when saving them ++ * or fallback values to be used. ++ */ ++ ret = ENOMEM; ++ goto done; ++ } ++ grp->gr_passwd = talloc_strdup(grp, grp_iter->gr_passwd); ++ ++ if (grp_iter->gr_mem != NULL && grp_iter->gr_mem[0] != '\0') { ++ size_t nmem; ++ ++ for (nmem = 0; grp_iter->gr_mem[nmem] != NULL; nmem++); ++ ++ grp->gr_mem = talloc_zero_array(grp, char *, nmem + 1); ++ if (grp->gr_mem == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (nmem = 0; grp_iter->gr_mem[nmem] != NULL; nmem++) { ++ grp->gr_mem[nmem] = talloc_strdup(grp, grp_iter->gr_mem[nmem]); ++ if (grp->gr_mem[nmem] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ ++ groups[n_groups] = grp; ++ n_groups++; ++ if (n_groups % FILES_REALLOC_CHUNK == 0) { ++ groups = talloc_realloc(mem_ctx, ++ groups, ++ struct group *, ++ talloc_get_size(groups) + FILES_REALLOC_CHUNK); ++ if (groups == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ } ++ ++ ret = EOK; ++ *_groups = groups; ++done: ++ if (ret != EOK) { ++ talloc_free(groups); ++ } ++ ++ if (grp_handle) { ++ close_ret = fclose(grp_handle); ++ if (close_ret != 0) { ++ close_ret = errno; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot close group file %s [%d]\n", ++ id_ctx->group_file, close_ret); ++ } ++ } ++ return ret; ++} ++ ++static errno_t delete_all_users(struct sss_domain_info *dom) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_dn *base_dn; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; ++ } ++ ++ base_dn = sysdb_user_base_dn(tmp_ctx, dom); ++ if (base_dn == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_delete_recursive(dom->sysdb, base_dn, true); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to delete users subtree [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ ++static errno_t save_file_user(struct files_id_ctx *id_ctx, ++ struct passwd *pw) ++{ ++ errno_t ret; ++ char *fqname; ++ TALLOC_CTX *tmp_ctx = NULL; ++ const char *shell; ++ const char *gecos; ++ struct sysdb_attrs *attrs = NULL; ++ ++ if (strcmp(pw->pw_name, "root") == 0 ++ || pw->pw_uid == 0 ++ || pw->pw_gid == 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Skipping %s\n", pw->pw_name); ++ return EOK; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ fqname = sss_create_internal_fqname(tmp_ctx, pw->pw_name, ++ id_ctx->domain->name); ++ if (fqname == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ attrs = sysdb_new_attrs(tmp_ctx); ++ if (attrs == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ if (pw->pw_shell && pw->pw_shell[0] != '\0') { ++ shell = pw->pw_shell; ++ } else { ++ shell = NULL; ++ } ++ ++ if (pw->pw_gecos && pw->pw_gecos[0] != '\0') { ++ gecos = pw->pw_gecos; ++ } else { ++ gecos = NULL; ++ } ++ ++ /* FIXME - optimize later */ ++ ret = sysdb_store_user(id_ctx->domain, ++ fqname, ++ pw->pw_passwd, ++ pw->pw_uid, ++ pw->pw_gid, ++ gecos, ++ pw->pw_dir, ++ shell, ++ NULL, attrs, ++ NULL, 0, 0); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static errno_t sf_enum_groups(struct files_id_ctx *id_ctx); ++ ++errno_t sf_enum_users(struct files_id_ctx *id_ctx) ++{ ++ errno_t ret; ++ errno_t tret; ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct passwd **users = NULL; ++ bool in_transaction = false; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = enum_files_users(tmp_ctx, id_ctx, &users); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sysdb_transaction_start(id_ctx->domain->sysdb); ++ if (ret != EOK) { ++ goto done; ++ } ++ in_transaction = true; ++ ++ /* remove previous cache contents */ ++ /* FIXME - this is terribly inefficient */ ++ ret = delete_all_users(id_ctx->domain); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ for (size_t i = 0; users[i]; i++) { ++ ret = save_file_user(id_ctx, users[i]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot save user %s: [%d]: %s\n", ++ users[i]->pw_name, ret, sss_strerror(ret)); ++ continue; ++ } ++ } ++ ++ ret = sysdb_transaction_commit(id_ctx->domain->sysdb); ++ if (ret != EOK) { ++ goto done; ++ } ++ in_transaction = false; ++ ++ /* Covers the case when someone edits /etc/group, adds a group member and ++ * only then edits passwd and adds the user. The reverse is not needed, ++ * because member/memberof links are established when groups are saved. ++ */ ++ ret = sf_enum_groups(id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Cannot refresh groups\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++done: ++ if (in_transaction) { ++ tret = sysdb_transaction_cancel(id_ctx->domain->sysdb); ++ if (tret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot cancel transaction: %d\n", ret); ++ } ++ } ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static const char **get_cached_user_names(TALLOC_CTX *mem_ctx, ++ struct sss_domain_info *dom) ++{ ++ errno_t ret; ++ struct ldb_result *res = NULL; ++ const char **user_names = NULL; ++ unsigned c = 0; ++ ++ ret = sysdb_enumpwent(mem_ctx, dom, &res); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ user_names = talloc_zero_array(mem_ctx, const char *, res->count + 1); ++ if (user_names == NULL) { ++ goto done; ++ } ++ ++ for (unsigned i = 0; i < res->count; i++) { ++ user_names[c] = ldb_msg_find_attr_as_string(res->msgs[i], ++ SYSDB_NAME, ++ NULL); ++ if (user_names[c] == NULL) { ++ continue; ++ } ++ c++; ++ } ++ ++done: ++ /* Don't free res and keep it around to avoid duplicating the names */ ++ return user_names; ++} ++ ++static errno_t delete_all_groups(struct sss_domain_info *dom) ++{ ++ TALLOC_CTX *tmp_ctx; ++ struct ldb_dn *base_dn; ++ errno_t ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); ++ return ENOMEM; ++ } ++ ++ base_dn = sysdb_group_base_dn(tmp_ctx, dom); ++ if (base_dn == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_delete_recursive(dom->sysdb, base_dn, true); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to delete groups subtree [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ ++static errno_t save_file_group(struct files_id_ctx *id_ctx, ++ struct group *grp, ++ const char **cached_users) ++{ ++ errno_t ret; ++ char *fqname; ++ struct sysdb_attrs *attrs = NULL; ++ TALLOC_CTX *tmp_ctx = NULL; ++ char **fq_gr_files_mem; ++ const char **fq_gr_mem; ++ unsigned mi = 0; ++ ++ if (strcmp(grp->gr_name, "root") == 0 ++ || grp->gr_gid == 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Skipping %s\n", grp->gr_name); ++ return EOK; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ fqname = sss_create_internal_fqname(tmp_ctx, grp->gr_name, ++ id_ctx->domain->name); ++ if (fqname == NULL) { ++ ret = ENOMEM; ++ goto done; ++ ++ } ++ ++ attrs = sysdb_new_attrs(tmp_ctx); ++ if (attrs == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ if (grp->gr_mem && grp->gr_mem[0]) { ++ fq_gr_files_mem = sss_create_internal_fqname_list( ++ tmp_ctx, ++ (const char *const*) grp->gr_mem, ++ id_ctx->domain->name); ++ if (fq_gr_files_mem == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ fq_gr_mem = talloc_zero_array(tmp_ctx, const char *, ++ talloc_array_length(fq_gr_files_mem)); ++ if (fq_gr_mem == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (unsigned i=0; fq_gr_files_mem[i] != NULL; i++) { ++ if (string_in_list(fq_gr_files_mem[i], ++ discard_const(cached_users), ++ true)) { ++ fq_gr_mem[mi] = fq_gr_files_mem[i]; ++ mi++; ++ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "User %s is cached, will become a member of %s\n", ++ fq_gr_files_mem[i], grp->gr_name); ++ } else { ++ ret = sysdb_attrs_add_string(attrs, ++ SYSDB_GHOST, ++ fq_gr_files_mem[i]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot add ghost %s for group %s\n", ++ fq_gr_files_mem[i], fqname); ++ continue; ++ } ++ ++ DEBUG(SSSDBG_TRACE_LIBS, ++ "User %s is not cached, will become a ghost of %s\n", ++ fq_gr_files_mem[i], grp->gr_name); ++ } ++ } ++ ++ if (fq_gr_mem != NULL && fq_gr_mem[0] != NULL) { ++ ret = sysdb_attrs_users_from_str_list( ++ attrs, SYSDB_MEMBER, id_ctx->domain->name, ++ (const char *const *) fq_gr_mem); ++ if (ret) { ++ DEBUG(SSSDBG_OP_FAILURE, "Could not add group members\n"); ++ goto done; ++ } ++ } ++ ++ } ++ ++ ret = sysdb_store_group(id_ctx->domain, fqname, grp->gr_gid, ++ attrs, 0, 0); ++ if (ret) { ++ DEBUG(SSSDBG_OP_FAILURE, "Could not add group to cache\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static errno_t sf_enum_groups(struct files_id_ctx *id_ctx) ++{ ++ errno_t ret; ++ errno_t tret; ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct group **groups = NULL; ++ bool in_transaction = false; ++ const char **cached_users = NULL; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = enum_files_groups(tmp_ctx, id_ctx, &groups); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ cached_users = get_cached_user_names(tmp_ctx, id_ctx->domain); ++ if (cached_users == NULL) { ++ goto done; ++ } ++ ++ ret = sysdb_transaction_start(id_ctx->domain->sysdb); ++ if (ret != EOK) { ++ goto done; ++ } ++ in_transaction = true; ++ ++ /* remove previous cache contents */ ++ ret = delete_all_groups(id_ctx->domain); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ for (size_t i = 0; groups[i]; i++) { ++ ret = save_file_group(id_ctx, groups[i], cached_users); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot save group %s\n", groups[i]->gr_name); ++ continue; ++ } ++ } ++ ++ ret = sysdb_transaction_commit(id_ctx->domain->sysdb); ++ if (ret != EOK) { ++ goto done; ++ } ++ in_transaction = false; ++ ++ ret = EOK; ++done: ++ if (in_transaction) { ++ tret = sysdb_transaction_cancel(id_ctx->domain->sysdb); ++ if (tret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot cancel transaction: %d\n", ret); ++ } ++ } ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static void sf_cb_done(struct files_id_ctx *id_ctx) ++{ ++ /* Only activate a domain when both callbacks are done */ ++ if (id_ctx->updating_passwd == false ++ && id_ctx->updating_groups == false) { ++ dp_sbus_domain_active(id_ctx->be->provider, ++ id_ctx->domain); ++ } ++} ++ ++static int sf_passwd_cb(const char *filename, uint32_t flags, void *pvt) ++{ ++ struct files_id_ctx *id_ctx; ++ errno_t ret; ++ ++ id_ctx = talloc_get_type(pvt, struct files_id_ctx); ++ if (id_ctx == NULL) { ++ return EINVAL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "passwd notification\n"); ++ ++ if (strcmp(filename, id_ctx->passwd_file) != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Wrong file, expected %s, got %s\n", ++ id_ctx->passwd_file, filename); ++ return EINVAL; ++ } ++ ++ id_ctx->updating_passwd = true; ++ dp_sbus_domain_inconsistent(id_ctx->be->provider, id_ctx->domain); ++ ++ dp_sbus_reset_users_ncache(id_ctx->be->provider, id_ctx->domain); ++ dp_sbus_reset_users_memcache(id_ctx->be->provider); ++ dp_sbus_reset_initgr_memcache(id_ctx->be->provider); ++ ++ ret = sf_enum_users(id_ctx); ++ ++ id_ctx->updating_passwd = false; ++ sf_cb_done(id_ctx); ++ files_account_info_finished(id_ctx, BE_REQ_USER, ret); ++ return ret; ++} ++ ++static int sf_group_cb(const char *filename, uint32_t flags, void *pvt) ++{ ++ struct files_id_ctx *id_ctx; ++ errno_t ret; ++ ++ id_ctx = talloc_get_type(pvt, struct files_id_ctx); ++ if (id_ctx == NULL) { ++ return EINVAL; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "group notification\n"); ++ ++ if (strcmp(filename, id_ctx->group_file) != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Wrong file, expected %s, got %s\n", ++ id_ctx->group_file, filename); ++ return EINVAL; ++ } ++ ++ id_ctx->updating_groups = true; ++ dp_sbus_domain_inconsistent(id_ctx->be->provider, id_ctx->domain); ++ ++ dp_sbus_reset_groups_ncache(id_ctx->be->provider, id_ctx->domain); ++ dp_sbus_reset_groups_memcache(id_ctx->be->provider); ++ dp_sbus_reset_initgr_memcache(id_ctx->be->provider); ++ ++ ret = sf_enum_groups(id_ctx); ++ ++ id_ctx->updating_groups = false; ++ sf_cb_done(id_ctx); ++ files_account_info_finished(id_ctx, BE_REQ_GROUP, ret); ++ return ret; ++} ++ ++static void startup_enum_files(struct tevent_context *ev, ++ struct tevent_immediate *imm, ++ void *pvt) ++{ ++ struct files_id_ctx *id_ctx = talloc_get_type(pvt, struct files_id_ctx); ++ errno_t ret; ++ ++ talloc_zfree(imm); ++ ++ ret = sf_enum_users(id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Enumerating users failed, data might be inconsistent!\n"); ++ } ++ ++ ret = sf_enum_groups(id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Enumerating groups failed, data might be inconsistent!\n"); ++ } ++} ++ ++static struct snotify_ctx *sf_setup_watch(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ const char *filename, ++ snotify_cb_fn fn, ++ struct files_id_ctx *id_ctx) ++{ ++ return snotify_create(mem_ctx, ev, SNOTIFY_WATCH_DIR, ++ filename, NULL, ++ IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MOVE_SELF | \ ++ IN_CREATE | IN_MOVED_TO, ++ fn, id_ctx); ++} ++ ++struct files_ctx *sf_init(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ const char *passwd_file, ++ const char *group_file, ++ struct files_id_ctx *id_ctx) ++{ ++ struct files_ctx *fctx; ++ struct tevent_immediate *imm; ++ ++ fctx = talloc(mem_ctx, struct files_ctx); ++ if (fctx == NULL) { ++ return NULL; ++ } ++ ++ fctx->pwd_watch = sf_setup_watch(fctx, ev, passwd_file, ++ sf_passwd_cb, id_ctx); ++ fctx->grp_watch = sf_setup_watch(fctx, ev, group_file, ++ sf_group_cb, id_ctx); ++ if (fctx->pwd_watch == NULL || fctx->grp_watch == NULL) { ++ talloc_free(fctx); ++ return NULL; ++ } ++ ++ /* Enumerate users and groups on startup to process any changes when ++ * sssd was down. We schedule a request here to minimize the time ++ * we spend in the init function ++ */ ++ imm = tevent_create_immediate(id_ctx); ++ if (imm == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n"); ++ talloc_free(fctx); ++ return NULL; ++ } ++ tevent_schedule_immediate(imm, ev, startup_enum_files, id_ctx); ++ ++ return fctx; ++} +diff --git a/src/providers/files/files_private.h b/src/providers/files/files_private.h +new file mode 100644 +index 0000000000000000000000000000000000000000..a7d195c9068d353db6996238ba53c86b625f2edb +--- /dev/null ++++ b/src/providers/files/files_private.h +@@ -0,0 +1,74 @@ ++/* ++ SSSD ++ ++ Files provider declarations ++ ++ Copyright (C) 2016 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 __FILES_PRIVATE_H_ ++#define __FILES_PRIVATE_H_ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "providers/data_provider/dp.h" ++ ++struct files_id_ctx { ++ struct be_ctx *be; ++ struct sss_domain_info *domain; ++ struct files_ctx *fctx; ++ ++ const char *passwd_file; ++ const char *group_file; ++ ++ bool updating_passwd; ++ bool updating_groups; ++ ++ struct tevent_req *users_req; ++ struct tevent_req *groups_req; ++ struct tevent_req *initgroups_req; ++}; ++ ++/* files_ops.c */ ++struct files_ctx *sf_init(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ const char *passwd_file, ++ const char *group_file, ++ struct files_id_ctx *id_ctx); ++ ++/* files_id.c */ ++struct tevent_req * ++files_account_info_handler_send(TALLOC_CTX *mem_ctx, ++ struct files_id_ctx *id_ctx, ++ struct dp_id_data *data, ++ struct dp_req_params *params); ++ ++errno_t files_account_info_handler_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct dp_reply_std *data); ++ ++void files_account_info_finished(struct files_id_ctx *id_ctx, ++ int req_type, ++ errno_t ret); ++#endif /* __FILES_PRIVATE_H_ */ +diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c +index d11ae965162209b740e1a8d9c91816e122166020..419857cc739d197493e46629d00aa5fb6cfde824 100644 +--- a/src/tests/dlopen-tests.c ++++ b/src/tests/dlopen-tests.c +@@ -80,6 +80,8 @@ struct so { + { "libsss_util.so", { LIBPFX"libsss_util.so", NULL } }, + { "libsss_simple.so", { LIBPFX"libdlopen_test_providers.so", + LIBPFX"libsss_simple.so", NULL } }, ++ { "libsss_files.so", { LIBPFX"libdlopen_test_providers.so", ++ LIBPFX"libsss_files.so", NULL } }, + #ifdef BUILD_SAMBA + { "libsss_ad.so", { LIBPFX"libdlopen_test_providers.so", + LIBPFX"libsss_ad.so", NULL } }, +-- +2.9.3 + diff --git a/0053-CONFDB-The-files-provider-always-enumerates.patch b/0053-CONFDB-The-files-provider-always-enumerates.patch new file mode 100644 index 0000000..e79898a --- /dev/null +++ b/0053-CONFDB-The-files-provider-always-enumerates.patch @@ -0,0 +1,48 @@ +From a60e6ec802cd2858dc85eabd442cff16fb23618f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Thu, 17 Nov 2016 15:42:03 +0100 +Subject: [PATCH 53/79] CONFDB: The files provider always enumerates + +Since the files provider always mirrors the whole passwd and group +contents, the files domain should always permit its contents to be +enumerated. + +Reviewed-by: Jakub Hrozek +--- + src/confdb/confdb.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index b0a75867c61fa3af21990d92419f17baa34864ae..e52b96c8a854c0706515395dd86a61211d6ac2a6 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -828,6 +828,7 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + char *default_domain; + bool fqnames_default = false; + int memcache_timeout; ++ bool enum_default; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) return ENOMEM; +@@ -954,14 +955,17 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + "Interpreting as true\n", domain->name); + domain->enumerate = true; + } else { /* assume the new format */ ++ enum_default = strcasecmp(domain->provider, "files") == 0 ? true : false; ++ + ret = get_entry_as_bool(res->msgs[0], &domain->enumerate, +- CONFDB_DOMAIN_ENUMERATE, 0); ++ CONFDB_DOMAIN_ENUMERATE, enum_default); + if(ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Invalid value for %s\n", CONFDB_DOMAIN_ENUMERATE); + goto done; + } + } ++ + if (!domain->enumerate) { + DEBUG(SSSDBG_TRACE_FUNC, "No enumeration for [%s]!\n", domain->name); + } +-- +2.9.3 + diff --git a/0054-CONFDB-Make-pwfield-configurable-per-domain.patch b/0054-CONFDB-Make-pwfield-configurable-per-domain.patch new file mode 100644 index 0000000..dfe66bb --- /dev/null +++ b/0054-CONFDB-Make-pwfield-configurable-per-domain.patch @@ -0,0 +1,147 @@ +From c778c36c5170c2b9f1cf7a6e3b0811124534df03 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 21 Nov 2016 12:10:46 +0100 +Subject: [PATCH 54/79] CONFDB: Make pwfield configurable per-domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previously, the pwfield option was only configurable at the NSS level. +Because it's important for the files provider to report "x" as the +pwfield instead of "*" which is the SSSD default, this commit makes the +pwfield configurable at the domain level. + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.c | 10 ++++++++++ + src/confdb/confdb.h | 1 + + src/responder/nss/nss_private.h | 4 ++++ + src/responder/nss/nss_protocol_grent.c | 6 +++--- + src/responder/nss/nss_protocol_pwent.c | 6 +++--- + src/responder/nss/nss_utils.c | 12 ++++++++++++ + 6 files changed, 33 insertions(+), 6 deletions(-) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index e52b96c8a854c0706515395dd86a61211d6ac2a6..5112c6d568cbe2d7374b19d4a0850172d6f3f400 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -1325,6 +1325,16 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + } + + tmp = ldb_msg_find_attr_as_string(res->msgs[0], ++ CONFDB_NSS_PWFIELD, NULL); ++ if (tmp != NULL) { ++ domain->pwfield = talloc_strdup(domain, tmp); ++ if (!domain->pwfield) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ tmp = ldb_msg_find_attr_as_string(res->msgs[0], + CONFDB_SUBDOMAIN_ENUMERATE, + CONFDB_DEFAULT_SUBDOMAIN_ENUMERATE); + if (tmp != NULL) { +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 7c944698157619652441fb0722a4363053d6a8f3..353dfd0a9afbcaba49fcfdc7930026bb2eebfc9e 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -270,6 +270,7 @@ struct sss_domain_info { + bool ignore_group_members; + uint32_t id_min; + uint32_t id_max; ++ const char *pwfield; + + bool cache_credentials; + uint32_t cache_credentials_min_ff_length; +diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h +index e63fbabc8cdfd97742fb517aa60292d4fad4a0ed..acb3c4aa504e538ca56dca8d43ee04b0f60954a9 100644 +--- a/src/responder/nss/nss_private.h ++++ b/src/responder/nss/nss_private.h +@@ -151,4 +151,8 @@ int sized_member_name(TALLOC_CTX *mem_ctx, + const char *member_name, + struct sized_string **_name); + ++const char * ++nss_get_pwfield(struct nss_ctx *nctx, ++ struct sss_domain_info *dom); ++ + #endif /* _NSS_PRIVATE_H_ */ +diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c +index 7409e0458c0e7c886ac6d523d10d5c90a2038ea9..283ab9f6731bc4c8261ca79075ab030005bf70db 100644 +--- a/src/responder/nss/nss_protocol_grent.c ++++ b/src/responder/nss/nss_protocol_grent.c +@@ -219,9 +219,6 @@ nss_protocol_fill_grent(struct nss_ctx *nss_ctx, + return ENOMEM; + } + +- /* Password field content. */ +- to_sized_string(&pwfield, nss_ctx->pwfield); +- + /* First two fields (length and reserved), filled up later. */ + ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)); + if (ret != EOK) { +@@ -235,6 +232,9 @@ nss_protocol_fill_grent(struct nss_ctx *nss_ctx, + talloc_free_children(tmp_ctx); + msg = result->msgs[i]; + ++ /* Password field content. */ ++ to_sized_string(&pwfield, nss_get_pwfield(nss_ctx, result->domain)); ++ + ret = nss_get_grent(tmp_ctx, nss_ctx, result->domain, msg, + &gid, &name); + if (ret != EOK) { +diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c +index 783b06a32f0ff898021ea763d3d3fa0fabf43b25..edda9d3c87389898435a34fe7927868bc1cd9ac5 100644 +--- a/src/responder/nss/nss_protocol_pwent.c ++++ b/src/responder/nss/nss_protocol_pwent.c +@@ -287,9 +287,6 @@ nss_protocol_fill_pwent(struct nss_ctx *nss_ctx, + return ENOMEM; + } + +- /* Password field content. */ +- to_sized_string(&pwfield, nss_ctx->pwfield); +- + /* First two fields (length and reserved), filled up later. */ + ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)); + if (ret != EOK) { +@@ -303,6 +300,9 @@ nss_protocol_fill_pwent(struct nss_ctx *nss_ctx, + talloc_free_children(tmp_ctx); + msg = result->msgs[i]; + ++ /* Password field content. */ ++ to_sized_string(&pwfield, nss_get_pwfield(nss_ctx, result->domain)); ++ + ret = nss_get_pwent(tmp_ctx, nss_ctx, result->domain, msg, &uid, &gid, + &name, &gecos, &homedir, &shell); + if (ret != EOK) { +diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c +index 41081c914e3f4dad31a7b8bf7614a5fd2eb61d7a..f839930a275db56e8d729888af870562d7b6f260 100644 +--- a/src/responder/nss/nss_utils.c ++++ b/src/responder/nss/nss_utils.c +@@ -24,6 +24,7 @@ + #include "util/util.h" + #include "confdb/confdb.h" + #include "responder/common/responder.h" ++#include "responder/nss/nss_private.h" + + const char * + nss_get_name_from_msg(struct sss_domain_info *domain, +@@ -138,3 +139,14 @@ done: + talloc_free(tmp_ctx); + return ret; + } ++ ++const char * ++nss_get_pwfield(struct nss_ctx *nctx, ++ struct sss_domain_info *dom) ++{ ++ if (dom->pwfield != NULL) { ++ return dom->pwfield; ++ } ++ ++ return nctx->pwfield; ++} +-- +2.9.3 + diff --git a/0055-CONFDB-The-files-domain-defaults-to-x-as-pwfield.patch b/0055-CONFDB-The-files-domain-defaults-to-x-as-pwfield.patch new file mode 100644 index 0000000..b07b3f0 --- /dev/null +++ b/0055-CONFDB-The-files-domain-defaults-to-x-as-pwfield.patch @@ -0,0 +1,38 @@ +From ece2ac6889da2b58f5d4027ec0a1d97992056f66 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 21 Nov 2016 12:11:39 +0100 +Subject: [PATCH 55/79] CONFDB: The files domain defaults to "x" as pwfield +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In order to make it possible for files provider users to authenticate +with pam_unix, default to "x" as the pwfield of users from the files +domain. + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index 5112c6d568cbe2d7374b19d4a0850172d6f3f400..c7afd683d7f21b513bb491adbf7f7bbe79786212 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -966,6 +966,13 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + } + } + ++ if (strcasecmp(domain->provider, "files") == 0) { ++ /* The password field must be reported as 'x', else pam_unix won't ++ * authenticate this entry. See man pwconv(8) for more details. ++ */ ++ domain->pwfield = "x"; ++ } ++ + if (!domain->enumerate) { + DEBUG(SSSDBG_TRACE_FUNC, "No enumeration for [%s]!\n", domain->name); + } +-- +2.9.3 + diff --git a/0056-MAN-Document-the-pwfield-configuration-option.patch b/0056-MAN-Document-the-pwfield-configuration-option.patch new file mode 100644 index 0000000..05cb893 --- /dev/null +++ b/0056-MAN-Document-the-pwfield-configuration-option.patch @@ -0,0 +1,47 @@ +From 26577ac05dc33c201c15164bcf19f45393beeca5 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 21 Nov 2016 13:12:11 +0100 +Subject: [PATCH 56/79] MAN: Document the pwfield configuration option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The pwfield was not documented at all previously. In addition, document +the different defaults for remote provider and the file provider. + +Reviewed-by: Pavel Březina +--- + src/man/sssd.conf.5.xml | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index cb608cb0ac06185118e3c9f01d785b83236f8abb..782aef7bf62d9bcef3c3dab534a25e01d85c1764 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -936,6 +936,23 @@ fallback_homedir = /home/%u + + + ++ ++ pwfield (string) ++ ++ ++ The value that NSS operations that return ++ users or groups will return for the ++ password field. ++ ++ ++ This option can also be set per-domain. ++ ++ ++ Default: * (remote domains) ++ or x (the files domain) ++ ++ ++ + + + +-- +2.9.3 + diff --git a/0057-TESTS-move-helper-fixtures-to-back-up-and-restore-a-.patch b/0057-TESTS-move-helper-fixtures-to-back-up-and-restore-a-.patch new file mode 100644 index 0000000..756404c --- /dev/null +++ b/0057-TESTS-move-helper-fixtures-to-back-up-and-restore-a-.patch @@ -0,0 +1,95 @@ +From 4e17c050dac8f2c6e2d278c4c4a27001c8d7d164 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 17 Nov 2016 15:43:54 +0100 +Subject: [PATCH 57/79] TESTS: move helper fixtures to back up and restore a + file to a utility module +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The fixtures will be useful for tests that set up and restore a user and +group database. While it would be possible to import them already, the +functions were previously used in a test and importing from a test seems +a bit like a hack. + +Reviewed-by: Pavel Březina +Reviewed-by: Lukáš Slebodník +--- + src/tests/intg/Makefile.am | 1 + + src/tests/intg/ent_test.py | 14 -------------- + src/tests/intg/util.py | 14 ++++++++++++++ + 3 files changed, 15 insertions(+), 14 deletions(-) + +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index e81e5ee950d1a1e50ff478a34ae29853a9783c8b..90e0f308043aa89da5afc0fd353f7d373ac6425f 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -1,5 +1,6 @@ + dist_noinst_DATA = \ + config.py.m4 \ ++ util.py \ + sssd_id.py \ + sssd_ldb.py \ + sssd_netgroup.py \ +diff --git a/src/tests/intg/ent_test.py b/src/tests/intg/ent_test.py +index 598930324a41ad9ff473e94e7f2deb302e1bc942..6b240ae9682331be7f4485a1ac81fad4cebde5e6 100644 +--- a/src/tests/intg/ent_test.py ++++ b/src/tests/intg/ent_test.py +@@ -19,25 +19,11 @@ + import re + import os + import io +-import shutil + import pytest + import ent + from util import * + + +-def backup_envvar_file(name): +- path = os.environ[name] +- backup_path = path + ".bak" +- shutil.copyfile(path, backup_path) +- return path +- +- +-def restore_envvar_file(name): +- path = os.environ[name] +- backup_path = path + ".bak" +- os.rename(backup_path, path) +- +- + @pytest.fixture(scope="module") + def passwd_path(request): + name = "NSS_WRAPPER_PASSWD" +diff --git a/src/tests/intg/util.py b/src/tests/intg/util.py +index 66ec0baa1aea4f8f9a3210d33640f087c80c6862..2b40311bd1560e05f3aafb34136a1b0efc8a1b49 100644 +--- a/src/tests/intg/util.py ++++ b/src/tests/intg/util.py +@@ -21,6 +21,7 @@ import re + import os + import subprocess + import config ++import shutil + + UNINDENT_RE = re.compile("^ +", re.MULTILINE) + +@@ -64,3 +65,16 @@ def first_dir(*args): + for arg in args: + if os.path.isdir(arg): + return arg ++ ++ ++def backup_envvar_file(name): ++ path = os.environ[name] ++ backup_path = path + ".bak" ++ shutil.copyfile(path, backup_path) ++ return path ++ ++ ++def restore_envvar_file(name): ++ path = os.environ[name] ++ backup_path = path + ".bak" ++ os.rename(backup_path, path) +-- +2.9.3 + diff --git a/0058-TESTS-add-a-helper-module-with-shared-NSS-constants.patch b/0058-TESTS-add-a-helper-module-with-shared-NSS-constants.patch new file mode 100644 index 0000000..ee9e7ff --- /dev/null +++ b/0058-TESTS-add-a-helper-module-with-shared-NSS-constants.patch @@ -0,0 +1,186 @@ +From 1921d739ff7b028baa591272cc8969e330c8f872 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 17 Nov 2016 15:46:00 +0100 +Subject: [PATCH 58/79] TESTS: add a helper module with shared NSS constants +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Every module that reads the sssd_nss module directly copied around the +same definition of NSS constants. This commit moves them into a single +file to avoid code duplication. + +Reviewed-by: Pavel Březina +Reviewed-by: Lukáš Slebodník +--- + src/tests/intg/Makefile.am | 1 + + src/tests/intg/sssd_id.py | 14 ++----------- + src/tests/intg/sssd_netgroup.py | 25 ++++------------------ + src/tests/intg/sssd_nss.py | 46 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 53 insertions(+), 33 deletions(-) + create mode 100644 src/tests/intg/sssd_nss.py + +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 90e0f308043aa89da5afc0fd353f7d373ac6425f..4d364f84a39cd75507c29e8f70d8d8a4881a8063 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -1,6 +1,7 @@ + dist_noinst_DATA = \ + config.py.m4 \ + util.py \ ++ sssd_nss.py \ + sssd_id.py \ + sssd_ldb.py \ + sssd_netgroup.py \ +diff --git a/src/tests/intg/sssd_id.py b/src/tests/intg/sssd_id.py +index 4f020c3a229c1288935ce120cf1118735e2ae0eb..f4ab9cfb8c0cd3fcf3a0195fd62696ae7d892d61 100644 +--- a/src/tests/intg/sssd_id.py ++++ b/src/tests/intg/sssd_id.py +@@ -21,15 +21,7 @@ import pwd + import grp + from ctypes import (cdll, c_int, c_char, c_uint32, c_long, c_char_p, + POINTER, pointer) +- +- +-class NssReturnCode(object): +- """ 'enum' class for name service switch return code """ +- TRYAGAIN = -2, +- UNAVAIL = -1 +- NOTFOUND = 0 +- SUCCESS = 1 +- RETURN = 2 ++from sssd_nss import NssReturnCode, nss_sss_ctypes_loader + + + def call_sssd_initgroups(user, gid): +@@ -45,10 +37,8 @@ def call_sssd_initgroups(user, gid): + gids should contain user group IDs if err is NssReturnCode.SUCCESS + otherwise errno will contain non-zero value. + """ +- libnss_sss_path = config.NSS_MODULE_DIR + "/libnss_sss.so.2" +- libnss_sss = cdll.LoadLibrary(libnss_sss_path) ++ func = nss_sss_ctypes_loader('_nss_sss_initgroups_dyn') + +- func = libnss_sss._nss_sss_initgroups_dyn + func.restype = c_int + func.argtypes = [POINTER(c_char), c_uint32, POINTER(c_long), + POINTER(c_long), POINTER(POINTER(c_uint32)), c_long, +diff --git a/src/tests/intg/sssd_netgroup.py b/src/tests/intg/sssd_netgroup.py +index 0ea3db3cf69be04a1eda6afb455e4d0a3b301f1b..162c5aac86beb2b3625ccb695da7033e6483341e 100644 +--- a/src/tests/intg/sssd_netgroup.py ++++ b/src/tests/intg/sssd_netgroup.py +@@ -19,6 +19,7 @@ + from ctypes import (cdll, c_int, c_char, c_char_p, c_size_t, c_void_p, c_ulong, + POINTER, Structure, Union, create_string_buffer, get_errno) + import config ++from sssd_nss import NssReturnCode, nss_sss_ctypes_loader + + + class NetgroupType(object): +@@ -50,15 +51,6 @@ NameList._fields_ = [("next", POINTER(NameList)), + ("name", POINTER(c_char))] + + +-class NssReturnCode(object): +- """ 'enum' class for name service switch return code """ +- TRYAGAIN = -2, +- UNAVAIL = -1 +- NOTFOUND = 0 +- SUCCESS = 1 +- RETURN = 2 +- +- + class Netgrent(Structure): + _fields_ = [("type", c_int), + ("val", Val), +@@ -92,10 +84,7 @@ class NetgroupRetriever(object): + result_p will contain POINTER(Netgrent) which can be used in + _getnetgrent_r or _getnetgrent_r. + """ +- libnss_sss_path = config.NSS_MODULE_DIR + "/libnss_sss.so.2" +- libnss_sss = cdll.LoadLibrary(libnss_sss_path) +- +- func = libnss_sss._nss_sss_setnetgrent ++ func = nss_sss_ctypes_loader('_nss_sss_setnetgrent') + func.restype = c_int + func.argtypes = [c_char_p, POINTER(Netgrent)] + +@@ -123,10 +112,7 @@ class NetgroupRetriever(object): + if err is NssReturnCode.SUCCESS netgroups will contain list of + touples. Each touple will consist of 3 elemets either string or + """ +- libnss_sss_path = config.NSS_MODULE_DIR + "/libnss_sss.so.2" +- libnss_sss = cdll.LoadLibrary(libnss_sss_path) +- +- func = libnss_sss._nss_sss_getnetgrent_r ++ func = nss_sss_ctypes_loader('_nss_sss_getnetgrent_r') + func.restype = c_int + func.argtypes = [POINTER(Netgrent), POINTER(c_char), c_size_t, + POINTER(c_int)] +@@ -148,10 +134,7 @@ class NetgroupRetriever(object): + + @return int a constant from class NssReturnCode + """ +- libnss_sss_path = config.NSS_MODULE_DIR + "/libnss_sss.so.2" +- libnss_sss = cdll.LoadLibrary(libnss_sss_path) +- +- func = libnss_sss._nss_sss_endnetgrent ++ func = nss_sss_ctypes_loader('_nss_sss_endnetgrent') + func.restype = c_int + func.argtypes = [POINTER(Netgrent)] + +diff --git a/src/tests/intg/sssd_nss.py b/src/tests/intg/sssd_nss.py +new file mode 100644 +index 0000000000000000000000000000000000000000..1e846310bca4dc314c47882cc5e7877002c3d78f +--- /dev/null ++++ b/src/tests/intg/sssd_nss.py +@@ -0,0 +1,46 @@ ++# ++# Shared module for integration tests that need to access the sssd_nss ++# module directly ++# ++# 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 config ++import ctypes ++ ++ ++class NssReturnCode(object): ++ """ 'enum' class for name service switch return code """ ++ TRYAGAIN = -2, ++ UNAVAIL = -1 ++ NOTFOUND = 0 ++ SUCCESS = 1 ++ RETURN = 2 ++ ++ ++class SssdNssError(Exception): ++ """ Raised when one of the NSS operations fail """ ++ def __init__(self, errno, nssop): ++ self.errno = errno ++ self.nssop = nssop ++ ++ def __str__(self): ++ return "NSS operation %s failed %d" % (nssop, errno) ++ ++ ++def nss_sss_ctypes_loader(func_name): ++ libnss_sss_path = config.NSS_MODULE_DIR + "/libnss_sss.so.2" ++ libnss_sss = ctypes.cdll.LoadLibrary(libnss_sss_path) ++ func = getattr(libnss_sss, func_name) ++ return func +-- +2.9.3 + diff --git a/0059-TESTS-Add-a-module-to-call-nss_sss-s-getpw-from-test.patch b/0059-TESTS-Add-a-module-to-call-nss_sss-s-getpw-from-test.patch new file mode 100644 index 0000000..3cff808 --- /dev/null +++ b/0059-TESTS-Add-a-module-to-call-nss_sss-s-getpw-from-test.patch @@ -0,0 +1,207 @@ +From 8578fba1500d43ad9632784462c255bf8bb360fe Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 25 Oct 2016 16:26:24 +0200 +Subject: [PATCH 59/79] TESTS: Add a module to call nss_sss's getpw* from tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implements a python module that allows to load the nss_sss module and +simulate calling getpw* functions from tests. + +Reviewed-by: Pavel Březina +Reviewed-by: Lukáš Slebodník +--- + src/tests/intg/Makefile.am | 1 + + src/tests/intg/sssd_passwd.py | 167 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 168 insertions(+) + create mode 100644 src/tests/intg/sssd_passwd.py + +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 4d364f84a39cd75507c29e8f70d8d8a4881a8063..6cd23967fd02261d516a749da0ef3d66432d0701 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -5,6 +5,7 @@ dist_noinst_DATA = \ + sssd_id.py \ + sssd_ldb.py \ + sssd_netgroup.py \ ++ sssd_passwd.py \ + ds.py \ + ds_openldap.py \ + ent.py \ +diff --git a/src/tests/intg/sssd_passwd.py b/src/tests/intg/sssd_passwd.py +new file mode 100644 +index 0000000000000000000000000000000000000000..8b741ea8c27efe2081ee200e6af02c322c5d53bc +--- /dev/null ++++ b/src/tests/intg/sssd_passwd.py +@@ -0,0 +1,167 @@ ++# ++# Module for simulation of utility "getent passwd -s sss" from coreutils ++# ++# 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 . ++# ++ ++from ctypes import (c_int, c_char_p, c_ulong, POINTER, ++ Structure, create_string_buffer, get_errno) ++from sssd_nss import NssReturnCode, SssdNssError, nss_sss_ctypes_loader ++ ++PASSWD_BUFLEN = 1024 ++ ++ ++class Passwd(Structure): ++ _fields_ = [("pw_name", c_char_p), ++ ("pw_passwd", c_char_p), ++ ("pw_uid", c_int), ++ ("pw_gid", c_int), ++ ("pw_gecos", c_char_p), ++ ("pw_dir", c_char_p), ++ ("pw_shell", c_char_p)] ++ ++ ++def set_user_dict(res, result_p): ++ if res != NssReturnCode.SUCCESS: ++ return dict() ++ ++ user_dict = dict() ++ user_dict['name'] = result_p[0].pw_name ++ user_dict['passwd'] = result_p[0].pw_passwd ++ user_dict['uid'] = result_p[0].pw_uid ++ user_dict['gid'] = result_p[0].pw_gid ++ user_dict['gecos'] = result_p[0].pw_gecos ++ user_dict['dir'] = result_p[0].pw_dir ++ user_dict['shell'] = result_p[0].pw_shell ++ return user_dict ++ ++ ++def getpwnam_r(name, result_p, buffer_p, buflen): ++ """ ++ ctypes wrapper for: ++ enum nss_status _nss_sss_getpwnam_r(const char *name, ++ struct passwd *result, ++ char *buffer, ++ size_t buflen, ++ int *errnop) ++ """ ++ func = nss_sss_ctypes_loader("_nss_sss_getpwnam_r") ++ func.restype = c_int ++ func.argtypes = [c_char_p, POINTER(Passwd), ++ c_char_p, c_ulong, POINTER(c_int)] ++ ++ errno = POINTER(c_int)(c_int(0)) ++ ++ res = func(c_char_p(name), result_p, buffer_p, buflen, errno) ++ ++ return (int(res), int(errno[0]), result_p) ++ ++ ++def setpwent(): ++ """ ++ ctypes wrapper for: ++ void setpwent(void) ++ """ ++ func = nss_sss_ctypes_loader("_nss_sss_setpwent") ++ func.argtypes = [] ++ ++ res = func() ++ assert res == NssReturnCode.SUCCESS ++ ++ errno = get_errno() ++ if errno != 0: ++ raise SssdNssError(errno, "setpwent") ++ ++ ++def endpwent(): ++ """ ++ ctypes wrapper for: ++ void endpwent(void) ++ """ ++ func = nss_sss_ctypes_loader("_nss_sss_endpwent") ++ func.argtypes = [] ++ ++ res = func() ++ assert res == NssReturnCode.SUCCESS ++ ++ errno = get_errno() ++ if errno != 0: ++ raise SssdNssError(errno, "endpwent") ++ ++ ++def getpwent_r(result_p, buffer_p, buflen): ++ """ ++ ctypes wrapper for: ++ enum nss_status _nss_sss_getpwent_r(struct passwd *result, ++ char *buffer, size_t buflen, ++ int *errnop) ++ """ ++ func = nss_sss_ctypes_loader("_nss_sss_getpwent_r") ++ func.restype = c_int ++ func.argtypes = [POINTER(Passwd), c_char_p, c_ulong, POINTER(c_int)] ++ ++ errno = POINTER(c_int)(c_int(0)) ++ ++ res = func(result_p, buffer_p, buflen, errno) ++ return (int(res), int(errno[0]), result_p) ++ ++ ++def getpwent(): ++ result = Passwd() ++ result_p = POINTER(Passwd)(result) ++ buff = create_string_buffer(PASSWD_BUFLEN) ++ ++ res, errno, result_p = getpwent_r(result_p, buff, PASSWD_BUFLEN) ++ if errno != 0: ++ raise SssdNssError(errno, "getpwent_r") ++ ++ user_dict = set_user_dict(res, result_p) ++ return res, user_dict ++ ++ ++def call_sssd_getpwnam(name): ++ """ ++ A Python wrapper to retrieve a user. Returns: ++ (res, user_dict) ++ if res is NssReturnCode.SUCCESS, then user_dict contains the keys ++ corresponding to the C passwd structure fields. Otherwise, the dictionary ++ is empty and errno indicates the error code ++ """ ++ result = Passwd() ++ result_p = POINTER(Passwd)(result) ++ buff = create_string_buffer(PASSWD_BUFLEN) ++ ++ res, errno, result_p = getpwnam_r(name, result_p, buff, PASSWD_BUFLEN) ++ if errno != 0: ++ raise SssdNssError(errno, "getpwnam_r") ++ ++ user_dict = set_user_dict(res, result_p) ++ return res, user_dict ++ ++ ++def call_sssd_enumeration(): ++ """ ++ enumerate users from sssd module only ++ """ ++ setpwent() ++ user_list = [] ++ ++ res, user = getpwent() ++ while res == NssReturnCode.SUCCESS: ++ user_list.append(user) ++ res, user = getpwent() ++ ++ endpwent() ++ return user_list +-- +2.9.3 + diff --git a/0060-TESTS-Add-a-module-to-call-nss_sss-s-getgr-from-test.patch b/0060-TESTS-Add-a-module-to-call-nss_sss-s-getgr-from-test.patch new file mode 100644 index 0000000..d0ed833 --- /dev/null +++ b/0060-TESTS-Add-a-module-to-call-nss_sss-s-getgr-from-test.patch @@ -0,0 +1,128 @@ +From 3728db53ac32da51fcaae96b132e8e56ebbaebfa Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 16 Nov 2016 17:02:43 +0100 +Subject: [PATCH 60/79] TESTS: Add a module to call nss_sss's getgr* from tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implements a python module that allows to load the nss_sss module and +call functions that act like getgr* + +Reviewed-by: Pavel Březina +Reviewed-by: Lukáš Slebodník +--- + src/tests/intg/Makefile.am | 1 + + src/tests/intg/sssd_group.py | 88 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+) + create mode 100644 src/tests/intg/sssd_group.py + +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 6cd23967fd02261d516a749da0ef3d66432d0701..03245b35d37e81f44fcc708de16541bd52ceb854 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -6,6 +6,7 @@ dist_noinst_DATA = \ + sssd_ldb.py \ + sssd_netgroup.py \ + sssd_passwd.py \ ++ sssd_group.py \ + ds.py \ + ds_openldap.py \ + ent.py \ +diff --git a/src/tests/intg/sssd_group.py b/src/tests/intg/sssd_group.py +new file mode 100644 +index 0000000000000000000000000000000000000000..a9cfb32d59a5406fb1ad738d64c7487404f39a2d +--- /dev/null ++++ b/src/tests/intg/sssd_group.py +@@ -0,0 +1,88 @@ ++# ++# Module for simulation of utility "getent group -s sss" from coreutils ++# ++# 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 . ++# ++ ++from ctypes import (c_int, c_char_p, c_ulong, POINTER, Structure, ++ create_string_buffer) ++from sssd_nss import NssReturnCode, SssdNssError, nss_sss_ctypes_loader ++ ++GROUP_BUFLEN = 1024 ++ ++ ++class Group(Structure): ++ _fields_ = [("gr_name", c_char_p), ++ ("gr_passwd", c_char_p), ++ ("gr_gid", c_int), ++ ("gr_mem", POINTER(c_char_p))] ++ ++ ++def getgrnam_r(name, result_p, buffer_p, buflen): ++ """ ++ ctypes wrapper for: ++ enum nss_status _nss_sss_getgrnam_r(const char *name, ++ struct group *result, ++ char *buffer, ++ size_t buflen, ++ int *errnop) ++ """ ++ func = nss_sss_ctypes_loader("_nss_sss_getgrnam_r") ++ func.restype = c_int ++ func.argtypes = [c_char_p, POINTER(Group), ++ c_char_p, c_ulong, POINTER(c_int)] ++ ++ errno = POINTER(c_int)(c_int(0)) ++ ++ res = func(c_char_p(name), result_p, buffer_p, buflen, errno) ++ ++ return (int(res), int(errno[0]), result_p) ++ ++ ++def set_group_dict(res, result_p): ++ if res != NssReturnCode.SUCCESS: ++ return dict() ++ ++ group_dict = dict() ++ group_dict['name'] = result_p[0].gr_name ++ group_dict['gid'] = result_p[0].gr_gid ++ group_dict['mem'] = list() ++ ++ i = 0 ++ while result_p[0].gr_mem[i] != None: ++ group_dict['mem'].append(result_p[0].gr_mem[i]) ++ i = i+1 ++ ++ return group_dict ++ ++ ++def call_sssd_getgrnam(name): ++ """ ++ A Python wrapper to retrieve a group. Returns: ++ (res, group_dict) ++ if res is NssReturnCode.SUCCESS, then group_dict contains the keys ++ corresponding to the C passwd structure fields. Otherwise, the dictionary ++ is empty and errno indicates the error code ++ """ ++ result = Group() ++ result_p = POINTER(Group)(result) ++ buff = create_string_buffer(GROUP_BUFLEN) ++ ++ res, errno, result_p = getgrnam_r(name, result_p, buff, GROUP_BUFLEN) ++ if errno != 0: ++ raise SssdNssError(errno, "getgrnam_r") ++ ++ group_dict = set_group_dict(res, result_p) ++ return res, group_dict +-- +2.9.3 + diff --git a/0061-TESTS-Add-files-provider-integration-tests.patch b/0061-TESTS-Add-files-provider-integration-tests.patch new file mode 100644 index 0000000..57da9ce --- /dev/null +++ b/0061-TESTS-Add-files-provider-integration-tests.patch @@ -0,0 +1,1002 @@ +From 8bdb8c0970dc9acb5b0a54dab0bae306ca964944 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 25 Oct 2016 16:16:01 +0200 +Subject: [PATCH 61/79] TESTS: Add files provider integration tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implements integration tests for the files provider. In order to change +entries in the nss-wrapped passwd and group files, this commit also +implements a helper module that creates a new passwd and group file and +moves it in place of the nss-wrapped files. We move the files instead of +modifying them in-place in order to trigger similar inotify +notifications as shadow-utils would. + +The unit test uses sleep on several places. This is suboptimal, but +during testing especially on slow machines, it became apparent that +sometimes the inotify message arrives later than the test would check +for the changed entries. Therefore, the check would query the NSS +responder even before the sss-files domain was invalidated. + +Reviewed-by: Pavel Březina +Reviewed-by: Lukáš Slebodník +--- + src/tests/intg/Makefile.am | 3 + + src/tests/intg/files_ops.py | 158 ++++++++ + src/tests/intg/test_files_ops.py | 84 +++++ + src/tests/intg/test_files_provider.py | 692 ++++++++++++++++++++++++++++++++++ + 4 files changed, 937 insertions(+) + create mode 100644 src/tests/intg/files_ops.py + create mode 100644 src/tests/intg/test_files_ops.py + create mode 100644 src/tests/intg/test_files_provider.py + +diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am +index 03245b35d37e81f44fcc708de16541bd52ceb854..64f90b49064d9f4ed8b8a745d7255c9db5733a8e 100644 +--- a/src/tests/intg/Makefile.am ++++ b/src/tests/intg/Makefile.am +@@ -23,6 +23,9 @@ dist_noinst_DATA = \ + secrets.py \ + test_secrets.py \ + test_sssctl.py \ ++ files_ops.py \ ++ test_files_ops.py \ ++ test_files_provider.py \ + $(NULL) + + config.py: config.py.m4 +diff --git a/src/tests/intg/files_ops.py b/src/tests/intg/files_ops.py +new file mode 100644 +index 0000000000000000000000000000000000000000..65b3e5ee401e453c51dcd55433be8d9fa3ce6c92 +--- /dev/null ++++ b/src/tests/intg/files_ops.py +@@ -0,0 +1,158 @@ ++# ++# SSSD integration test - operations on UNIX user and group database ++# ++# 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 tempfile ++import pytest ++ ++import ent ++from util import backup_envvar_file, restore_envvar_file ++ ++ ++@pytest.fixture ++def passwd_ops_setup(request): ++ pwd_file = os.environ["NSS_WRAPPER_PASSWD"] ++ backup_envvar_file("NSS_WRAPPER_PASSWD") ++ request.addfinalizer(lambda: restore_envvar_file("NSS_WRAPPER_PASSWD")) ++ pwd_ops = PasswdOps(pwd_file) ++ return pwd_ops ++ ++ ++@pytest.fixture ++def group_ops_setup(request): ++ grp_file = os.environ["NSS_WRAPPER_GROUP"] ++ backup_envvar_file("NSS_WRAPPER_GROUP") ++ request.addfinalizer(lambda: restore_envvar_file("NSS_WRAPPER_GROUP")) ++ grp_ops = GroupOps(grp_file) ++ return grp_ops ++ ++ ++@pytest.fixture ++def group_db_setup(request): ++ group = request.param ++ grp_ops = group_ops_setup(request) ++ grp_ops.groupadd(**group) ++ ent.assert_group_by_name(group['name'], group) ++ return grp_ops ++ ++ ++class FilesOps(object): ++ """ ++ A naive implementation of operations as a basis for user or group ++ operations. Uses rename to (hopefully) trigger the same fs-level ++ notifications as shadow-utils would. ++ """ ++ def __init__(self, file_name): ++ self.file_name = file_name ++ self.tmp_dir = os.path.dirname(self.file_name) ++ ++ @staticmethod ++ def _get_named_line(name, contents): ++ for num, line in enumerate(contents, 0): ++ pname = line.split(':')[0] ++ if name == pname: ++ return num ++ raise KeyError("%s not found" % name) ++ ++ def _read_contents(self): ++ with open(self.file_name, "r") as pfile: ++ contents = pfile.readlines() ++ return contents ++ ++ def _write_contents(self, contents): ++ tmp_file = tempfile.NamedTemporaryFile(dir=self.tmp_dir, delete=False) ++ tmp_file.writelines(contents) ++ tmp_file.flush() ++ ++ os.rename(tmp_file.name, self.file_name) ++ ++ def _append_line(self, new_line): ++ contents = self._read_contents() ++ contents.extend(new_line) ++ self._write_contents(contents) ++ ++ def _subst_line(self, key, line): ++ contents = self._read_contents() ++ kindex = self._get_named_line(key, contents) ++ contents[kindex] = line ++ self._write_contents(contents) ++ ++ def _del_line(self, key): ++ contents = self._read_contents() ++ kindex = self._get_named_line(key, contents) ++ contents.pop(kindex) ++ self._write_contents(contents) ++ ++ contents = self._read_contents() ++ ++ ++class PasswdOps(FilesOps): ++ """ ++ A naive implementation of user operations ++ """ ++ def __init__(self, file_name): ++ super(PasswdOps, self).__init__(file_name) ++ ++ def _pwd2line(self, name, uid, gid, passwd, gecos, homedir, shell): ++ pwd_fmt = "{name}:{passwd}:{uid}:{gid}:{gecos}:{homedir}:{shell}\n" ++ return pwd_fmt.format(name=name, ++ passwd=passwd, ++ uid=uid, ++ gid=gid, ++ gecos=gecos, ++ homedir=homedir, ++ shell=shell) ++ ++ def useradd(self, name, uid, gid, passwd='', gecos='', dir='', shell=''): ++ pwd_line = self._pwd2line(name, uid, gid, passwd, gecos, dir, shell) ++ self._append_line(pwd_line) ++ ++ def usermod(self, name, uid, gid, passwd='', gecos='', dir='', shell=''): ++ pwd_line = self._pwd2line(name, uid, gid, passwd, gecos, dir, shell) ++ self._subst_line(name, pwd_line) ++ ++ def userdel(self, name): ++ self._del_line(name) ++ ++ ++class GroupOps(FilesOps): ++ """ ++ A naive implementation of group operations ++ """ ++ def __init__(self, file_name): ++ super(GroupOps, self).__init__(file_name) ++ ++ def _grp2line(self, name, gid, mem, passwd): ++ member_list = ",".join(m for m in mem) ++ grp_fmt = "{name}:{passwd}:{gid}:{member_list}\n" ++ return grp_fmt.format(name=name, ++ passwd=passwd, ++ gid=gid, ++ member_list=member_list) ++ ++ def groupadd(self, name, gid, mem, passwd="*"): ++ grp_line = self._grp2line(name, gid, mem, passwd) ++ self._append_line(grp_line) ++ ++ def groupmod(self, old_name, name, gid, mem, passwd="*"): ++ grp_line = self._grp2line(name, gid, mem, passwd) ++ self._subst_line(old_name, grp_line) ++ ++ def groupdel(self, name): ++ self._del_line(name) +diff --git a/src/tests/intg/test_files_ops.py b/src/tests/intg/test_files_ops.py +new file mode 100644 +index 0000000000000000000000000000000000000000..63816acbfe6bc9aab7425136bb7c7b90af42f0f6 +--- /dev/null ++++ b/src/tests/intg/test_files_ops.py +@@ -0,0 +1,84 @@ ++# ++# SSSD integration test - operations on UNIX user and group database ++# ++# 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 pwd ++import grp ++import pytest ++ ++import ent ++from files_ops import passwd_ops_setup, group_ops_setup ++ ++USER1 = dict(name='user1', passwd='*', uid=10001, gid=20001, ++ gecos='User for tests', ++ dir='/home/user1', ++ shell='/bin/bash') ++ ++GROUP1 = dict(name='group1', ++ gid=30001, ++ mem=['user1']) ++ ++ ++def test_useradd(passwd_ops_setup): ++ with pytest.raises(KeyError): ++ pwd.getpwnam("user1") ++ passwd_ops_setup.useradd(**USER1) ++ ent.assert_passwd_by_name("user1", USER1) ++ ++ ++def test_usermod(passwd_ops_setup): ++ passwd_ops_setup.useradd(**USER1) ++ ent.assert_passwd_by_name("user1", USER1) ++ ++ USER1['shell'] = '/bin/zsh' ++ passwd_ops_setup.usermod(**USER1) ++ ent.assert_passwd_by_name("user1", USER1) ++ ++ ++def test_userdel(passwd_ops_setup): ++ passwd_ops_setup.useradd(**USER1) ++ ent.assert_passwd_by_name("user1", USER1) ++ ++ passwd_ops_setup.userdel("user1") ++ with pytest.raises(KeyError): ++ pwd.getpwnam("user1") ++ ++ ++def test_groupadd(group_ops_setup): ++ with pytest.raises(KeyError): ++ grp.getgrnam("group1") ++ group_ops_setup.groupadd(**GROUP1) ++ ent.assert_group_by_name("group1", GROUP1) ++ ++ ++def test_groupmod(group_ops_setup): ++ group_ops_setup.groupadd(**GROUP1) ++ ent.assert_group_by_name("group1", GROUP1) ++ ++ modgroup = dict(GROUP1) ++ modgroup['mem'] = [] ++ ++ group_ops_setup.groupmod(old_name=GROUP1["name"], **modgroup) ++ ent.assert_group_by_name("group1", modgroup) ++ ++ ++def test_groupdel(group_ops_setup): ++ group_ops_setup.groupadd(**GROUP1) ++ ent.assert_group_by_name("group1", GROUP1) ++ ++ group_ops_setup.groupdel("group1") ++ with pytest.raises(KeyError): ++ grp.getgrnam("group1") +diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py +new file mode 100644 +index 0000000000000000000000000000000000000000..0a2e5a7f4da8b27bc410c401baf4f5f226c2a301 +--- /dev/null ++++ b/src/tests/intg/test_files_provider.py +@@ -0,0 +1,692 @@ ++# ++# SSSD files domain 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 stat ++import time ++import config ++import signal ++import subprocess ++import pwd ++import grp ++import pytest ++ ++import ent ++import sssd_id ++from sssd_nss import NssReturnCode ++from sssd_passwd import call_sssd_getpwnam, call_sssd_enumeration ++from sssd_group import call_sssd_getgrnam ++from files_ops import passwd_ops_setup, group_ops_setup ++from util import unindent ++ ++CANARY = dict(name='canary', passwd='x', uid=100001, gid=200001, ++ gecos='Used to check if passwd is resolvable', ++ dir='/home/canary', ++ shell='/bin/bash') ++ ++USER1 = dict(name='user1', passwd='x', uid=10001, gid=20001, ++ gecos='User for tests', ++ dir='/home/user1', ++ shell='/bin/bash') ++ ++USER2 = dict(name='user2', passwd='x', uid=10002, gid=20001, ++ gecos='User2 for tests', ++ dir='/home/user2', ++ shell='/bin/bash') ++ ++CANARY_GR = dict(name='canary', ++ gid=300001, ++ mem=[]) ++ ++GROUP1 = dict(name='group1', ++ gid=30001, ++ mem=['user1']) ++ ++GROUP12 = dict(name='group12', ++ gid=30012, ++ mem=['user1', 'user2']) ++ ++GROUP_NOMEM = dict(name='group_nomem', ++ gid=40000, ++ mem=[]) ++ ++ ++def stop_sssd(): ++ pid_file = open(config.PIDFILE_PATH, "r") ++ 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""" ++ os.environ["SSS_FILES_PASSWD"] = os.environ["NSS_WRAPPER_PASSWD"] ++ os.environ["SSS_FILES_GROUP"] = os.environ["NSS_WRAPPER_GROUP"] ++ 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) ++ ++ ++# Fixtures ++@pytest.fixture ++def files_domain_only(request): ++ conf = unindent("""\ ++ [sssd] ++ domains = files ++ services = nss ++ ++ [domain/files] ++ id_provider = files ++ """).format(**locals()) ++ create_conf_fixture(request, conf) ++ create_sssd_fixture(request) ++ return None ++ ++ ++def setup_pw_with_list(request, user_list): ++ pwd_ops = passwd_ops_setup(request) ++ for user in user_list: ++ pwd_ops.useradd(**user) ++ ent.assert_passwd_by_name(CANARY['name'], CANARY) ++ return pwd_ops ++ ++ ++@pytest.fixture ++def add_user_with_canary(request): ++ return setup_pw_with_list(request, [CANARY, USER1]) ++ ++ ++@pytest.fixture ++def setup_pw_with_canary(request): ++ return setup_pw_with_list(request, [CANARY]) ++ ++ ++def setup_gr_with_list(request, group_list): ++ grp_ops = group_ops_setup(request) ++ for group in group_list: ++ grp_ops.groupadd(**group) ++ ent.assert_group_by_name(CANARY_GR['name'], CANARY_GR) ++ return grp_ops ++ ++ ++@pytest.fixture ++def add_group_with_canary(request): ++ return setup_gr_with_list(request, [GROUP1, CANARY_GR]) ++ ++ ++@pytest.fixture ++def setup_gr_with_canary(request): ++ return setup_gr_with_list(request, [CANARY_GR]) ++ ++ ++def poll_canary(fn, name, threshold=20): ++ """ ++ If we query SSSD while it's updating its cache, it would return NOTFOUND ++ rather than a result from potentially outdated or incomplete cache. In ++ reality this doesn't hurt because the order of the modules is normally ++ "sss files" so the user lookup would fall back to files. But in tests ++ we use this loop to wait until the canary user who is always there is ++ resolved. ++ """ ++ for _ in range(0, threshold): ++ res, _ = fn(name) ++ if res == NssReturnCode.SUCCESS: ++ return True ++ elif res == NssReturnCode.NOTFOUND: ++ time.sleep(0.1) ++ continue ++ else: ++ return False ++ return False ++ ++ ++def sssd_getpwnam_sync(name): ++ ret = poll_canary(call_sssd_getpwnam, CANARY["name"]) ++ if ret is False: ++ return NssReturnCode.NOTFOUND, None ++ ++ return call_sssd_getpwnam(name) ++ ++ ++def sssd_getgrnam_sync(name): ++ ret = poll_canary(call_sssd_getgrnam, CANARY_GR["name"]) ++ if ret is False: ++ return NssReturnCode.NOTFOUND, None ++ ++ return call_sssd_getgrnam(name) ++ ++ ++def sssd_id_sync(name): ++ sssd_getpwnam_sync(CANARY["name"]) ++ res, _, groups = sssd_id.get_user_groups(name) ++ return res, groups ++ ++ ++# Helper functions ++def user_generator(seqnum): ++ return dict(name='user%d' % seqnum, ++ passwd='*', ++ uid=10000 + seqnum, ++ gid=20000 + seqnum, ++ gecos='User for tests', ++ dir='/home/user%d' % seqnum, ++ shell='/bin/bash') ++ ++ ++def check_user(exp_user, delay=1.0): ++ if delay > 0: ++ time.sleep(delay) ++ ++ res, found_user = sssd_getpwnam_sync(exp_user["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert found_user == exp_user ++ ++ ++def check_group(exp_group, delay=1.0): ++ if delay > 0: ++ time.sleep(delay) ++ ++ res, found_group = sssd_getgrnam_sync(exp_group["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert found_group == exp_group ++ ++ ++def check_group_list(exp_groups_list): ++ for exp_group in exp_groups_list: ++ check_group(exp_group) ++ ++ ++# User tests ++def test_getpwnam_after_start(add_user_with_canary, files_domain_only): ++ """ ++ Test that after startup without any additional operations, a user ++ can be resolved through sssd ++ """ ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert user == USER1 ++ ++ ++def test_getpwnam_neg(files_domain_only): ++ """ ++ Test that a nonexistant user cannot be resolved ++ """ ++ res, _ = call_sssd_getpwnam("nosuchuser") ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def test_root_does_not_resolve(files_domain_only): ++ """ ++ SSSD currently does not resolve the root user even though it can ++ be resolved through the NSS interface ++ """ ++ nss_root = pwd.getpwnam("root") ++ assert nss_root is not None ++ ++ res, _ = call_sssd_getpwnam("root") ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def test_add_remove_add_file_user(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that removing a user is detected and the user ++ is removed from the sssd database. Similarly, an add ++ should be detected. Do this several times to test retaining ++ the inotify watch for moved and unlinked files. ++ """ ++ res, _ = call_sssd_getpwnam(USER1["name"]) ++ assert res == NssReturnCode.NOTFOUND ++ ++ setup_pw_with_canary.useradd(**USER1) ++ check_user(USER1) ++ ++ setup_pw_with_canary.userdel(USER1["name"]) ++ time.sleep(1.0) ++ res, _ = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.NOTFOUND ++ ++ setup_pw_with_canary.useradd(**USER1) ++ check_user(USER1) ++ ++ ++def test_mod_user_shell(add_user_with_canary, files_domain_only): ++ """ ++ Test that modifying a user shell is detected and the user ++ is modified in the sssd database ++ """ ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert user == USER1 ++ ++ moduser = dict(USER1) ++ moduser['shell'] = '/bin/zsh' ++ add_user_with_canary.usermod(**moduser) ++ ++ check_user(moduser) ++ ++ ++def test_enum_users(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that enumerating all users works with the default configuration. Also ++ test that removing all entries and then enumerating again returns an empty ++ set ++ """ ++ num_users = 10 ++ for i in range(1, num_users+1): ++ user = user_generator(i) ++ setup_pw_with_canary.useradd(**user) ++ ++ sssd_getpwnam_sync(CANARY["name"]) ++ user_list = call_sssd_enumeration() ++ # +1 because the canary is added ++ assert len(user_list) == num_users+1 ++ ++ ++def incomplete_user_setup(pwd_ops, del_field, exp_field): ++ adduser = dict(USER1) ++ del adduser[del_field] ++ exp_user = dict(USER1) ++ exp_user[del_field] = exp_field ++ ++ pwd_ops.useradd(**adduser) ++ ++ return exp_user ++ ++ ++def test_user_no_shell(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that resolving a user without a shell defined works and returns ++ a fallback value ++ """ ++ check_user(incomplete_user_setup(setup_pw_with_canary, 'shell', '')) ++ ++ ++def test_user_no_dir(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that resolving a user without a homedir defined works and returns ++ a fallback value ++ """ ++ check_user(incomplete_user_setup(setup_pw_with_canary, 'dir', '/')) ++ ++ ++def test_user_no_gecos(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that resolving a user without a gecos defined works and returns ++ a fallback value ++ """ ++ check_user(incomplete_user_setup(setup_pw_with_canary, 'gecos', '')) ++ ++ ++def test_user_no_passwd(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that resolving a user without a password defined works and returns ++ a fallback value ++ """ ++ check_user(incomplete_user_setup(setup_pw_with_canary, 'passwd', 'x')) ++ ++ ++def bad_incomplete_user_setup(pwd_ops, del_field): ++ adduser = dict(USER1) ++ adduser[del_field] = '' ++ ++ pwd_ops.useradd(**adduser) ++ ++ ++def test_incomplete_user_fail(setup_pw_with_canary, files_domain_only): ++ """ ++ Test resolving an incomplete user where the missing field is required ++ to be present in the user record and thus the user shouldn't resolve. ++ ++ We cannot test uid and gid missing because nss_wrapper doesn't even ++ load the malformed passwd file, then. ++ """ ++ bad_incomplete_user_setup(setup_pw_with_canary, 'name') ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def test_getgrnam_after_start(add_group_with_canary, files_domain_only): ++ """ ++ Test that after startup without any additional operations, a group ++ can be resolved through sssd ++ """ ++ check_group(GROUP1) ++ ++ ++def test_getgrnam_neg(files_domain_only): ++ """ ++ Test that a nonexistant group cannot be resolved ++ """ ++ res, user = sssd_getgrnam_sync("nosuchgroup") ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def test_root_group_does_not_resolve(files_domain_only): ++ """ ++ SSSD currently does not resolve the root group even though it can ++ be resolved through the NSS interface ++ """ ++ nss_root = grp.getgrnam("root") ++ assert nss_root is not None ++ ++ res, user = call_sssd_getgrnam("root") ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def test_add_remove_add_file_group(setup_gr_with_canary, files_domain_only): ++ """ ++ Test that removing a group is detected and the group ++ is removed from the sssd database. Similarly, an add ++ should be detected. Do this several times to test retaining ++ the inotify watch for moved and unlinked files. ++ """ ++ res, group = call_sssd_getgrnam(GROUP1["name"]) ++ assert res == NssReturnCode.NOTFOUND ++ ++ setup_gr_with_canary.groupadd(**GROUP1) ++ check_group(GROUP1) ++ ++ setup_gr_with_canary.groupdel(GROUP1["name"]) ++ time.sleep(1) ++ res, group = call_sssd_getgrnam(GROUP1["name"]) ++ assert res == NssReturnCode.NOTFOUND ++ ++ setup_gr_with_canary.groupadd(**GROUP1) ++ check_group(GROUP1) ++ ++ ++def test_mod_group_name(add_group_with_canary, files_domain_only): ++ """ ++ Test that modifying a group name is detected and the group ++ is modified in the sssd database ++ """ ++ check_group(GROUP1) ++ ++ modgroup = dict(GROUP1) ++ modgroup['name'] = 'group1_mod' ++ add_group_with_canary.groupmod(old_name=GROUP1["name"], **modgroup) ++ ++ check_group(modgroup) ++ ++ ++def test_mod_group_gid(add_group_with_canary, files_domain_only): ++ """ ++ Test that modifying a group name is detected and the group ++ is modified in the sssd database ++ """ ++ check_group(GROUP1) ++ ++ modgroup = dict(GROUP1) ++ modgroup['gid'] = 30002 ++ add_group_with_canary.groupmod(old_name=GROUP1["name"], **modgroup) ++ ++ check_group(modgroup) ++ ++ ++@pytest.fixture ++def add_group_nomem_with_canary(request): ++ return setup_gr_with_list(request, [GROUP_NOMEM, CANARY_GR]) ++ ++ ++def test_getgrnam_no_members(add_group_nomem_with_canary, files_domain_only): ++ """ ++ Test that after startup without any additional operations, a group ++ can be resolved through sssd ++ """ ++ check_group(GROUP_NOMEM) ++ ++ ++def groupadd_list(grp_ops, groups): ++ for grp in groups: ++ grp_ops.groupadd(**grp) ++ ++ ++def useradd_list(pwd_ops, users): ++ for usr in users: ++ pwd_ops.useradd(**usr) ++ ++ ++def user_and_group_setup(pwd_ops, grp_ops, users, groups, reverse): ++ """ ++ The reverse is added so that we test cases where a group is added first, ++ then a user for this group is created -- in that case, we need to properly ++ link the group after the user is added. ++ """ ++ if reverse is False: ++ useradd_list(pwd_ops, users) ++ groupadd_list(grp_ops, groups) ++ else: ++ groupadd_list(grp_ops, groups) ++ useradd_list(pwd_ops, users) ++ ++ ++def members_check(added_groups): ++ # Test that users are members as per getgrnam ++ check_group_list(added_groups) ++ ++ # Test that users are members as per initgroups ++ for group in added_groups: ++ for member in group['mem']: ++ res, groups = sssd_id_sync(member) ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ assert group['name'] in groups ++ ++ ++def test_getgrnam_members_users_first(setup_pw_with_canary, ++ setup_gr_with_canary, ++ files_domain_only): ++ """ ++ A user is linked with a group ++ """ ++ user_and_group_setup(setup_pw_with_canary, ++ setup_gr_with_canary, ++ [USER1], ++ [GROUP1], ++ False) ++ members_check([GROUP1]) ++ ++ ++def test_getgrnam_members_users_multiple(setup_pw_with_canary, ++ setup_gr_with_canary, ++ files_domain_only): ++ """ ++ Multiple users are linked with a group ++ """ ++ user_and_group_setup(setup_pw_with_canary, ++ setup_gr_with_canary, ++ [USER1, USER2], ++ [GROUP12], ++ False) ++ members_check([GROUP12]) ++ ++ ++def test_getgrnam_members_groups_first(setup_pw_with_canary, ++ setup_gr_with_canary, ++ files_domain_only): ++ """ ++ A group is linked with a user ++ """ ++ user_and_group_setup(setup_pw_with_canary, ++ setup_gr_with_canary, ++ [USER1], ++ [GROUP1], ++ True) ++ members_check([GROUP1]) ++ ++ ++def test_getgrnam_ghost(setup_pw_with_canary, ++ setup_gr_with_canary, ++ files_domain_only): ++ """ ++ Test that a group with members while the members are not present ++ are added as ghosts. This is also what nss_files does, getgrnam would ++ return group members that do not exist as well. ++ """ ++ user_and_group_setup(setup_pw_with_canary, ++ setup_gr_with_canary, ++ [], ++ [GROUP12], ++ False) ++ check_group(GROUP12) ++ for member in GROUP12['mem']: ++ res, _ = call_sssd_getpwnam(member) ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def ghost_and_member_test(pw_ops, grp_ops, reverse): ++ user_and_group_setup(pw_ops, ++ grp_ops, ++ [USER1], ++ [GROUP12], ++ reverse) ++ check_group(GROUP12) ++ ++ # We checked that the group added has the same members as group12, ++ # so both user1 and user2. Now check that user1 is a member of ++ # group12 and its own primary GID but user2 doesn't exist, it's ++ # just a ghost entry ++ res, groups = sssd_id_sync('user1') ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ assert len(groups) == 2 ++ assert 'group12' in groups ++ ++ res, _ = call_sssd_getpwnam('user2') ++ assert res == NssReturnCode.NOTFOUND ++ ++ ++def test_getgrnam_user_ghost_and_member(setup_pw_with_canary, ++ setup_gr_with_canary, ++ files_domain_only): ++ """ ++ Test that a group with one member and one ghost. ++ """ ++ ghost_and_member_test(setup_pw_with_canary, ++ setup_gr_with_canary, ++ False) ++ ++ ++def test_getgrnam_user_member_and_ghost(setup_pw_with_canary, ++ setup_gr_with_canary, ++ files_domain_only): ++ """ ++ Test that a group with one member and one ghost, adding the group ++ first and then linking the member ++ """ ++ ghost_and_member_test(setup_pw_with_canary, ++ setup_gr_with_canary, ++ True) ++ ++ ++def test_getgrnam_add_remove_members(setup_pw_with_canary, ++ add_group_nomem_with_canary, ++ files_domain_only): ++ """ ++ Test that a user is linked with a group ++ """ ++ pwd_ops = setup_pw_with_canary ++ ++ check_group(GROUP_NOMEM) ++ ++ for usr in [USER1, USER2]: ++ pwd_ops.useradd(**usr) ++ ++ modgroup = dict(GROUP_NOMEM) ++ modgroup['mem'] = ['user1', 'user2'] ++ add_group_nomem_with_canary.groupmod(old_name=modgroup['name'], **modgroup) ++ check_group(modgroup) ++ ++ res, groups = sssd_id_sync('user1') ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ assert len(groups) == 2 ++ assert 'group_nomem' in groups ++ ++ res, groups = sssd_id_sync('user2') ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ assert 'group_nomem' in groups ++ ++ modgroup['mem'] = ['user2'] ++ add_group_nomem_with_canary.groupmod(old_name=modgroup['name'], **modgroup) ++ check_group(modgroup) ++ ++ # User1 exists, but is not a member of any supplementary group anymore ++ res, _ = call_sssd_getpwnam('user1') ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ res, groups = sssd_id_sync('user1') ++ assert res == sssd_id.NssReturnCode.NOTFOUND ++ ++ # user2 still is ++ res, groups = sssd_id_sync('user2') ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ assert len(groups) == 2 ++ assert 'group_nomem' in groups ++ ++ ++def test_getgrnam_add_remove_ghosts(setup_pw_with_canary, ++ add_group_nomem_with_canary, ++ files_domain_only): ++ """ ++ Test that a user is linked with a group ++ """ ++ pwd_ops = setup_pw_with_canary ++ ++ check_group(GROUP_NOMEM) ++ ++ modgroup = dict(GROUP_NOMEM) ++ modgroup['mem'] = ['user1', 'user2'] ++ add_group_nomem_with_canary.groupmod(old_name=modgroup['name'], **modgroup) ++ check_group(modgroup) ++ ++ modgroup['mem'] = ['user2'] ++ add_group_nomem_with_canary.groupmod(old_name=modgroup['name'], **modgroup) ++ check_group(modgroup) ++ ++ res, _ = call_sssd_getpwnam('user1') ++ assert res == NssReturnCode.NOTFOUND ++ res, _ = call_sssd_getpwnam('user2') ++ assert res == NssReturnCode.NOTFOUND ++ ++ # Add this user and verify it's been added as a member ++ pwd_ops.useradd(**USER2) ++ res, groups = sssd_id_sync('user2') ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ assert len(groups) == 2 ++ assert 'group_nomem' in groups +-- +2.9.3 + diff --git a/0062-MONITOR-Remove-checks-for-sssd.conf-changes.patch b/0062-MONITOR-Remove-checks-for-sssd.conf-changes.patch new file mode 100644 index 0000000..a42fe1e --- /dev/null +++ b/0062-MONITOR-Remove-checks-for-sssd.conf-changes.patch @@ -0,0 +1,44 @@ +From f9f1310ba1b87223f8d4d935b30b8238e5c00022 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 22 Nov 2016 17:47:51 +0100 +Subject: [PATCH 62/79] MONITOR: Remove checks for sssd.conf changes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This feature was if-ed out for many years and since it's quite unlikely +we will re-enable the feature in the foreseeable future, let's just +remove this code. + +Reviewed-by: Pavel Březina +--- + src/monitor/monitor.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index 1fa3d4baf579f15b9f93355a4b0c8b9d706bbacf..71719917b1b01acfb0eba527c414fe72d1961080 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -2296,19 +2296,6 @@ static int monitor_process_init(struct mt_ctx *ctx, + ret = sss_sigchld_init(ctx, ctx->ev, &ctx->sigchld_ctx); + if (ret != EOK) return ret; + +-#if 0 +- This feature is incomplete and can leave the SSSD in a bad state if the +- config file is changed while the SSSD is running. +- +- Uncomment this once the backends are honoring reloadConfig() +- +- /* Watch for changes to the confdb config file */ +- ret = monitor_config_file(ctx, ctx, config_file, monitor_signal_reconf, +- true); +- if (ret != EOK) { +- return ret; +- } +-#endif + /* Watch for changes to the DNS resolv.conf */ + ret = monitor_config_file(ctx, ctx, RESOLV_CONF_PATH, + monitor_update_resolv, false); +-- +2.9.3 + diff --git a/0063-MONITOR-Use-the-common-inotify-code-to-watch-resolv..patch b/0063-MONITOR-Use-the-common-inotify-code-to-watch-resolv..patch new file mode 100644 index 0000000..aa4a449 --- /dev/null +++ b/0063-MONITOR-Use-the-common-inotify-code-to-watch-resolv..patch @@ -0,0 +1,700 @@ +From ee6c7e8b589497119ec1ee40e99611f362111600 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 22 Nov 2016 18:02:35 +0100 +Subject: [PATCH 63/79] MONITOR: Use the common inotify code to watch + resolv.conf +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The monitor code used its own inotify callbacks to watch for changes to +resolv.conf. Instead of keeping this duplicated code around, let's use +the shared inotify module that also powers the files provider. + +Reviewed-by: Pavel Březina +--- + Makefile.am | 1 + + src/monitor/monitor.c | 481 ++++++++++++-------------------------------------- + src/monitor/monitor.h | 3 - + 3 files changed, 114 insertions(+), 371 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index e6d3530d003fff9e92ff6520043f6906d4200bd7..e676e18415c9d20ffd5ba2ce825dddd62d50c909 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1279,6 +1279,7 @@ sssd_SOURCES = \ + src/confdb/confdb_setup.c \ + src/monitor/monitor_iface_generated.c \ + src/util/nscd.c \ ++ src/util/inotify.c \ + $(NULL) + sssd_LDADD = \ + $(SSSD_LIBS) \ +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index 71719917b1b01acfb0eba527c414fe72d1961080..bc7402103d8024391d2ee27f48b28247ff9cd49c 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -50,6 +50,7 @@ + #include "sbus/sssd_dbus.h" + #include "monitor/monitor_interfaces.h" + #include "responder/common/responder_sbus.h" ++#include "util/inotify.h" + + #ifdef USE_KEYRING + #include +@@ -125,9 +126,11 @@ struct mt_svc { + struct sss_child_ctx *child_ctx; + }; + ++typedef int (*monitor_reconf_fn)(struct config_file_ctx *file_ctx, ++ const char *filename); ++ + struct config_file_callback { + int wd; +- int retries; + monitor_reconf_fn fn; + char *filename; + time_t modified; +@@ -136,11 +139,18 @@ struct config_file_callback { + }; + + struct config_file_ctx { +- TALLOC_CTX *parent_ctx; +- struct tevent_timer *timer; +- bool needs_update; ++ struct config_file_inotify_check { ++ struct snotify_ctx *snctx; ++ } inotify_check; ++ ++ struct config_file_poll_check { ++ TALLOC_CTX *parent_ctx; ++ struct tevent_timer *timer; ++ struct config_file_callback *callbacks; ++ } poll_check; ++ ++ monitor_reconf_fn fn; + struct mt_ctx *mt_ctx; +- struct config_file_callback *callbacks; + }; + + struct mt_ctx { +@@ -153,7 +163,6 @@ struct mt_ctx { + struct mt_svc *svc_list; + struct sbus_connection *sbus_srv; + struct config_file_ctx *file_ctx; +- int inotify_fd; + int service_id_timeout; + bool check_children; + bool services_started; +@@ -712,19 +721,23 @@ static void reload_reply(DBusPendingCall *pending, void *data) + + static int service_signal_dns_reload(struct mt_svc *svc); + static int monitor_update_resolv(struct config_file_ctx *file_ctx, +- const char *filename) ++ const char *filename) + { + int ret; + struct mt_svc *cur_svc; +- DEBUG(SSSDBG_OP_FAILURE, "Resolv.conf has been updated. Reloading.\n"); ++ struct mt_ctx *mt_ctx; ++ ++ mt_ctx = file_ctx->mt_ctx; ++ ++ DEBUG(SSSDBG_TRACE_LIBS, "Resolv.conf has been updated. Reloading.\n"); + + ret = res_init(); +- if(ret != 0) { ++ if (ret != 0) { + return EIO; + } + + /* Signal all services to reload their DNS configuration */ +- for(cur_svc = file_ctx->mt_ctx->svc_list; cur_svc; cur_svc = cur_svc->next) { ++ for (cur_svc = mt_ctx->svc_list; cur_svc; cur_svc = cur_svc->next) { + service_signal_dns_reload(cur_svc); + } + return EOK; +@@ -1716,258 +1729,52 @@ done: + return ret; + } + +-static errno_t monitor_config_file_fallback(TALLOC_CTX *mem_ctx, +- struct mt_ctx *ctx, +- const char *file, +- monitor_reconf_fn fn, +- bool ignore_missing); ++static void poll_config_file(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, void *ptr); ++static errno_t monitor_config_file_fallback(TALLOC_CTX *parent_ctx, ++ struct config_file_ctx *file_ctx, ++ const char *file); + +-#ifdef HAVE_INOTIFY +-static void process_config_file(struct tevent_context *ev, +- struct tevent_timer *te, +- struct timeval t, void *ptr); +- +-static void config_file_changed(struct tevent_context *ev, +- struct tevent_fd *fde, +- uint16_t flags, void *data) ++static errno_t create_poll_timer(struct config_file_ctx *file_ctx) + { +- struct tevent_timer *te = NULL; + struct timeval tv; +- struct config_file_ctx *file_ctx; + +- file_ctx = talloc_get_type(data, struct config_file_ctx); +- if (file_ctx->needs_update) { +- /* Skip updating. It's already queued for update. +- */ +- return; +- } +- +- /* We will queue the file for update in one second. +- * This way, if there is a script writing to the file +- * repeatedly, we won't be attempting to update multiple +- * times. +- */ + gettimeofday(&tv, NULL); +- tv.tv_sec += 1; ++ tv.tv_sec += CONFIG_FILE_POLL_INTERVAL; ++ tv.tv_usec = 0; + +- te = tevent_add_timer(ev, ev, tv, process_config_file, file_ctx); +- if (!te) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Unable to queue config file update! Exiting.\n"); +- kill(getpid(), SIGTERM); +- return; ++ file_ctx->poll_check.timer = tevent_add_timer(file_ctx->mt_ctx->ev, ++ file_ctx->poll_check.parent_ctx, ++ tv, ++ poll_config_file, ++ file_ctx); ++ if (!file_ctx->poll_check.timer) { ++ talloc_free(file_ctx); ++ return EIO; + } +- file_ctx->needs_update = 1; ++ ++ return EOK; + } + +-struct rewatch_ctx { +- struct config_file_callback *cb; +- struct config_file_ctx *file_ctx; +-}; +-static void rewatch_config_file(struct tevent_context *ev, +- struct tevent_timer *te, +- struct timeval t, void *ptr); +-static void process_config_file(struct tevent_context *ev, +- struct tevent_timer *te, +- struct timeval t, void *ptr) ++static void poll_config_file(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval t, void *ptr) + { +- TALLOC_CTX *tmp_ctx; +- struct inotify_event *in_event; +- char *name; +- ssize_t len; ++ int ret, err; ++ struct stat file_stat; + struct config_file_ctx *file_ctx; + struct config_file_callback *cb; +- struct rewatch_ctx *rw_ctx; +- errno_t ret; + + file_ctx = talloc_get_type(ptr, struct config_file_ctx); + +- DEBUG(SSSDBG_CRIT_FAILURE, "Processing config file changes\n"); +- +- tmp_ctx = talloc_new(NULL); +- if (!tmp_ctx) return; +- +- in_event = talloc(tmp_ctx, struct inotify_event); +- if (!in_event) { +- goto done; +- } +- +- errno = 0; +- len = sss_atomic_read_s(file_ctx->mt_ctx->inotify_fd, in_event, +- sizeof(struct inotify_event)); +- if (len == -1) { +- ret = errno; +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Critical error reading inotify file descriptor [%d]: %s\n", +- ret, strerror(ret)); +- goto done; +- } +- +- if (in_event->len > 0) { +- /* Read in the name, even though we don't use it, +- * so that read ptr is in the right place +- */ +- name = talloc_size(tmp_ctx, in_event->len); +- if (!name) { +- goto done; +- } +- +- errno = 0; +- len = sss_atomic_read_s(file_ctx->mt_ctx->inotify_fd, name, in_event->len); +- if (len == -1) { +- ret = errno; +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Critical error reading inotify file descriptor [%d]: %s\n", +- ret, strerror(ret)); +- goto done; +- } +- } +- +- for (cb = file_ctx->callbacks; cb; cb = cb->next) { +- if (cb->wd == in_event->wd) { +- break; +- } +- } +- if (!cb) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Unknown watch descriptor\n"); +- goto done; +- } +- +- if (in_event->mask & IN_IGNORED) { +- /* Some text editors will move a new file on top of the +- * existing one instead of modifying it. In this case, +- * the kernel will send us an IN_IGNORE signal. +- * We will try to open a new watch descriptor on the +- * new file. +- */ +- struct timeval tv; +- struct tevent_timer *tev; +- tv.tv_sec = t.tv_sec+5; +- tv.tv_usec = t.tv_usec; +- DEBUG(SSSDBG_FUNC_DATA, "Restoring inotify watch.\n"); +- +- cb->retries = 0; +- rw_ctx = talloc(file_ctx, struct rewatch_ctx); +- if(!rw_ctx) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not restore inotify watch. Quitting!\n"); +- close(file_ctx->mt_ctx->inotify_fd); +- kill(getpid(), SIGTERM); +- goto done; +- } +- rw_ctx->cb = cb; +- rw_ctx->file_ctx = file_ctx; +- +- tev = tevent_add_timer(ev, rw_ctx, tv, rewatch_config_file, rw_ctx); +- if (tev == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not restore inotify watch. Quitting!\n"); +- close(file_ctx->mt_ctx->inotify_fd); +- kill(getpid(), SIGTERM); +- } +- goto done; +- } +- +- /* Tell the monitor to signal the children */ +- cb->fn(file_ctx, cb->filename); +- file_ctx->needs_update = 0; +- +-done: +- talloc_free(tmp_ctx); +-} +- +-static void rewatch_config_file(struct tevent_context *ev, +- struct tevent_timer *te, +- struct timeval t, void *ptr) +-{ +- int err; +- struct tevent_timer *tev = NULL; +- struct timeval tv; +- struct config_file_callback *cb; +- +- struct rewatch_ctx *rw_ctx; +- struct config_file_ctx *file_ctx; +- +- rw_ctx = talloc_get_type(ptr, struct rewatch_ctx); +- +- cb = rw_ctx->cb; +- file_ctx = rw_ctx->file_ctx; +- +- /* Retry six times at five-second intervals before giving up */ +- cb->retries++; +- if (cb->retries > 6) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not restore inotify watch. Switching to polling!\n"); +- close(file_ctx->mt_ctx->inotify_fd); +- err = monitor_config_file_fallback(file_ctx->parent_ctx, +- file_ctx->mt_ctx, +- cb->filename, +- cb->fn,true); +- if (err != EOK) +- kill(getpid(), SIGTERM); +- +- cb->fn(file_ctx, cb->filename); +- talloc_free(rw_ctx); +- +- /* A new callback was created in monitor_config_file_fallback()*/ +- DLIST_REMOVE(file_ctx->callbacks, cb); +- talloc_free(cb); +- +- return; +- } +- +- cb->wd = inotify_add_watch(file_ctx->mt_ctx->inotify_fd, +- cb->filename, IN_MODIFY); +- if (cb->wd < 0) { +- err = errno; +- +- tv.tv_sec = t.tv_sec+5; +- tv.tv_usec = t.tv_usec; +- +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Could not add inotify watch for file [%s]. Error [%d:%s]\n", +- cb->filename, err, strerror(err)); +- +- tev = tevent_add_timer(ev, ev, tv, rewatch_config_file, rw_ctx); +- if (tev == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not restore inotify watch. Quitting!\n"); +- close(file_ctx->mt_ctx->inotify_fd); +- kill(getpid(), SIGTERM); +- } +- +- return; +- } +- cb->retries = 0; +- +- /* Tell the monitor to signal the children */ +- cb->fn(file_ctx, cb->filename); +- +- talloc_free(rw_ctx); +- file_ctx->needs_update = 0; +-} +-#endif /* HAVE_INOTIFY */ +- +-static void poll_config_file(struct tevent_context *ev, +- struct tevent_timer *te, +- struct timeval t, void *ptr) +-{ +- int ret, err; +- struct stat file_stat; +- struct timeval tv; +- struct config_file_ctx *file_ctx; +- struct config_file_callback *cb; +- +- file_ctx = talloc_get_type(ptr,struct config_file_ctx); +- +- for (cb = file_ctx->callbacks; cb; cb = cb->next) { ++ for (cb = file_ctx->poll_check.callbacks; cb; cb = cb->next) { + ret = stat(cb->filename, &file_stat); + if (ret < 0) { + err = errno; + DEBUG(SSSDBG_FATAL_FAILURE, + "Could not stat file [%s]. Error [%d:%s]\n", +- cb->filename, err, strerror(err)); +- /* TODO: If the config file is missing, should we shut down? */ ++ cb->filename, err, strerror(err)); + return; + } + +@@ -1984,88 +1791,51 @@ static void poll_config_file(struct tevent_context *ev, + } + } + +- gettimeofday(&tv, NULL); +- tv.tv_sec += CONFIG_FILE_POLL_INTERVAL; +- tv.tv_usec = 0; +- file_ctx->timer = tevent_add_timer(ev, file_ctx->parent_ctx, tv, +- poll_config_file, file_ctx); +- if (!file_ctx->timer) { ++ ret = create_poll_timer(file_ctx); ++ if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Error: Config file no longer monitored for changes!\n"); + } + } + +-static int try_inotify(struct config_file_ctx *file_ctx, const char *filename, +- monitor_reconf_fn fn) ++static int resolv_conf_inotify_cb(const char *filename, ++ uint32_t flags, ++ void *pvt) ++{ ++ struct config_file_ctx *file_ctx; ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "Received inotify notification for %s\n", filename); ++ ++ file_ctx = talloc_get_type(pvt, struct config_file_ctx); ++ if (file_ctx == NULL) { ++ return EINVAL; ++ } ++ ++ return file_ctx->fn(file_ctx, filename); ++} ++ ++static int try_inotify(struct config_file_ctx *file_ctx, ++ const char *filename) + { + #ifdef HAVE_INOTIFY +- int err, fd_args, ret; +- struct tevent_fd *tfd; +- struct config_file_callback *cb; ++ struct snotify_ctx *snctx; ++ /* We will queue the file for update in one second. ++ * This way, if there is a script writing to the file ++ * repeatedly, we won't be attempting to update multiple ++ * times. ++ */ ++ struct timeval delay = { .tv_sec = 1, .tv_usec = 0 }; + +- /* Monitoring the file descriptor should be global */ +- if (!file_ctx->mt_ctx->inotify_fd) { +- /* Set up inotify to monitor the config file for changes */ +- file_ctx->mt_ctx->inotify_fd = inotify_init(); +- if (file_ctx->mt_ctx->inotify_fd < 0) { +- err = errno; +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not initialize inotify, error [%d:%s]\n", +- err, strerror(err)); +- return err; +- } +- +- fd_args = fcntl(file_ctx->mt_ctx->inotify_fd, F_GETFL, NULL); +- if (fd_args < 0) { +- /* Could not set nonblocking */ +- close(file_ctx->mt_ctx->inotify_fd); +- return EINVAL; +- } +- +- fd_args |= O_NONBLOCK; +- ret = fcntl(file_ctx->mt_ctx->inotify_fd, F_SETFL, fd_args); +- if (ret < 0) { +- /* Could not set nonblocking */ +- close(file_ctx->mt_ctx->inotify_fd); +- return EINVAL; +- } +- +- /* Add the inotify file descriptor to the TEvent context */ +- tfd = tevent_add_fd(file_ctx->mt_ctx->ev, file_ctx, +- file_ctx->mt_ctx->inotify_fd, +- TEVENT_FD_READ, config_file_changed, +- file_ctx); +- if (!tfd) { +- close(file_ctx->mt_ctx->inotify_fd); +- return EIO; +- } +- } +- +- cb = talloc_zero(file_ctx, struct config_file_callback); +- if(!cb) { +- close(file_ctx->mt_ctx->inotify_fd); +- return ENOMEM; ++ snctx = snotify_create(file_ctx, file_ctx->mt_ctx->ev, SNOTIFY_WATCH_DIR, ++ filename, &delay, ++ IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MOVE_SELF | \ ++ IN_CREATE | IN_MOVED_TO | IN_IGNORED, ++ resolv_conf_inotify_cb, file_ctx); ++ if (snctx == NULL) { ++ return EIO; + } + +- cb->filename = talloc_strdup(cb, filename); +- if (!cb->filename) { +- close(file_ctx->mt_ctx->inotify_fd); +- return ENOMEM; +- } +- cb->wd = inotify_add_watch(file_ctx->mt_ctx->inotify_fd, +- cb->filename, IN_MODIFY); +- if (cb->wd < 0) { +- err = errno; +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not add inotify watch for file [%s]. Error [%d:%s]\n", +- cb->filename, err, strerror(err)); +- close(file_ctx->mt_ctx->inotify_fd); +- return err; +- } +- cb->fn = fn; +- +- DLIST_ADD(file_ctx->callbacks, cb); +- + return EOK; + #else + return EINVAL; +@@ -2074,36 +1844,18 @@ static int try_inotify(struct config_file_ctx *file_ctx, const char *filename, + + static int monitor_config_file(TALLOC_CTX *mem_ctx, + struct mt_ctx *ctx, +- const char *file, + monitor_reconf_fn fn, +- bool ignore_missing) ++ const char *file) + { +- int ret, err; ++ int ret; + bool use_inotify; +- struct stat file_stat; + +- ret = stat(file, &file_stat); +- if (ret < 0) { +- err = errno; +- if (err == ENOENT && ignore_missing) { +- DEBUG(SSSDBG_MINOR_FAILURE, +- "file [%s] is missing. Will not update online status " +- "based on watching the file\n", file); +- return EOK; +- } else { +- DEBUG(SSSDBG_MINOR_FAILURE, +- "Could not stat file [%s]. Error [%d:%s]\n", +- file, err, strerror(err)); +- +- return err; +- } +- } + if (!ctx->file_ctx) { + ctx->file_ctx = talloc_zero(mem_ctx, struct config_file_ctx); + if (!ctx->file_ctx) return ENOMEM; + +- ctx->file_ctx->parent_ctx = mem_ctx; + ctx->file_ctx->mt_ctx = ctx; ++ ctx->file_ctx->fn = fn; + } + + ret = confdb_get_bool(ctx->cdb, +@@ -2116,73 +1868,68 @@ static int monitor_config_file(TALLOC_CTX *mem_ctx, + } + + if (use_inotify) { +- ret = try_inotify(ctx->file_ctx, file, fn); ++ ret = try_inotify(ctx->file_ctx, file); + if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Falling back to polling\n"); + use_inotify = false; + } + } + +- if (!use_inotify) { +- /* Could not monitor file with inotify, fall back to polling */ +- ret = monitor_config_file_fallback(mem_ctx, ctx, file, fn, true); ++ if (use_inotify == false) { ++ ret = monitor_config_file_fallback(mem_ctx, ctx->file_ctx, file); + } + + return ret; + } + +-static errno_t monitor_config_file_fallback(TALLOC_CTX *mem_ctx, +- struct mt_ctx *ctx, +- const char *file, +- monitor_reconf_fn fn, +- bool ignore_missing) ++static errno_t monitor_config_file_fallback(TALLOC_CTX *parent_ctx, ++ struct config_file_ctx *file_ctx, ++ const char *file) + { + struct config_file_callback *cb = NULL; + struct stat file_stat; + int ret, err; +- struct timeval tv; + + ret = stat(file, &file_stat); + if (ret < 0) { + err = errno; +- if (err == ENOENT && ignore_missing) { ++ if (err == ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, +- "file [%s] is missing. Will not update online status " +- "based on watching the file\n", file); ++ "file [%s] is missing. Will not update online status " ++ "based on watching the file\n", file); + return EOK; + + } else { + DEBUG(SSSDBG_FATAL_FAILURE, +- "Could not stat file [%s]. Error [%d:%s]\n", +- file, err, strerror(err)); ++ "Could not stat file [%s]. Error [%d:%s]\n", ++ file, err, strerror(err)); + + return err; + } + } + +- cb = talloc_zero(ctx->file_ctx, struct config_file_callback); ++ file_ctx->poll_check.parent_ctx = parent_ctx; ++ ++ cb = talloc_zero(file_ctx, struct config_file_callback); + if (!cb) { +- talloc_free(ctx->file_ctx); ++ talloc_free(file_ctx); + return ENOMEM; + } + cb->filename = talloc_strdup(cb, file); + if (!cb->filename) { +- talloc_free(ctx->file_ctx); ++ talloc_free(file_ctx); + return ENOMEM; + } +- cb->fn = fn; ++ cb->fn = file_ctx->fn; + cb->modified = file_stat.st_mtime; + +- DLIST_ADD(ctx->file_ctx->callbacks, cb); ++ DLIST_ADD(file_ctx->poll_check.callbacks, cb); + +- if(!ctx->file_ctx->timer) { +- gettimeofday(&tv, NULL); +- tv.tv_sec += CONFIG_FILE_POLL_INTERVAL; +- tv.tv_usec = 0; +- ctx->file_ctx->timer = tevent_add_timer(ctx->ev, mem_ctx, tv, +- poll_config_file, ctx->file_ctx); +- if (!ctx->file_ctx->timer) { +- talloc_free(ctx->file_ctx); +- return EIO; ++ if(!file_ctx->poll_check.timer) { ++ ret = create_poll_timer(file_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Cannot create poll timer\n"); ++ return ret; + } + } + +@@ -2198,8 +1945,7 @@ static void missing_resolv_conf(struct tevent_context *ev, + int ret; + struct mt_ctx *ctx = talloc_get_type(data, struct mt_ctx); + +- ret = monitor_config_file(ctx, ctx, RESOLV_CONF_PATH, +- monitor_update_resolv, false); ++ ret = monitor_config_file(ctx, ctx, monitor_update_resolv, RESOLV_CONF_PATH); + if (ret == EOK) { + signal_res_init(ctx); + } else if (ret == ENOENT) { +@@ -2297,8 +2043,7 @@ static int monitor_process_init(struct mt_ctx *ctx, + if (ret != EOK) return ret; + + /* Watch for changes to the DNS resolv.conf */ +- ret = monitor_config_file(ctx, ctx, RESOLV_CONF_PATH, +- monitor_update_resolv, false); ++ ret = monitor_config_file(ctx, ctx, monitor_update_resolv, RESOLV_CONF_PATH); + if (ret == ENOENT) { + tv = tevent_timeval_current_ofs(MISSING_RESOLV_CONF_POLL_TIME, 0); + te = tevent_add_timer(ctx->ev, ctx, tv, missing_resolv_conf, ctx); +diff --git a/src/monitor/monitor.h b/src/monitor/monitor.h +index b5a300bf3c9b281b5e9ffa76872ff30668865117..3f679de8198aa882bc488dad642bacc9f215ba5b 100644 +--- a/src/monitor/monitor.h ++++ b/src/monitor/monitor.h +@@ -32,9 +32,6 @@ + + struct config_file_ctx; + +-typedef int (*monitor_reconf_fn) (struct config_file_ctx *file_ctx, +- const char *filename); +- + struct mt_ctx; + + /* from monitor_netlink.c */ +-- +2.9.3 + diff --git a/0064-MAN-Add-documentation-for-the-files-provider.patch b/0064-MAN-Add-documentation-for-the-files-provider.patch new file mode 100644 index 0000000..434c74d --- /dev/null +++ b/0064-MAN-Add-documentation-for-the-files-provider.patch @@ -0,0 +1,155 @@ +From da95ec568a941c85982e30611398efb86bc884ab Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 7 Dec 2016 14:38:43 +0100 +Subject: [PATCH 64/79] MAN: Add documentation for the files provider +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The new provider needs a man page. + +Reviewed-by: Pavel Březina +--- + contrib/sssd.spec.in | 1 + + src/man/Makefile.am | 4 +++ + src/man/po/po4a.cfg | 1 + + src/man/sssd-files.5.xml | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 94 insertions(+) + create mode 100644 src/man/sssd-files.5.xml + +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index fe51f9998aedbc7c157e8897a0dae67af037cec4..baeb08b124d17d4eb9cb4fdfac4168ec12dc56ad 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -850,6 +850,7 @@ done + + # The files provider is intentionally packaged in -common + %{_libdir}/%{name}/libsss_files.so ++%{_mandir}/man5/sssd-files.5* + + %dir %{sssdstatedir} + %dir %{_localstatedir}/cache/krb5rcache +diff --git a/src/man/Makefile.am b/src/man/Makefile.am +index 49058bc669eb1a5fb8093a0d64c65609a3164744..760bb7831b5852e1bf3be497ad5babdb4f4318c2 100644 +--- a/src/man/Makefile.am ++++ b/src/man/Makefile.am +@@ -84,6 +84,10 @@ if BUILD_NFS_IDMAP + man_MANS += sss_rpcidmapd.5 + endif + ++if HAVE_INOTIFY ++man_MANS += sssd-files.5 ++endif ++ + SUFFIXES = .1.xml .1 .3.xml .3 .5.xml .5 .8.xml .8 + .1.xml.1: + $(XMLLINT) $(XMLLINT_FLAGS) $< +diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg +index 00fd414428ac20fb97277d6d9225853d6ae6dad1..ffcf9a2793da3c0115bc846744ccb9592a9a68ef 100644 +--- a/src/man/po/po4a.cfg ++++ b/src/man/po/po4a.cfg +@@ -28,6 +28,7 @@ + [type:docbook] sss_ssh_knownhostsproxy.1.xml $lang:$(builddir)/$lang/sss_ssh_knownhostsproxy.1.xml + [type:docbook] idmap_sss.8.xml $lang:$(builddir)/$lang/idmap_sss.8.xml + [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] 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" +diff --git a/src/man/sssd-files.5.xml b/src/man/sssd-files.5.xml +new file mode 100644 +index 0000000000000000000000000000000000000000..d44fffc034803f774699ed01f11f0e5ec5cd8042 +--- /dev/null ++++ b/src/man/sssd-files.5.xml +@@ -0,0 +1,88 @@ ++ ++ ++ ++SSSD Manual pages ++ ++ ++ ++ ++ sssd-files ++ 5 ++ File Formats and Conventions ++ ++ ++ ++ sssd-files ++ SSSD files provider ++ ++ ++ ++ DESCRIPTION ++ ++ This manual page describes the files provider ++ for ++ ++ sssd ++ 8 ++ . ++ For a detailed syntax reference, refer to the FILE FORMAT section of the ++ ++ sssd.conf ++ 5 ++ manual page. ++ ++ ++ The files provider mirrors the content of the ++ ++ passwd ++ 5 ++ ++ and ++ ++ group ++ 5 ++ ++ files. The purpose of the files provider is to make the users ++ and groups traditionally only accessible with NSS interfaces ++ also available through the SSSD interfaces such as ++ ++ sssd-ifp ++ 5 ++ . ++ ++ ++ ++ ++ CONFIGURATION OPTIONS ++ ++ The files provider has no specific options of its own, however, ++ generic SSSD domain options can be set where applicable. ++ Refer to the section DOMAIN SECTIONS of the ++ ++ sssd.conf ++ 5 ++ manual page for details on the configuration ++ of an SSSD domain. ++ ++ ++ ++ ++ EXAMPLE ++ ++ The following example assumes that SSSD is correctly ++ configured and files is one of the domains in the ++ [sssd] section. ++ ++ ++ ++[domain/files] ++id_provider = files ++ ++ ++ ++ ++ ++ ++ ++ +-- +2.9.3 + diff --git a/0065-EXAMPLES-Do-not-point-to-id_provider-local.patch b/0065-EXAMPLES-Do-not-point-to-id_provider-local.patch new file mode 100644 index 0000000..e194ec9 --- /dev/null +++ b/0065-EXAMPLES-Do-not-point-to-id_provider-local.patch @@ -0,0 +1,38 @@ +From 89e53f7139b134360fda9e43d47ebfb89fcaac92 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 10 Feb 2017 16:39:19 +0100 +Subject: [PATCH 65/79] EXAMPLES: Do not point to id_provider=local +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It makes more sense to show id_provider=files + +Reviewed-by: Pavel Březina +--- + src/examples/sssd.conf | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/examples/sssd.conf b/src/examples/sssd.conf +index a851dbb7ecd5c3220fbd6a946a6c7be2822dbd27..1e8b537a730300a143764142ff0096db6a3f976b 100644 +--- a/src/examples/sssd.conf ++++ b/src/examples/sssd.conf +@@ -1,5 +1,4 @@ + [sssd] +-config_file_version = 2 + services = nss, pam + domains = shadowutils + +@@ -8,8 +7,7 @@ domains = shadowutils + [pam] + + [domain/shadowutils] +-id_provider = proxy +-proxy_lib_name = files ++id_provider = files + + auth_provider = proxy + proxy_pam_target = sssd-shadowutils +-- +2.9.3 + diff --git a/0066-SBUS-Document-how-to-free-the-result-of-sbus_create_.patch b/0066-SBUS-Document-how-to-free-the-result-of-sbus_create_.patch new file mode 100644 index 0000000..5d12f26 --- /dev/null +++ b/0066-SBUS-Document-how-to-free-the-result-of-sbus_create_.patch @@ -0,0 +1,40 @@ +From 0e7047c1533e5e424b28959488e8ffa91613abd9 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 13 Feb 2017 11:46:06 +0100 +Subject: [PATCH 66/79] SBUS: Document how to free the result of + sbus_create_message +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It might not be apparent how to free the message constructed by +sbus_create_message(). This patch just adds a comment that tells the +developer to either free the parent context or unref the message with a +dbus call directly. + +Reviewed-by: Pavel Březina +--- + src/sbus/sssd_dbus_utils.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/sbus/sssd_dbus_utils.h b/src/sbus/sssd_dbus_utils.h +index 74c21fb7930c7f5f5417b6a2587cf691b1bc0b19..e53a7faef10a4066b58213741cd73f7411419f00 100644 +--- a/src/sbus/sssd_dbus_utils.h ++++ b/src/sbus/sssd_dbus_utils.h +@@ -25,6 +25,13 @@ errno_t sbus_talloc_bound_message(TALLOC_CTX *mem_ctx, DBusMessage *msg); + errno_t sbus_error_to_errno(DBusError *error); + errno_t sbus_check_reply(DBusMessage *reply); + ++/* Creates a DBusMessage from a vararg list. Please note that even though ++ * this function and sbus_create_message accept a talloc memory context, ++ * it is not valid to free the resulting message with talloc_free() directly. ++ * Instead, either free the parent memory context or directly call ++ * dbus_message_unref on the message if you pass NULL memory context to ++ * these functions ++ */ + DBusMessage *sbus_create_message_valist(TALLOC_CTX *mem_ctx, + const char *bus, + const char *path, +-- +2.9.3 + diff --git a/0067-IPA_SUDO-Unused-value-fix.patch b/0067-IPA_SUDO-Unused-value-fix.patch new file mode 100644 index 0000000..c05513b --- /dev/null +++ b/0067-IPA_SUDO-Unused-value-fix.patch @@ -0,0 +1,33 @@ +From 334029028e566fab3dce5ce4b1b53cc4809c21b8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20=C4=8Cech?= +Date: Thu, 16 Feb 2017 13:57:09 +0100 +Subject: [PATCH 67/79] IPA_SUDO: Unused value fix +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Unused value was immediately overwritten. + +Resolves: +https://fedorahosted.org/sssd/ticket/3309 + +Reviewed-by: Fabiano Fidêncio +--- + src/providers/ipa/ipa_sudo_conversion.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/providers/ipa/ipa_sudo_conversion.c b/src/providers/ipa/ipa_sudo_conversion.c +index 05d863c20954c816e52d27fe4a5e1553776c6d41..f6d17d82b7c2f1bd63f8deb00c3c7c19d9881bc1 100644 +--- a/src/providers/ipa/ipa_sudo_conversion.c ++++ b/src/providers/ipa/ipa_sudo_conversion.c +@@ -956,7 +956,6 @@ convert_attributes(struct ipa_sudo_conv *conv, + value = table[i].conv_fn(tmp_ctx, conv, values[j], &skip_entry); + if (value == NULL) { + if (skip_entry) { +- ret = ENOENT; + continue; + } else { + ret = ENOMEM; +-- +2.9.3 + diff --git a/0068-intg-Fix-python3-issues.patch b/0068-intg-Fix-python3-issues.patch new file mode 100644 index 0000000..b52ba3b --- /dev/null +++ b/0068-intg-Fix-python3-issues.patch @@ -0,0 +1,99 @@ +From bac4458c89a589055ae3daf4f72cc7dba886264a Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Thu, 16 Feb 2017 10:07:33 +0100 +Subject: [PATCH 68/79] intg: Fix python3 issues + +NamedTemporaryFile use the default mode 'w+b' +and we tried to write strings. It is not a problem on python2 +but failed on pyhton3 + +Python module ctypes directly uses C functions from libraries. +C functions usually expect/returns "char *" when string is expected. +But python3 uses unicode for string. Decoding returned bytes +("char *") to unicode strings simplify tests in python3. +Otherwise we would need to convert bytes to string in each assertion. + +Reviewed-by: Martin Basti +--- + src/tests/intg/files_ops.py | 3 ++- + src/tests/intg/sssd_group.py | 6 ++++-- + src/tests/intg/sssd_passwd.py | 11 ++++++----- + 3 files changed, 12 insertions(+), 8 deletions(-) + +diff --git a/src/tests/intg/files_ops.py b/src/tests/intg/files_ops.py +index 65b3e5ee401e453c51dcd55433be8d9fa3ce6c92..62f56517d46b43c10783ca93bb59d75cbedd9224 100644 +--- a/src/tests/intg/files_ops.py ++++ b/src/tests/intg/files_ops.py +@@ -76,7 +76,8 @@ class FilesOps(object): + return contents + + def _write_contents(self, contents): +- tmp_file = tempfile.NamedTemporaryFile(dir=self.tmp_dir, delete=False) ++ tmp_file = tempfile.NamedTemporaryFile(mode='w', dir=self.tmp_dir, ++ delete=False) + tmp_file.writelines(contents) + tmp_file.flush() + +diff --git a/src/tests/intg/sssd_group.py b/src/tests/intg/sssd_group.py +index a9cfb32d59a5406fb1ad738d64c7487404f39a2d..ab873a726d4c1c35ed00fe4c431566ecef648880 100644 +--- a/src/tests/intg/sssd_group.py ++++ b/src/tests/intg/sssd_group.py +@@ -46,6 +46,7 @@ def getgrnam_r(name, result_p, buffer_p, buflen): + + errno = POINTER(c_int)(c_int(0)) + ++ name = name.encode('utf-8') + res = func(c_char_p(name), result_p, buffer_p, buflen, errno) + + return (int(res), int(errno[0]), result_p) +@@ -56,13 +57,14 @@ def set_group_dict(res, result_p): + return dict() + + group_dict = dict() +- group_dict['name'] = result_p[0].gr_name ++ group_dict['name'] = result_p[0].gr_name.decode('utf-8') + group_dict['gid'] = result_p[0].gr_gid + group_dict['mem'] = list() + + i = 0 + while result_p[0].gr_mem[i] != None: +- group_dict['mem'].append(result_p[0].gr_mem[i]) ++ grp_name = result_p[0].gr_mem[i].decode('utf-8') ++ group_dict['mem'].append(grp_name) + i = i+1 + + return group_dict +diff --git a/src/tests/intg/sssd_passwd.py b/src/tests/intg/sssd_passwd.py +index 8b741ea8c27efe2081ee200e6af02c322c5d53bc..f285b4971d0d9e826bf6cb38ebefeaf1b4422187 100644 +--- a/src/tests/intg/sssd_passwd.py ++++ b/src/tests/intg/sssd_passwd.py +@@ -38,13 +38,13 @@ def set_user_dict(res, result_p): + return dict() + + user_dict = dict() +- user_dict['name'] = result_p[0].pw_name +- user_dict['passwd'] = result_p[0].pw_passwd ++ user_dict['name'] = result_p[0].pw_name.decode('utf-8') ++ user_dict['passwd'] = result_p[0].pw_passwd.decode('utf-8') + user_dict['uid'] = result_p[0].pw_uid + user_dict['gid'] = result_p[0].pw_gid +- user_dict['gecos'] = result_p[0].pw_gecos +- user_dict['dir'] = result_p[0].pw_dir +- user_dict['shell'] = result_p[0].pw_shell ++ user_dict['gecos'] = result_p[0].pw_gecos.decode('utf-8') ++ user_dict['dir'] = result_p[0].pw_dir.decode('utf-8') ++ user_dict['shell'] = result_p[0].pw_shell.decode('utf-8') + return user_dict + + +@@ -64,6 +64,7 @@ def getpwnam_r(name, result_p, buffer_p, buflen): + + errno = POINTER(c_int)(c_int(0)) + ++ name = name.encode('utf-8') + res = func(c_char_p(name), result_p, buffer_p, buflen, errno) + + return (int(res), int(errno[0]), result_p) +-- +2.9.3 + diff --git a/0069-DYNDNS-Update-PTR-record-after-non-fatal-error.patch b/0069-DYNDNS-Update-PTR-record-after-non-fatal-error.patch new file mode 100644 index 0000000..3c838ee --- /dev/null +++ b/0069-DYNDNS-Update-PTR-record-after-non-fatal-error.patch @@ -0,0 +1,36 @@ +From fccd8f9ab7a0ac9868c43ea0e8c3af142b2809fa Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Mon, 24 Oct 2016 15:46:50 -0400 +Subject: [PATCH 69/79] DYNDNS: Update PTR record after non-fatal error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Continue to send PTR record update in situations where the nsupdate +child forward zone updates are successful but nsupdate returns non-zero + +Resolves: +https://fedorahosted.org/sssd/ticket/3227 + +Reviewed-by: Fabiano Fidêncio +--- + src/providers/ldap/sdap_dyndns.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c +index 83ec05183de9c429f6576c4e2e0fb769c9470a2f..9d28b57589b85a56abf363880bd310bb63ebd8d2 100644 +--- a/src/providers/ldap/sdap_dyndns.c ++++ b/src/providers/ldap/sdap_dyndns.c +@@ -381,9 +381,6 @@ sdap_dyndns_update_done(struct tevent_req *subreq) + return; + } + } +- +- tevent_req_error(req, ret); +- return; + } + + if (state->update_ptr == false) { +-- +2.9.3 + diff --git a/0070-DYNDNS-Correct-debug-log-message-of-realm.patch b/0070-DYNDNS-Correct-debug-log-message-of-realm.patch new file mode 100644 index 0000000..09e7ee2 --- /dev/null +++ b/0070-DYNDNS-Correct-debug-log-message-of-realm.patch @@ -0,0 +1,43 @@ +From d694d4fdcc81f24c2f9e3bb5a0dbe0a52498f196 Mon Sep 17 00:00:00 2001 +From: Justin Stephenson +Date: Mon, 24 Oct 2016 18:04:11 -0400 +Subject: [PATCH 70/79] DYNDNS: Correct debug log message of realm +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the realm is not added to the nsupdate message, the SSSD Debug log +message should inform about utilizing autodiscovered realm. + +Resolves: +https://fedorahosted.org/sssd/ticket/3220 + +Reviewed-by: Fabiano Fidêncio +--- + src/providers/be_dyndns.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/providers/be_dyndns.c b/src/providers/be_dyndns.c +index 07dc3339d2f65874504de60b8d3f2e63a8d7045f..ee264156824d7c5ab27c919ae0c56bbd6c0bc54f 100644 +--- a/src/providers/be_dyndns.c ++++ b/src/providers/be_dyndns.c +@@ -435,11 +435,15 @@ nsupdate_msg_create_common(TALLOC_CTX *mem_ctx, const char *realm, + /* Add the server, realm and headers */ + update_msg = talloc_asprintf(tmp_ctx, "server %s\n%s", + servername, realm_directive); +- } else { ++ } else if (realm != NULL) { + DEBUG(SSSDBG_FUNC_DATA, + "Creating update message for realm [%s].\n", realm); + /* Add the realm headers */ + update_msg = talloc_asprintf(tmp_ctx, "%s", realm_directive); ++ } else { ++ DEBUG(SSSDBG_FUNC_DATA, ++ "Creating update message for auto-discovered realm.\n"); ++ update_msg = talloc_asprintf(tmp_ctx, "%s", realm_directive); + } + talloc_free(realm_directive); + if (update_msg == NULL) { +-- +2.9.3 + diff --git a/0071-sdap_extend_map-make-sure-memory-can-be-freed.patch b/0071-sdap_extend_map-make-sure-memory-can-be-freed.patch new file mode 100644 index 0000000..87e11ee --- /dev/null +++ b/0071-sdap_extend_map-make-sure-memory-can-be-freed.patch @@ -0,0 +1,83 @@ +From 08bf6b4a281ef4308119dccbba4e86cf28b505d2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 4 Nov 2016 17:13:30 +0100 +Subject: [PATCH 71/79] sdap_extend_map: make sure memory can be freed + +If there is an error after calling talloc_realloc() the caller cannot +free the memory properly because neither src_map nor _map were pointing +to a valid memory location. With this patch _map will always point to +the current valid location so that it can always be used with +talloc_free(). + +Reviewed-by: Petr Cech +--- + src/providers/ldap/sdap.c | 4 ++-- + src/providers/ldap/sdap.h | 21 +++++++++++++++++++++ + 2 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index bfb7fc6d2a38debf56acae18b8e7eb7a08ccbd1b..342667aae6cc0b6c3df8c76f11eb2445e6bad0be 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -162,9 +162,9 @@ int sdap_extend_map(TALLOC_CTX *memctx, + char *sysdb_attr; + errno_t ret; + ++ *_map = src_map; + if (extra_attrs == NULL) { + DEBUG(SSSDBG_FUNC_DATA, "No extra attributes\n"); +- *_map = src_map; + *_new_size = num_entries; + return EOK; + } +@@ -177,6 +177,7 @@ int sdap_extend_map(TALLOC_CTX *memctx, + if (map == NULL) { + return ENOMEM; + } ++ *_map = map; + + for (i = 0; *extra_attrs != NULL; extra_attrs++) { + ret = split_extra_attr(map, *extra_attrs, &sysdb_attr, &ldap_attr); +@@ -221,7 +222,6 @@ int sdap_extend_map(TALLOC_CTX *memctx, + /* Sentinel */ + memset(&map[num_entries+nextra], 0, sizeof(struct sdap_attr_map)); + +- *_map = map; + *_new_size = num_entries + nextra; + return EOK; + } +diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h +index 6d4543ed48ce19f82252d34b6d0833a546a81bb9..6079a8bf62d0bdf23c8d462dc0f19c705e391a6e 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -512,6 +512,27 @@ int sdap_copy_map(TALLOC_CTX *memctx, + int num_entries, + struct sdap_attr_map **_map); + ++/** ++ * @brief Add attributes to a map ++ * ++ * sdap_extend_map() will call talloc_realloc() on the second argument so the ++ * original storage location might change. The return value _map will always ++ * contain the current memory location which can be used with talloc_free() ++ * even if there is an error. ++ * ++ * @param[in] memctx Talloc memory context ++ * @param[in] src_map Original map, should not be accessed anymore ++ * @param[in] num_entries Number of entries in the original map ++ * @param[in] extra_attrs NULL-terminated array of extra attribute pairs ++ * sysdb_attr:ldap_attr ++ * @param[out] _map New map ++ * @param[out] _new_size Number of entries in the new map ++ * ++ * @return ++ * - EOK success ++ * - ENOMEM memory allocation failed ++ * - ERR_DUP_EXTRA_ATTR sysdb attribute is already used ++ */ + int sdap_extend_map(TALLOC_CTX *memctx, + struct sdap_attr_map *src_map, + size_t num_entries, +-- +2.9.3 + diff --git a/0072-check_duplicate-check-name-member-before-using-it.patch b/0072-check_duplicate-check-name-member-before-using-it.patch new file mode 100644 index 0000000..3e31752 --- /dev/null +++ b/0072-check_duplicate-check-name-member-before-using-it.patch @@ -0,0 +1,79 @@ +From 454cf0c3808a9f6a0c9f79e9796e17c58907ee6c Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 4 Nov 2016 17:17:13 +0100 +Subject: [PATCH 72/79] check_duplicate: check name member before using it + +Resolves https://fedorahosted.org/sssd/ticket/3231 + +Reviewed-by: Petr Cech +--- + src/providers/ldap/sdap.c | 2 +- + src/tests/ipa_ldap_opt-tests.c | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index 342667aae6cc0b6c3df8c76f11eb2445e6bad0be..d562a96e252e20051c2b4eabffd3205a8575cc72 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -137,7 +137,7 @@ static enum duplicate_t check_duplicate(struct sdap_attr_map *map, + + for (i = 0; i < num_entries; i++) { + if (strcmp(map[i].sys_name, sysdb_attr) == 0) { +- if (strcmp(map[i].name, ldap_attr) == 0) { ++ if (map[i].name != NULL && strcmp(map[i].name, ldap_attr) == 0) { + return ALREADY_IN_MAP; + } else { + return CONFLICT_WITH_MAP; +diff --git a/src/tests/ipa_ldap_opt-tests.c b/src/tests/ipa_ldap_opt-tests.c +index 622a617c3b55495b41bffa1868497d302cc1f1e3..30e09f75b788332858231177017f90c35e21229f 100644 +--- a/src/tests/ipa_ldap_opt-tests.c ++++ b/src/tests/ipa_ldap_opt-tests.c +@@ -467,6 +467,37 @@ START_TEST(test_extra_opts_dup) + extra_attrs, + &out_map, &new_size); + fail_unless(ret == ERR_DUP_EXTRA_ATTR, "[%s]", sss_strerror(ret)); ++ ++ talloc_free(out_map); ++} ++END_TEST ++ ++START_TEST(test_extra_opts_empty_name) ++{ ++ errno_t ret; ++ char *extra_attrs[] = { discard_const(SYSDB_UUID":bar"), ++ NULL }; ++ struct sdap_attr_map *in_map; ++ struct sdap_attr_map *out_map; ++ size_t new_size; ++ ++ ret = sdap_copy_map(global_talloc_context, rfc2307_user_map, ++ SDAP_OPTS_USER, &in_map); ++ fail_unless(ret == EOK, "[%s]", strerror(ret)); ++ ++ /* Make sure the name if really NULL */ ++ fail_unless(rfc2307_user_map[SDAP_AT_USER_UUID].name == NULL, ++ "The reference name is not NULL anymore, " ++ "please choose a different attribute."); ++ ++ ret = sdap_extend_map(global_talloc_context, ++ in_map, ++ SDAP_OPTS_USER, ++ extra_attrs, ++ &out_map, &new_size); ++ fail_unless(ret == ERR_DUP_EXTRA_ATTR, "[%s]", sss_strerror(ret)); ++ ++ talloc_free(out_map); + } + END_TEST + +@@ -500,6 +531,7 @@ Suite *ipa_ldap_opt_suite (void) + tcase_add_test (tc_extra_opts, test_no_extra_opts); + tcase_add_test (tc_extra_opts, test_extra_opts_neg); + tcase_add_test (tc_extra_opts, test_extra_opts_dup); ++ tcase_add_test (tc_extra_opts, test_extra_opts_empty_name); + suite_add_tcase (s, tc_extra_opts); + + return s; +-- +2.9.3 + diff --git a/0073-FILES-Fix-reallocation-logic.patch b/0073-FILES-Fix-reallocation-logic.patch new file mode 100644 index 0000000..dfbaa83 --- /dev/null +++ b/0073-FILES-Fix-reallocation-logic.patch @@ -0,0 +1,167 @@ +From fc91d72f32660712f7c9e872e00deb91f188fea3 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Feb 2017 22:14:35 +0100 +Subject: [PATCH 73/79] FILES: Fix reallocation logic +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There were two bugs in the files provider reallocation logic: + 1) the reallocated array was not NULL-terminated properly + 2) talloc_get_size was used in place of talloc_array_length + +This bug could have resulted in a crash when the passwd or groups file +contained more than FILES_REALLOC_CHUNK entries. + +Reviewed-by: Pavel Březina +--- + src/providers/files/files_ops.c | 9 +++-- + src/tests/intg/test_files_provider.py | 66 ++++++++++++++++++++++++++++++++++- + 2 files changed, 72 insertions(+), 3 deletions(-) + +diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c +index beda47abd75efd1d399ce074cceb9ea9f4b1399f..9ebf3b11bdb6be60c03392a74a0d434c90808782 100644 +--- a/src/providers/files/files_ops.c ++++ b/src/providers/files/files_ops.c +@@ -27,6 +27,9 @@ + #include "util/inotify.h" + #include "util/util.h" + ++/* When changing this constant, make sure to also adjust the files integration ++ * test for reallocation branch ++ */ + #define FILES_REALLOC_CHUNK 64 + + #define PWD_MAXSIZE 1024 +@@ -108,7 +111,7 @@ static errno_t enum_files_users(TALLOC_CTX *mem_ctx, + users = talloc_realloc(mem_ctx, + users, + struct passwd *, +- talloc_get_size(users) + FILES_REALLOC_CHUNK); ++ talloc_array_length(users) + FILES_REALLOC_CHUNK); + if (users == NULL) { + ret = ENOMEM; + goto done; +@@ -117,6 +120,7 @@ static errno_t enum_files_users(TALLOC_CTX *mem_ctx, + } + + ret = EOK; ++ users[n_users] = NULL; + *_users = users; + done: + if (ret != EOK) { +@@ -211,7 +215,7 @@ static errno_t enum_files_groups(TALLOC_CTX *mem_ctx, + groups = talloc_realloc(mem_ctx, + groups, + struct group *, +- talloc_get_size(groups) + FILES_REALLOC_CHUNK); ++ talloc_array_length(groups) + FILES_REALLOC_CHUNK); + if (groups == NULL) { + ret = ENOMEM; + goto done; +@@ -220,6 +224,7 @@ static errno_t enum_files_groups(TALLOC_CTX *mem_ctx, + } + + ret = EOK; ++ groups[n_groups] = NULL; + *_groups = groups; + done: + if (ret != EOK) { +diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py +index 0a2e5a7f4da8b27bc410c401baf4f5f226c2a301..528b5e5acc5078cce5bfab9a5f9dab5509e580eb 100644 +--- a/src/tests/intg/test_files_provider.py ++++ b/src/tests/intg/test_files_provider.py +@@ -34,6 +34,9 @@ from sssd_group import call_sssd_getgrnam + from files_ops import passwd_ops_setup, group_ops_setup + from util import unindent + ++# Sync this with files_ops.c ++FILES_REALLOC_CHUNK = 64 ++ + CANARY = dict(name='canary', passwd='x', uid=100001, gid=200001, + gecos='Used to check if passwd is resolvable', + dir='/home/canary', +@@ -204,7 +207,7 @@ def sssd_id_sync(name): + # Helper functions + def user_generator(seqnum): + return dict(name='user%d' % seqnum, +- passwd='*', ++ passwd='x', + uid=10000 + seqnum, + gid=20000 + seqnum, + gecos='User for tests', +@@ -221,6 +224,12 @@ def check_user(exp_user, delay=1.0): + assert found_user == exp_user + + ++def group_generator(seqnum): ++ return dict(name='group%d' % seqnum, ++ gid=30000 + seqnum, ++ mem=[]) ++ ++ + def check_group(exp_group, delay=1.0): + if delay > 0: + time.sleep(delay) +@@ -690,3 +699,58 @@ def test_getgrnam_add_remove_ghosts(setup_pw_with_canary, + assert res == sssd_id.NssReturnCode.SUCCESS + assert len(groups) == 2 + assert 'group_nomem' in groups ++ ++ ++def realloc_users(pwd_ops, num): ++ # Intentionally not including the the last one because ++ # canary is added first ++ for i in range(1, num): ++ user = user_generator(i) ++ pwd_ops.useradd(**user) ++ ++ user = user_generator(num-1) ++ check_user(user) ++ ++ ++def test_realloc_users_exact(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that returning exactly FILES_REALLOC_CHUNK users (see files_ops.c) ++ works fine to test reallocation logic. Test exact number of users to ++ check for off-by-one errors. ++ """ ++ realloc_users(setup_pw_with_canary, FILES_REALLOC_CHUNK) ++ ++ ++def test_realloc_users(setup_pw_with_canary, files_domain_only): ++ """ ++ Test that returning exactly FILES_REALLOC_CHUNK users (see files_ops.c) ++ works fine to test reallocation logic. ++ """ ++ realloc_users(setup_pw_with_canary, FILES_REALLOC_CHUNK*3) ++ ++ ++def realloc_groups(grp_ops, num): ++ for i in range(1, num): ++ group = group_generator(i) ++ grp_ops.groupadd(**group) ++ ++ group = group_generator(num-1) ++ check_group(group) ++ ++ ++def test_realloc_groups_exact(setup_gr_with_canary, files_domain_only): ++ """ ++ Test that returning exactly FILES_REALLOC_CHUNK groups (see files_ops.c) ++ works fine to test reallocation logic. Test exact number of groups to ++ check for off-by-one errors. ++ """ ++ realloc_groups(setup_gr_with_canary, FILES_REALLOC_CHUNK*3) ++ ++ ++def test_realloc_groups(setup_gr_with_canary, files_domain_only): ++ """ ++ Test that returning exactly FILES_REALLOC_CHUNK groups (see files_ops.c) ++ works fine to test reallocation logic. Test exact number of groups to ++ check for off-by-one errors. ++ """ ++ realloc_groups(setup_gr_with_canary, FILES_REALLOC_CHUNK*3) +-- +2.9.3 + diff --git a/0074-pam_sss-check-conversation-callback.patch b/0074-pam_sss-check-conversation-callback.patch new file mode 100644 index 0000000..6f2be6f --- /dev/null +++ b/0074-pam_sss-check-conversation-callback.patch @@ -0,0 +1,47 @@ +From 0965a77c4ff0b358d24582955cb7ae375ebaa0d2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 22 Feb 2017 11:39:48 +0100 +Subject: [PATCH 74/79] pam_sss: check conversation callback +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this patch pam_sss checks if a conversation callback is available +before using it. + +Resolves https://fedorahosted.org/sssd/ticket/3296 + +Reviewed-by: Pavel Březina +--- + src/sss_client/pam_sss.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index b4175ae2c7fc1385a19f81045695bcd73d43f754..03613b8cd0cc3e3521f520e86d5e67371ed6ed48 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -205,6 +205,10 @@ static int do_pam_conversation(pam_handle_t *pamh, const int msg_style, + + 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; ++ } + + do { + pam_msg = malloc(sizeof(struct pam_message)); +@@ -1304,6 +1308,10 @@ static int prompt_2fa(pam_handle_t *pamh, struct pam_items *pi, + 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_fa1; +-- +2.9.3 + diff --git a/0075-MONITOR-Don-t-return-an-error-in-case-we-fail-to-reg.patch b/0075-MONITOR-Don-t-return-an-error-in-case-we-fail-to-reg.patch new file mode 100644 index 0000000..a72b3d9 --- /dev/null +++ b/0075-MONITOR-Don-t-return-an-error-in-case-we-fail-to-reg.patch @@ -0,0 +1,44 @@ +From 86bcc81a665dde4799d67ab7ea2bbd23608e7dab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= +Date: Fri, 3 Feb 2017 18:31:51 +0100 +Subject: [PATCH 75/79] MONITOR: Don't return an error in case we fail to + register a service +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This behaviour was mistakenly changed by the {dbus,socket}-activation +series and, as it's now, I've noticed the monitor may end up in some +weird state due to this change, where it doesn't stop properly and leave +some defuncts children processes. + +Let's change it back to what it was before and avoid possible +regressions (even if no regression where hit yet). + +Signed-off-by: Fabiano Fidêncio + +Reviewed-by: Pavel Březina +--- + src/monitor/monitor.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index bc7402103d8024391d2ee27f48b28247ff9cd49c..59bf70741b76871c4937ad75c448aaf776cc37eb 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -372,11 +372,7 @@ static int client_registration(struct sbus_request *dbus_req, void *data) + sbus_request_finish(dbus_req, NULL); + /* FIXME: should we just talloc_zfree(conn) ? */ + +- if (ret == ENOENT) { +- goto done; +- } +- +- return ret; ++ goto done; + } + + /* Fill in svc structure with connection data */ +-- +2.9.3 + diff --git a/0076-FILES-Remove-unnecessary-check.patch b/0076-FILES-Remove-unnecessary-check.patch new file mode 100644 index 0000000..48cb4e4 --- /dev/null +++ b/0076-FILES-Remove-unnecessary-check.patch @@ -0,0 +1,34 @@ +From 1f49be4429c17475b789e9089ce4d0ae48315e74 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Thu, 16 Feb 2017 09:15:29 +0100 +Subject: [PATCH 76/79] FILES: Remove unnecessary check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"grp_iter->gr_mem" is an array of strings and not just a string. +We tried to compare first string to NULL (acctually '\0') +But after that we iterated over the array to find count of members +and we check for NULL one more time. + +Reviewed-by: Fabiano Fidêncio +--- + src/providers/files/files_ops.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c +index 9ebf3b11bdb6be60c03392a74a0d434c90808782..370dcd971459069915178bac1fa3e87ecf2884ef 100644 +--- a/src/providers/files/files_ops.c ++++ b/src/providers/files/files_ops.c +@@ -189,7 +189,7 @@ static errno_t enum_files_groups(TALLOC_CTX *mem_ctx, + } + grp->gr_passwd = talloc_strdup(grp, grp_iter->gr_passwd); + +- if (grp_iter->gr_mem != NULL && grp_iter->gr_mem[0] != '\0') { ++ if (grp_iter->gr_mem != NULL) { + size_t nmem; + + for (nmem = 0; grp_iter->gr_mem[nmem] != NULL; nmem++); +-- +2.9.3 + diff --git a/0077-PAM-store-user-object-in-the-preq-context.patch b/0077-PAM-store-user-object-in-the-preq-context.patch new file mode 100644 index 0000000..7463855 --- /dev/null +++ b/0077-PAM-store-user-object-in-the-preq-context.patch @@ -0,0 +1,80 @@ +From f561c2bd3c72631ccb7ad6d0b5f6541b27b0922d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 19 Sep 2016 16:56:46 +0200 +Subject: [PATCH 77/95] PAM: store user object in the preq context + +Reviewed-by: Jakub Hrozek +--- + src/responder/pam/pamsrv.h | 1 + + src/responder/pam/pamsrv_cmd.c | 12 ++++++------ + 2 files changed, 7 insertions(+), 6 deletions(-) + +diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h +index 75045d03937bf71365b4d2654f4257e149087215..e3568123ac6e35d05d63b7b481dbcfbeddbd458c 100644 +--- a/src/responder/pam/pamsrv.h ++++ b/src/responder/pam/pamsrv.h +@@ -68,6 +68,7 @@ struct pam_auth_req { + + struct pam_auth_dp_req *dpreq_spy; + ++ struct ldb_message *user_obj; + struct ldb_message *cert_user_obj; + char *token_name; + }; +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index e73a819069d0fdf56327d96e38ecfa0585d52a68..e74dd1684ca0a853f4ff1f9f98bb0f9e8d3d219f 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1560,7 +1560,6 @@ static int pam_check_user_search(struct pam_auth_req *preq) + struct pam_ctx *pctx = + talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx); + static const char *user_attrs[] = SYSDB_PW_ATTRS; +- struct ldb_message *msg; + struct ldb_result *res; + const char *sysdb_name; + +@@ -1621,11 +1620,12 @@ static int pam_check_user_search(struct pam_auth_req *preq) + } + + if (preq->pd->name_is_upn) { +- ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg); ++ ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, ++ &preq->user_obj); + if (ret == EOK) { + /* Since sysdb_search_user_by_upn() searches the whole cache we + * have to set the domain so that it matches the result. */ +- sysdb_name = ldb_msg_find_attr_as_string(msg, ++ sysdb_name = ldb_msg_find_attr_as_string(preq->user_obj, + SYSDB_NAME, NULL); + if (sysdb_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cached entry has no name.\n"); +@@ -1654,7 +1654,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + } else if (res->count == 0) { + ret = ENOENT; + } else { +- msg = res->msgs[0]; ++ preq->user_obj = res->msgs[0]; + } + } + if (ret != EOK && ret != ENOENT) { +@@ -1693,7 +1693,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + + /* if we need to check the remote account go on */ + if (preq->check_provider) { +- cacheExpire = ldb_msg_find_attr_as_uint64(msg, ++ cacheExpire = ldb_msg_find_attr_as_uint64(preq->user_obj, + SYSDB_CACHE_EXPIRE, 0); + if (cacheExpire < time(NULL)) { + break; +@@ -1704,7 +1704,7 @@ static int pam_check_user_search(struct pam_auth_req *preq) + "Returning info for user [%s@%s]\n", name, dom->name); + + /* We might have searched by alias. Pass on the primary name */ +- ret = pd_set_primary_name(msg, preq->pd); ++ ret = pd_set_primary_name(preq->user_obj, preq->pd); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n"); + return ret; +-- +2.9.3 + diff --git a/0078-PAM-fix-memory-leak-in-pam_sss.patch b/0078-PAM-fix-memory-leak-in-pam_sss.patch new file mode 100644 index 0000000..bf6b3a6 --- /dev/null +++ b/0078-PAM-fix-memory-leak-in-pam_sss.patch @@ -0,0 +1,72 @@ +From 327a16652bbafbb77b5b90cc7abac3ded7c14364 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 25 Jan 2017 17:34:54 +0100 +Subject: [PATCH 78/95] PAM: fix memory leak in pam_sss + +Since there can be multiple rounds trips between the PAM client and SSSD +it might be possible that the same data is send multiple times by SSSD. +So before overriding the old data it should be freed. I've seen this +with the domain name which is send both in the pre-auth and the auth +responses. To be on the safe side I added free() for some other items as +well. + +Reviewed-by: Jakub Hrozek +--- + src/sss_client/pam_sss.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index 03613b8cd0cc3e3521f520e86d5e67371ed6ed48..8f97af77e3a0c2fa26c84962b6438fe774a75f1a 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -869,6 +869,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + break; + } + D(("domain name: [%s]", &buf[p])); ++ free(pi->domain_name); + pi->domain_name = strdup((char *) &buf[p]); + if (pi->domain_name == NULL) { + D(("strdup failed")); +@@ -937,6 +938,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + break; + } + ++ free(pi->otp_vendor); + pi->otp_vendor = strdup((char *) &buf[p]); + if (pi->otp_vendor == NULL) { + D(("strdup failed")); +@@ -950,6 +952,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + pi->otp_vendor = NULL; + break; + } ++ free(pi->otp_token_id); + pi->otp_token_id = strdup((char *) &buf[p + offset]); + if (pi->otp_token_id == NULL) { + D(("strdup failed")); +@@ -963,6 +966,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + pi->otp_token_id = NULL; + break; + } ++ free(pi->otp_challenge); + pi->otp_challenge = strdup((char *) &buf[p + offset]); + if (pi->otp_challenge == NULL) { + D(("strdup failed")); +@@ -976,6 +980,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + break; + } + ++ free(pi->cert_user); + pi->cert_user = strdup((char *) &buf[p]); + if (pi->cert_user == NULL) { + D(("strdup failed")); +@@ -1010,6 +1015,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + pi->cert_user = NULL; + break; + } ++ free(pi->token_name); + pi->token_name = strdup((char *) &buf[p + offset]); + if (pi->token_name == NULL) { + D(("strdup failed")); +-- +2.9.3 + diff --git a/0079-PAM-use-sentinel-error-code-in-PAM-tests.patch b/0079-PAM-use-sentinel-error-code-in-PAM-tests.patch new file mode 100644 index 0000000..8145db9 --- /dev/null +++ b/0079-PAM-use-sentinel-error-code-in-PAM-tests.patch @@ -0,0 +1,28 @@ +From 254f3898cc9fb9d76e12d72a2955906c49748e6d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Jan 2017 18:18:23 +0100 +Subject: [PATCH 79/95] PAM: use sentinel error code in PAM tests + +Reviewed-by: Jakub Hrozek +--- + src/tests/cmocka/test_pam_srv.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index 3b8327eb3acbb35cbd3e1a3cd4d8f7dc06b82c9f..e6ed8f509b5b69978f80c158e52bfea738909465 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -883,7 +883,9 @@ void test_pam_open_session(void **state) + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_OPEN_SESSION); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + +- pam_test_ctx->exp_pam_status = PAM_NO_MODULE_DATA; ++ /* make sure pam_status is not touched by setting it to a value which is ++ * not used by SSSD. */ ++ pam_test_ctx->exp_pam_status = _PAM_RETURN_VALUES; + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_OPEN_SESSION, + pam_test_ctx->pam_cmds); +-- +2.9.3 + diff --git a/0080-utils-new-error-codes.patch b/0080-utils-new-error-codes.patch new file mode 100644 index 0000000..0ac6640 --- /dev/null +++ b/0080-utils-new-error-codes.patch @@ -0,0 +1,44 @@ +From d4757440418c7b73bbecec7e40baf6dfe8cc9460 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Jan 2017 18:04:11 +0100 +Subject: [PATCH 80/95] utils: new error codes + +ERR_SC_AUTH_NOT_SUPPORTED can be used by backends to indicate that +Smartcard authentication is not supported. ERR_NO_AUTH_METHOD_AVAILABLE +can be used by backends that no authentication method was found. + +Reviewed-by: Jakub Hrozek +--- + src/util/util_errors.c | 2 ++ + src/util/util_errors.h | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/src/util/util_errors.c b/src/util/util_errors.c +index 88ebf4e301f7194ccea920767141f838cbd8dc56..17388c997db5315c2491af1021e75aff07632488 100644 +--- a/src/util/util_errors.c ++++ b/src/util/util_errors.c +@@ -102,6 +102,8 @@ struct err_string error_to_str[] = { + { "No proxy server for secrets available"}, /* ERR_SEC_NO_PROXY */ + { "The maximum number of stored secrets has been reached" }, /* ERR_SEC_INVALID_TOO_MANY_SECRETS */ + { "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 */ + { "ERR_LAST" } /* ERR_LAST */ + }; + +diff --git a/src/util/util_errors.h b/src/util/util_errors.h +index 525983f21cfb7aaa299ed78f672161e6d0664ad0..7aacad26084a3a2af6333988f07db865f6a4d299 100644 +--- a/src/util/util_errors.h ++++ b/src/util/util_errors.h +@@ -124,6 +124,8 @@ enum sssd_errors { + ERR_SEC_NO_PROXY, + ERR_SEC_INVALID_TOO_MANY_SECRETS, + ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE, ++ ERR_NO_AUTH_METHOD_AVAILABLE, ++ ERR_SC_AUTH_NOT_SUPPORTED, + ERR_LAST /* ALWAYS LAST */ + }; + +-- +2.9.3 + diff --git a/0081-LDAP-proxy-tell-frontend-that-Smartcard-auth-is-not-.patch b/0081-LDAP-proxy-tell-frontend-that-Smartcard-auth-is-not-.patch new file mode 100644 index 0000000..a8d046c --- /dev/null +++ b/0081-LDAP-proxy-tell-frontend-that-Smartcard-auth-is-not-.patch @@ -0,0 +1,63 @@ +From f70d946f8cde55b6bdc09345e22849842bca4387 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 25 Jan 2017 20:29:43 +0100 +Subject: [PATCH 81/95] LDAP/proxy: tell frontend that Smartcard auth is not + supported + +Reviewed-by: Jakub Hrozek +--- + src/providers/ldap/ldap_auth.c | 11 ++++++++++- + src/providers/proxy/proxy_auth.c | 8 ++++++++ + 2 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c +index 00d38284e428eea42254820fd08ee4fb125235a6..00ddd889b6294e457c13218491547b84f1468266 100644 +--- a/src/providers/ldap/ldap_auth.c ++++ b/src/providers/ldap/ldap_auth.c +@@ -645,7 +645,13 @@ static struct tevent_req *auth_send(TALLOC_CTX *memctx, + + /* The token must be a password token */ + if (sss_authtok_get_type(authtok) != SSS_AUTHTOK_TYPE_PASSWORD) { +- tevent_req_error(req, ERR_AUTH_FAILED); ++ if (sss_authtok_get_type(authtok) == SSS_AUTHTOK_TYPE_SC_PIN ++ || sss_authtok_get_type(authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) { ++ /* Tell frontend that we do not support Smartcard authentication */ ++ tevent_req_error(req, ERR_SC_AUTH_NOT_SUPPORTED); ++ } else { ++ tevent_req_error(req, ERR_AUTH_FAILED); ++ } + return tevent_req_post(req, ev); + } + +@@ -1028,6 +1034,9 @@ static void sdap_pam_auth_handler_done(struct tevent_req *subreq) + state->pd->account_locked = true; + state->pd->pam_status = PAM_PERM_DENIED; + break; ++ case ERR_SC_AUTH_NOT_SUPPORTED: ++ state->pd->pam_status = PAM_BAD_ITEM; ++ break; + default: + state->pd->pam_status = PAM_SYSTEM_ERR; + break; +diff --git a/src/providers/proxy/proxy_auth.c b/src/providers/proxy/proxy_auth.c +index 2b3510c38b1cb265e3042425c373f39e524a71eb..e53b38e666343f8530593d5eac21787ce7cb46e1 100644 +--- a/src/providers/proxy/proxy_auth.c ++++ b/src/providers/proxy/proxy_auth.c +@@ -737,6 +737,14 @@ proxy_pam_handler_send(TALLOC_CTX *mem_ctx, + state->auth_ctx = proxy_auth_ctx; + state->be_ctx = params->be_ctx; + ++ /* Tell frontend that we do not support Smartcard authentication */ ++ if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN ++ || sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) { ++ pd->pam_status = PAM_BAD_ITEM; ++ goto immediately; ++ } ++ ++ + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: + case SSS_PAM_CHAUTHTOK: +-- +2.9.3 + diff --git a/0082-authtok-enhance-support-for-Smartcard-auth-blobs.patch b/0082-authtok-enhance-support-for-Smartcard-auth-blobs.patch new file mode 100644 index 0000000..9ec0cfd --- /dev/null +++ b/0082-authtok-enhance-support-for-Smartcard-auth-blobs.patch @@ -0,0 +1,775 @@ +From dd17a3aaddab6f122dff3bd15b7005464c07c0ea Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 23 Sep 2016 17:11:35 +0200 +Subject: [PATCH 82/95] authtok: enhance support for Smartcard auth blobs + +The blobs contains beside the PIN the name of the PKCS#11 module and the +token name where the certificate of the user was found and the key id. +Those data will be used e.g. by the pkinit module to make sure them +right certificate is used. + +Reviewed-by: Jakub Hrozek +--- + src/tests/cmocka/test_authtok.c | 89 ++++++++++- + src/util/authtok-utils.c | 91 ++++++++++++ + src/util/authtok-utils.h | 56 +++++++ + src/util/authtok.c | 320 ++++++++++++++++++++++++++++++++++++++-- + src/util/authtok.h | 84 +++++++++++ + 5 files changed, 625 insertions(+), 15 deletions(-) + +diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c +index 30dcc9c8401103a275bd592fe8afd2c2f396ffb1..b2ef08a1c75625253706910e0cc80f4d5d28be5d 100644 +--- a/src/tests/cmocka/test_authtok.c ++++ b/src/tests/cmocka/test_authtok.c +@@ -437,6 +437,85 @@ void test_sss_authtok_2fa_blobs(void **state) + talloc_free(fa2); + } + ++void test_sss_authtok_sc_blobs(void **state) ++{ ++ int ret; ++ struct test_state *ts; ++ size_t needed_size; ++ uint8_t *buf; ++ const char *pin; ++ size_t pin_len; ++ const char *token_name; ++ size_t token_name_len; ++ const char *module_name; ++ size_t module_name_len; ++ const char *key_id; ++ size_t key_id_len; ++ ++ ts = talloc_get_type_abort(*state, struct test_state); ++ ++ ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0, ++ NULL, 0, &needed_size); ++ assert_int_equal(ret, EAGAIN); ++ ++ buf = talloc_size(ts, needed_size); ++ assert_non_null(buf); ++ ++ ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0, ++ buf, needed_size, &needed_size); ++ assert_int_equal(ret, EOK); ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++ assert_memory_equal(buf, "\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0abc\0defg\0hijkl\0mnopqr\0", ++ needed_size); ++#else ++ assert_memory_equal(buf, "\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7abc\0defg\0hijkl\0mnopqr\0", ++ needed_size); ++#endif ++ ++ ret = sss_authtok_set(ts->authtoken, SSS_AUTHTOK_TYPE_SC_PIN, buf, ++ needed_size); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_authtok_get_sc(ts->authtoken, &pin, &pin_len, ++ &token_name, &token_name_len, ++ &module_name, &module_name_len, ++ &key_id, &key_id_len); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(pin_len, 3); ++ assert_string_equal(pin, "abc"); ++ assert_int_equal(token_name_len, 4); ++ assert_string_equal(token_name, "defg"); ++ assert_int_equal(module_name_len, 5); ++ assert_string_equal(module_name, "hijkl"); ++ assert_int_equal(key_id_len, 6); ++ assert_string_equal(key_id, "mnopqr"); ++ ++ ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL, ++ &token_name, &token_name_len, ++ &module_name, &module_name_len, ++ &key_id, &key_id_len); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(token_name_len, 4); ++ assert_string_equal(token_name, "defg"); ++ assert_int_equal(module_name_len, 5); ++ assert_string_equal(module_name, "hijkl"); ++ assert_int_equal(key_id_len, 6); ++ assert_string_equal(key_id, "mnopqr"); ++ ++ ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL, ++ &token_name, NULL, ++ &module_name, NULL, ++ &key_id, NULL); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(token_name, "defg"); ++ assert_string_equal(module_name, "hijkl"); ++ assert_string_equal(key_id, "mnopqr"); ++ ++ sss_authtok_set_empty(ts->authtoken); ++ talloc_free(buf); ++} ++ + #define MISSING_NULL_CHECK do { \ + assert_int_equal(ret, EOK); \ + assert_int_equal(fa1_len, 3); \ +@@ -524,8 +603,8 @@ void test_sss_authtok_sc_pin(void **state) + assert_int_equal(sss_authtok_get_type(ts->authtoken), + SSS_AUTHTOK_TYPE_SC_PIN); + size = sss_authtok_get_size(ts->authtoken); +- assert_int_equal(size, 9); +- assert_memory_equal(sss_authtok_get_data(ts->authtoken), "12345678\0", ++ assert_int_equal(size, 28); ++ assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\0\0\0", + size); + + ret = sss_authtok_set_sc_pin(ts->authtoken, "12345678", 5); +@@ -533,8 +612,8 @@ void test_sss_authtok_sc_pin(void **state) + assert_int_equal(sss_authtok_get_type(ts->authtoken), + SSS_AUTHTOK_TYPE_SC_PIN); + size = sss_authtok_get_size(ts->authtoken); +- assert_int_equal(size, 6); +- assert_memory_equal(sss_authtok_get_data(ts->authtoken), "12345\0", ++ assert_int_equal(size, 25); ++ assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\0\0\0", + size); + + ret = sss_authtok_get_sc_pin(ts->authtoken, &pin, &len); +@@ -592,6 +671,8 @@ int main(int argc, const char *argv[]) + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_sc_pin, + setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_authtok_sc_blobs, ++ setup, teardown), + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ +diff --git a/src/util/authtok-utils.c b/src/util/authtok-utils.c +index 65fba9022db11786c0c7e4dcab6fec89c9e0cb19..e7123df341ef0f1954b1202921124e9144909fc1 100644 +--- a/src/util/authtok-utils.c ++++ b/src/util/authtok-utils.c +@@ -72,3 +72,94 @@ errno_t sss_auth_pack_2fa_blob(const char *fa1, size_t fa1_len, + + return 0; + } ++ ++errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len, ++ const char *token_name, size_t token_name_len, ++ const char *module_name, size_t module_name_len, ++ const char *key_id, size_t key_id_len, ++ uint8_t *buf, size_t buf_len, ++ size_t *_sc_blob_len) ++{ ++ size_t c; ++ uint32_t tmp_uint32_t; ++ ++ if (pin_len > UINT32_MAX || token_name_len > UINT32_MAX ++ || module_name_len > UINT32_MAX ++ || (pin_len != 0 && pin == NULL) ++ || (token_name_len != 0 && token_name == NULL) ++ || (module_name_len != 0 && module_name == NULL) ++ || (key_id_len != 0 && key_id == NULL)) { ++ return EINVAL; ++ } ++ ++ /* A missing pin is ok in the case of a reader with a keyboard */ ++ if (pin == NULL) { ++ pin = ""; ++ pin_len = 0; ++ } ++ ++ if (token_name == NULL) { ++ token_name = ""; ++ token_name_len = 0; ++ } ++ ++ if (module_name == NULL) { ++ module_name = ""; ++ module_name_len = 0; ++ } ++ ++ if (key_id == NULL) { ++ key_id = ""; ++ key_id_len = 0; ++ } ++ ++ /* len should not include the trailing \0 */ ++ if (pin_len == 0 || pin[pin_len - 1] == '\0') { ++ pin_len = strlen(pin); ++ } ++ ++ if (token_name_len == 0 || token_name[token_name_len - 1] == '\0') { ++ token_name_len = strlen(token_name); ++ } ++ ++ if (module_name_len == 0 || module_name[module_name_len - 1] == '\0') { ++ module_name_len = strlen(module_name); ++ } ++ ++ if (key_id_len == 0 || key_id[key_id_len - 1] == '\0') { ++ key_id_len = strlen(key_id); ++ } ++ ++ *_sc_blob_len = pin_len + token_name_len + module_name_len + key_id_len + 4 ++ + 4 * sizeof(uint32_t); ++ if (buf == NULL || buf_len < *_sc_blob_len) { ++ return EAGAIN; ++ } ++ ++ c = 0; ++ tmp_uint32_t = (uint32_t) pin_len + 1; ++ SAFEALIGN_COPY_UINT32(buf, &tmp_uint32_t, &c); ++ tmp_uint32_t = (uint32_t) token_name_len + 1; ++ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c); ++ tmp_uint32_t = (uint32_t) module_name_len + 1; ++ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c); ++ tmp_uint32_t = (uint32_t) key_id_len + 1; ++ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c); ++ ++ memcpy(buf + c, pin, pin_len); ++ buf[c + pin_len] = '\0'; ++ c += pin_len + 1; ++ ++ memcpy(buf + c, token_name, token_name_len); ++ buf[c + token_name_len] = '\0'; ++ c += token_name_len + 1; ++ ++ memcpy(buf + c, module_name, module_name_len); ++ buf[c + module_name_len] = '\0'; ++ c += module_name_len + 1; ++ ++ memcpy(buf + c, key_id, key_id_len); ++ buf[c + key_id_len] = '\0'; ++ ++ return 0; ++} +diff --git a/src/util/authtok-utils.h b/src/util/authtok-utils.h +index 07aef3c18395d6e967289f6e345f27e9ee868da2..c5aace39feea0b3c7e24c7ea80206e663712a11c 100644 +--- a/src/util/authtok-utils.h ++++ b/src/util/authtok-utils.h +@@ -25,6 +25,37 @@ + #include "sss_client/sss_cli.h" + + /** ++ * @brief Fill memory buffer with Smartcard authentication blob ++ * ++ * @param[in] pin PIN, null terminated ++ * @param[in] pin_len Length of the PIN, if 0 ++ * strlen() will be called internally ++ * @param[in] token_name Token name, null terminated ++ * @param[in] token_name_len Length of the token name, if 0 ++ * strlen() will be called internally ++ * @param[in] module_name Name of PKCS#11 module, null terminated ++ * @param[in] module_name_len Length of the module name, if 0 ++ * strlen() will be called internally ++ * @param[in] key_id Key ID of the certificate ++ * @param[in] key_id_len Length of the key id of the certificate, if 0 ++ * strlen() will be called internally ++ * @param[in] buf memory buffer of size buf_len, may be NULL ++ * @param[in] buf_len size of memory buffer buf ++ * ++ * @param[out] _sc_blob len size of the Smartcard authentication blob ++ * ++ * @return EOK on success ++ * EINVAL if input data is not consistent ++ * EAGAIN if provided buffer is too small, _sc_blob_len ++ * contains the size needed to store the SC blob ++ */ ++errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len, ++ const char *token_name, size_t token_name_len, ++ const char *module_name, size_t module_name_len, ++ const char *key_id, size_t key_id_len, ++ uint8_t *buf, size_t buf_len, ++ size_t *_sc_blob_len); ++/** + * @brief Fill memory buffer with 2FA blob + * + * @param[in] fa1 First authentication factor, null terminated +@@ -67,4 +98,29 @@ errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx, + const uint8_t *blob, size_t blob_len, + char **fa1, size_t *_fa1_len, + char **fa2, size_t *_fa2_len); ++ ++/** ++ * @brief Extract SC data from memory buffer ++ * ++ * @param[in] mem_ctx Talloc memory context to allocate the 2FA ++ * data on ++ * @param[in] blob Memory buffer containing the 2FA data ++ * @param[in] blob_len Size of the memory buffer ++ * @param[out] _pin PIN, null terminated ++ * @param[out] _pin_len Length of the PIN ++ * @param[out] _token_name Token name, null terminated ++ * @param[out] _token_name_len Length of the token name ++ * @param[out] _module_name Name of PKCS#11 module, null terminated ++ * @param[out] _module_name_len Length of the module name ++ * ++ * @return EOK on success ++ * EINVAL if input data is not consistent ++ * EINVAL if no memory can be allocated ++ */ ++errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx, ++ const uint8_t *blob, size_t blob_len, ++ char **pin, size_t *_pin_len, ++ char **token_name, size_t *_token_name_len, ++ char **module_name, size_t *_module_name_len, ++ char **key_id, size_t *_key_id_len); + #endif /* __AUTHTOK_UTILS_H__ */ +diff --git a/src/util/authtok.c b/src/util/authtok.c +index 6062cd875ce2c6b541ef237e7f7bdddac80366c5..c2f78be329230a255ce04ce68a01084f7f584c7c 100644 +--- a/src/util/authtok.c ++++ b/src/util/authtok.c +@@ -196,10 +196,9 @@ errno_t sss_authtok_set(struct sss_auth_token *tok, + case SSS_AUTHTOK_TYPE_2FA: + return sss_authtok_set_2fa_from_blob(tok, data, len); + case SSS_AUTHTOK_TYPE_SC_PIN: +- return sss_authtok_set_sc_pin(tok, (const char*)data, len); ++ return sss_authtok_set_sc_from_blob(tok, data, len); + case SSS_AUTHTOK_TYPE_SC_KEYPAD: +- sss_authtok_set_sc_keypad(tok); +- return EOK; ++ return sss_authtok_set_sc_from_blob(tok, data, len); + case SSS_AUTHTOK_TYPE_EMPTY: + sss_authtok_set_empty(tok); + return EOK; +@@ -425,6 +424,104 @@ errno_t sss_authtok_set_2fa(struct sss_auth_token *tok, + return EOK; + } + ++errno_t sss_authtok_set_sc(struct sss_auth_token *tok, ++ enum sss_authtok_type type, ++ const char *pin, size_t pin_len, ++ const char *token_name, size_t token_name_len, ++ const char *module_name, size_t module_name_len, ++ const char *key_id, size_t key_id_len) ++{ ++ int ret; ++ size_t needed_size; ++ ++ if (type != SSS_AUTHTOK_TYPE_SC_PIN ++ && type != SSS_AUTHTOK_TYPE_SC_KEYPAD) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid type [%d].\n", type); ++ return EINVAL; ++ } ++ ++ sss_authtok_set_empty(tok); ++ ++ ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len, ++ module_name, module_name_len, ++ key_id, key_id_len, NULL, 0, ++ &needed_size); ++ if (ret != EAGAIN) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n"); ++ return ret; ++ } ++ ++ tok->data = talloc_size(tok, needed_size); ++ if (tok->data == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = sss_auth_pack_sc_blob(pin, pin_len, token_name, token_name_len, ++ module_name, module_name_len, ++ key_id, key_id_len, tok->data, ++ needed_size, &needed_size); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n"); ++ talloc_free(tok->data); ++ return ret; ++ } ++ ++ tok->length = needed_size; ++ tok->type = type; ++ ++ return EOK; ++} ++ ++errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok, ++ const uint8_t *data, ++ size_t len) ++{ ++ int ret; ++ char *pin = NULL; ++ size_t pin_len; ++ char *token_name = NULL; ++ size_t token_name_len; ++ char *module_name = NULL; ++ size_t module_name_len; ++ char *key_id = NULL; ++ size_t key_id_len; ++ TALLOC_CTX *tmp_ctx; ++ ++ if (tok == NULL) { ++ return EFAULT; ++ } ++ if (data == NULL || len == 0) { ++ return EINVAL; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sss_auth_unpack_sc_blob(tmp_ctx, data, len, &pin, &pin_len, ++ &token_name, &token_name_len, ++ &module_name, &module_name_len, ++ &key_id, &key_id_len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_sc_blob failed.\n"); ++ goto done; ++ } ++ ++ ret = sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, pin_len, ++ token_name, token_name_len, ++ module_name, module_name_len, ++ key_id, key_id_len); ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ + errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin, + size_t len) + { +@@ -435,15 +532,17 @@ errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin, + return EINVAL; + } + +- sss_authtok_set_empty(tok); +- +- return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_SC_PIN, +- "sc_pin", pin, len); ++ return sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, len, ++ NULL, 0, NULL, 0, NULL, 0); + } + +-errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin, ++errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin, + size_t *len) + { ++ int ret; ++ const char *pin = NULL; ++ size_t pin_len; ++ + if (!tok) { + return EFAULT; + } +@@ -451,9 +550,16 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin, + case SSS_AUTHTOK_TYPE_EMPTY: + return ENOENT; + case SSS_AUTHTOK_TYPE_SC_PIN: +- *pin = (const char *)tok->data; ++ ret = sss_authtok_get_sc(tok, &pin, &pin_len, ++ NULL, NULL, NULL, NULL, NULL, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n"); ++ return ret; ++ } ++ ++ *_pin = pin; + if (len) { +- *len = tok->length - 1; ++ *len = pin_len; + } + return EOK; + case SSS_AUTHTOK_TYPE_PASSWORD: +@@ -468,10 +574,202 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin, + + void sss_authtok_set_sc_keypad(struct sss_auth_token *tok) + { +- if (!tok) { ++ if (tok == NULL) { + return; + } ++ + sss_authtok_set_empty(tok); + + tok->type = SSS_AUTHTOK_TYPE_SC_KEYPAD; + } ++ ++errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx, ++ const uint8_t *blob, size_t blob_len, ++ char **pin, size_t *_pin_len, ++ char **token_name, size_t *_token_name_len, ++ char **module_name, size_t *_module_name_len, ++ char **key_id, size_t *_key_id_len) ++{ ++ size_t c; ++ uint32_t pin_len; ++ uint32_t token_name_len; ++ uint32_t module_name_len; ++ uint32_t key_id_len; ++ ++ c = 0; ++ ++ if (blob == NULL || blob_len == 0) { ++ pin_len = 0; ++ token_name_len = 0; ++ module_name_len = 0; ++ key_id_len = 0; ++ } else if (blob_len > 0 ++ && strnlen((const char *) blob, blob_len) == blob_len - 1) { ++ pin_len = blob_len; ++ token_name_len = 0; ++ module_name_len = 0; ++ key_id_len = 0; ++ } else { ++ if (blob_len < 4 * sizeof(uint32_t)) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n"); ++ return EINVAL; ++ } ++ ++ SAFEALIGN_COPY_UINT32(&pin_len, blob, &c); ++ SAFEALIGN_COPY_UINT32(&token_name_len, blob + c, &c); ++ SAFEALIGN_COPY_UINT32(&module_name_len, blob + c, &c); ++ SAFEALIGN_COPY_UINT32(&key_id_len, blob + c, &c); ++ ++ if (blob_len != 4 * sizeof(uint32_t) + pin_len + token_name_len ++ + module_name_len + key_id_len) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n"); ++ return EINVAL; ++ } ++ } ++ ++ if (pin_len != 0) { ++ *pin = talloc_strndup(mem_ctx, (const char *) blob + c, pin_len); ++ if (*pin == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ return ENOMEM; ++ } ++ } else { ++ *pin = NULL; ++ } ++ ++ if (token_name_len != 0) { ++ *token_name = talloc_strndup(mem_ctx, (const char *) blob + c + pin_len, ++ token_name_len); ++ if (*token_name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ talloc_free(*pin); ++ return ENOMEM; ++ } ++ } else { ++ *token_name = NULL; ++ } ++ ++ if (module_name_len != 0) { ++ *module_name = talloc_strndup(mem_ctx, ++ (const char *) blob + c + pin_len ++ + token_name_len, ++ module_name_len); ++ if (*module_name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ talloc_free(*pin); ++ talloc_free(*token_name); ++ return ENOMEM; ++ } ++ } else { ++ *module_name = NULL; ++ } ++ ++ if (key_id_len != 0) { ++ *key_id = talloc_strndup(mem_ctx, ++ (const char *) blob + c + pin_len ++ + token_name_len ++ + module_name_len, ++ key_id_len); ++ if (*key_id == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ talloc_free(*pin); ++ talloc_free(*token_name); ++ talloc_free(*module_name); ++ return ENOMEM; ++ } ++ } else { ++ *key_id = NULL; ++ } ++ ++ /* Re-calculate length for the case where \0 was missing in the blob */ ++ if (_pin_len != NULL) { ++ *_pin_len = (*pin == NULL) ? 0 : strlen(*pin); ++ } ++ if (_token_name_len != NULL) { ++ *_token_name_len = (*token_name == NULL) ? 0 : strlen(*token_name); ++ } ++ if (_module_name_len != NULL) { ++ *_module_name_len = (*module_name == NULL) ? 0 : strlen(*module_name); ++ } ++ ++ if (_key_id_len != NULL) { ++ *_key_id_len = (*key_id == NULL) ? 0 : strlen(*key_id); ++ } ++ ++ return EOK; ++} ++ ++errno_t sss_authtok_get_sc(struct sss_auth_token *tok, ++ const char **_pin, size_t *_pin_len, ++ const char **_token_name, size_t *_token_name_len, ++ const char **_module_name, size_t *_module_name_len, ++ const char **_key_id, size_t *_key_id_len) ++{ ++ size_t c = 0; ++ size_t pin_len; ++ size_t token_name_len; ++ size_t module_name_len; ++ size_t key_id_len; ++ uint32_t tmp_uint32_t; ++ ++ if (!tok) { ++ return EFAULT; ++ } ++ ++ if (tok->type != SSS_AUTHTOK_TYPE_SC_PIN ++ && tok->type != SSS_AUTHTOK_TYPE_SC_KEYPAD) { ++ return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES; ++ } ++ ++ if (tok->length < 4 * sizeof(uint32_t)) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n"); ++ return EINVAL; ++ } ++ ++ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data, &c); ++ pin_len = tmp_uint32_t - 1; ++ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c); ++ token_name_len = tmp_uint32_t - 1; ++ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c); ++ module_name_len = tmp_uint32_t -1; ++ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c); ++ key_id_len = tmp_uint32_t -1; ++ ++ if (tok->length != 4 * sizeof(uint32_t) + 4 + pin_len + token_name_len ++ + module_name_len + key_id_len) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n"); ++ return EINVAL; ++ } ++ ++ if (_pin != NULL) { ++ *_pin = (const char *) tok->data + c; ++ } ++ if (_pin_len != NULL) { ++ *_pin_len = pin_len; ++ } ++ ++ if (_token_name != NULL) { ++ *_token_name = (const char *) tok->data + c + pin_len + 1; ++ } ++ if (_token_name_len != NULL) { ++ *_token_name_len = token_name_len; ++ } ++ ++ if (_module_name != NULL) { ++ *_module_name = (const char *) tok->data + c + pin_len + 1 ++ + token_name_len + 1; ++ } ++ if (_module_name_len != NULL) { ++ *_module_name_len = module_name_len; ++ } ++ ++ if (_key_id != NULL) { ++ *_key_id = (const char *) tok->data + c + pin_len + 1 ++ + token_name_len + 1 + module_name_len + 1; ++ } ++ if (_key_id_len != NULL) { ++ *_key_id_len = key_id_len; ++ } ++ ++ return EOK; ++} +diff --git a/src/util/authtok.h b/src/util/authtok.h +index f1a01a42306a720fc39e701078550a071835e980..c146ba2066f2aca032e04d6a33459c47c963370f 100644 +--- a/src/util/authtok.h ++++ b/src/util/authtok.h +@@ -264,4 +264,88 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **pin, + */ + void sss_authtok_set_sc_keypad(struct sss_auth_token *tok); + ++/** ++ * @brief Set complete Smart Card authentication blob including PKCS#11 token ++ * name, module name and key id. ++ * ++ * @param tok A pointer to an sss_auth_token ++ * @param type Authentication token type, may be ++ * SSS_AUTHTOK_TYPE_SC_PIN or SSS_AUTHTOK_TYPE_SC_KEYPAD ++ * @param pin A pointer to a const char *, that will point to a null ++ * terminated string containing the pin ++ * @param pin_len The length of the pin string, if set to 0 it will be ++ * calculated ++ * @param token_name A pointer to a const char *, that will point to a null ++ * terminated string containing the PKCS#11 token name ++ * @param token_name_len The length of the token name string, if set to 0 it ++ * will be calculated ++ * @param module_name A pointer to a const char *, that will point to a null ++ * terminated string containing the PKCS#11 module name ++ * @param module_name_len The length of the module name string, if set to 0 it ++ * will be calculated ++ * @param key_id A pointer to a const char *, that will point to a null ++ * terminated string containing the PKCS#11 key id ++ * @param key_id_len The length of the key id string, if set to 0 it will be ++ * calculated ++ * ++ * @return EOK on success ++ * EINVAL unexpected or inval input ++ * ENOMEM memory allocation error ++ */ ++errno_t sss_authtok_set_sc(struct sss_auth_token *tok, ++ enum sss_authtok_type type, ++ const char *pin, size_t pin_len, ++ const char *token_name, size_t token_name_len, ++ const char *module_name, size_t module_name_len, ++ const char *key_id, size_t key_id_len); ++/** ++ * @brief Set a Smart Card authentication data, replacing any previous data ++ * ++ * @param tok A pointer to a sss_auth_token structure to change, also ++ * used as a memory context to allocate the internal data. ++ * @param data Smart Card authentication data blob ++ * @param len The length of the blob ++ * ++ * @return EOK on success ++ * ENOMEM on error ++ */ ++errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok, ++ const uint8_t *data, ++ size_t len); ++ ++/** ++ * @brief Get complete Smart Card authtoken data ++ * ++ * @param tok A pointer to a sss_auth_token structure ++ * @param[out] _pin A pointer to a const char *, that will point to ++ * a null terminated string holding the pin, ++ * may not be modified or freed ++ * @param[out] _pin__len Length of the pin ++ * @param[out] _token_name A pointer to a const char *, that will point to ++ * a null terminated string holding the PKCS#11 ++ * token name, may not be modified or freed ++ * @param[out] _token_name_len Length of the PKCS#11 token name ++ * @param[out] _module_name A pointer to a const char *, that will point to ++ * a null terminated string holding the PKCS#11 ++ * module name, may not be modified or freed ++ * @param[out] _module_name_len Length of the PKCS#11 module name ++ * @param[out] _key_id A pointer to a const char *, that will point to ++ * a null terminated string holding the PKCS#11 ++ * key id, may not be modified or freed ++ * @param[out] _key_id_len Length of the PKCS#11 key id ++ * ++ * Any of the output pointers may be NULL if the caller does not need the ++ * specific item. ++ * ++ * @return EOK on success ++ * EFAULT missing token ++ * EINVAL if input data is not consistent ++ * ENOENT if the token is empty ++ * EACCESS if the token is not a Smart Card token ++ */ ++errno_t sss_authtok_get_sc(struct sss_auth_token *tok, ++ const char **_pin, size_t *_pin_len, ++ const char **_token_name, size_t *_token_name_len, ++ const char **_module_name, size_t *_module_name_len, ++ const char **_key_id, size_t *_key_id_len); + #endif /* __AUTHTOK_H__ */ +-- +2.9.3 + diff --git a/0083-PAM-forward-Smartcard-credentials-to-backends.patch b/0083-PAM-forward-Smartcard-credentials-to-backends.patch new file mode 100644 index 0000000..b5e6f27 --- /dev/null +++ b/0083-PAM-forward-Smartcard-credentials-to-backends.patch @@ -0,0 +1,265 @@ +From 82c5971fafe6063a90289ebba08035fc49ae8590 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 8 Oct 2015 16:42:39 +0200 +Subject: [PATCH 83/95] PAM: forward Smartcard credentials to backends + +Reviewed-by: Jakub Hrozek +--- + src/responder/pam/pamsrv.h | 3 ++ + src/responder/pam/pamsrv_cmd.c | 102 +++++++++++++++++++++++++++++++--------- + src/tests/cmocka/test_pam_srv.c | 16 ++++++- + 3 files changed, 97 insertions(+), 24 deletions(-) + +diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h +index e3568123ac6e35d05d63b7b481dbcfbeddbd458c..389888eca389f793079ad169f7713151c896e90c 100644 +--- a/src/responder/pam/pamsrv.h ++++ b/src/responder/pam/pamsrv.h +@@ -71,6 +71,9 @@ struct pam_auth_req { + struct ldb_message *user_obj; + struct ldb_message *cert_user_obj; + char *token_name; ++ char *module_name; ++ char *key_id; ++ bool cert_auth_local; + }; + + struct sss_cmd_table *get_pam_cmds(void); +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index e74dd1684ca0a853f4ff1f9f98bb0f9e8d3d219f..529c74f703b3169676dc13b145b39243903ebb50 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -53,6 +53,14 @@ pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain, + + static void pam_reply(struct pam_auth_req *preq); + ++static errno_t check_cert(TALLOC_CTX *mctx, ++ struct tevent_context *ev, ++ struct pam_ctx *pctx, ++ struct pam_auth_req *preq, ++ struct pam_data *pd); ++ ++static int pam_check_user_done(struct pam_auth_req *preq, int ret); ++ + static errno_t pack_user_info_msg(TALLOC_CTX *mem_ctx, + const char *user_error_message, + size_t *resp_len, +@@ -718,6 +726,28 @@ static void pam_reply(struct pam_auth_req *preq) + DEBUG(SSSDBG_FUNC_DATA, + "pam_reply called with result [%d]: %s.\n", + pd->pam_status, pam_strerror(NULL, pd->pam_status)); ++ ++ if (pd->cmd == SSS_PAM_AUTHENTICATE ++ && (pd->pam_status == PAM_AUTHINFO_UNAVAIL ++ || pd->pam_status == PAM_NO_MODULE_DATA ++ || pd->pam_status == PAM_BAD_ITEM) ++ && may_do_cert_auth(pctx, pd)) { ++ /* We have Smartcard credentials and the backend indicates that it is ++ * offline (PAM_AUTHINFO_UNAVAIL) or cannot handle the credentials ++ * (PAM_BAD_ITEM), so let's try authentication against the Smartcard ++ * PAM_NO_MODULE_DATA is returned by the krb5 backend if no ++ * authentication method was found at all, this might happen if the ++ * user has a Smartcard assigned but the pkint plugin is not available ++ * on the client. */ ++ DEBUG(SSSDBG_IMPORTANT_INFO, ++ "Backend cannot handle Smartcard authentication, " ++ "trying local Smartcard authentication.\n"); ++ preq->cert_auth_local = true; ++ ret = check_cert(cctx, cctx->ev, pctx, preq, pd); ++ pam_check_user_done(preq, ret); ++ return; ++ } ++ + if (pd->pam_status == PAM_AUTHINFO_UNAVAIL || preq->use_cached_auth) { + + switch(pd->cmd) { +@@ -1019,7 +1049,6 @@ static void pam_forwarder_cert_cb(struct tevent_req *req); + static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + static int pam_check_user_search(struct pam_auth_req *preq); +-static int pam_check_user_done(struct pam_auth_req *preq, int ret); + + static errno_t pam_cmd_assume_upn(struct pam_auth_req *preq) + { +@@ -1234,6 +1263,7 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) + } + talloc_set_destructor(preq, pam_auth_req_destructor); + preq->cctx = cctx; ++ preq->cert_auth_local = false; + + preq->pd = create_pam_data(preq); + if (!preq->pd) { +@@ -1330,8 +1360,9 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd) + } + } + +- +- if (may_do_cert_auth(pctx, pd)) { ++ /* try backend first for authentication before doing local Smartcard ++ * authentication */ ++ if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) { + ret = check_cert(cctx, cctx->ev, pctx, preq, pd); + /* Finish here */ + goto done; +@@ -1439,6 +1470,12 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + preq->cert_user_obj = talloc_steal(preq, result->msgs[0]); + + if (preq->pd->logon_name == NULL) { ++ if (preq->pd->cmd != SSS_PAM_PREAUTH) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Missing logon name only allowed during pre-auth.\n"); ++ ret = ENOENT; ++ goto done; ++ } + cert_user = ldb_msg_find_attr_as_string(preq->cert_user_obj, + SYSDB_NAME, NULL); + if (cert_user == NULL) { +@@ -1451,20 +1488,17 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n", + cert_user); + +- ret = add_pam_cert_response(preq->pd, cert_user, preq->token_name); ++ 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, "add_pam_cert_response failed.\n"); +- } +- +- preq->pd->domain = talloc_strdup(preq->pd, result->domain->name); +- if (preq->pd->domain == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); +- ret = ENOMEM; ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_parse_name_for_domains failed.\n"); + goto done; + } +- preq->pd->pam_status = PAM_SUCCESS; +- pam_reply(preq); +- return; + } + } else { + if (preq->pd->logon_name == NULL) { +@@ -1475,7 +1509,12 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) + } + } + +- ret = pam_check_user_search(preq); ++ if (preq->user_obj == NULL) { ++ ret = pam_check_user_search(preq); ++ } else { ++ ret = EOK; ++ } ++ + if (ret == EOK) { + pam_dom_forwarder(preq); + } +@@ -1531,7 +1570,9 @@ static void pam_forwarder_cb(struct tevent_req *req) + } + } + +- if (may_do_cert_auth(pctx, pd)) { ++ /* try backend first for authentication before doing local Smartcard ++ * authentication */ ++ if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) { + ret = check_cert(cctx, cctx->ev, pctx, preq, pd); + /* Finish here */ + goto done; +@@ -1985,7 +2026,7 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) + NULL); + if (cert_user == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, +- "Certificate user object has not name.\n"); ++ "Certificate user object has no name.\n"); + preq->pd->pam_status = PAM_USER_UNKNOWN; + pam_reply(preq); + return; +@@ -1994,11 +2035,21 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) + /* pam_check_user_search() calls pd_set_primary_name() is the search + * was successful, so pd->user contains the canonical sysdb name + * as well */ +- if (strcmp(cert_user, preq->pd->user) == 0) { +- +- preq->pd->pam_status = PAM_SUCCESS; ++ if (ldb_dn_compare(preq->cert_user_obj->dn, preq->user_obj->dn) == 0) { + + if (preq->pd->cmd == SSS_PAM_PREAUTH) { ++ ret = sss_authtok_set_sc(preq->pd->authtok, ++ SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0, ++ preq->token_name, 0, ++ preq->module_name, 0, ++ preq->key_id, 0); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_sc failed, " ++ "Smartcard authentication " ++ "detection might fail in the " ++ "backend.\n"); ++ } ++ + ret = add_pam_cert_response(preq->pd, cert_user, + preq->token_name); + if (ret != EOK) { +@@ -2007,9 +2058,14 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) + } + } + +- preq->callback = pam_reply; +- pam_reply(preq); +- return; ++ /* We are done if we do not have to call the backend */ ++ if (preq->pd->cmd == SSS_PAM_AUTHENTICATE ++ && preq->cert_auth_local) { ++ preq->pd->pam_status = PAM_SUCCESS; ++ preq->callback = pam_reply; ++ pam_reply(preq); ++ return; ++ } + } else { + if (preq->pd->cmd == SSS_PAM_PREAUTH) { + DEBUG(SSSDBG_TRACE_FUNC, +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index e6ed8f509b5b69978f80c158e52bfea738909465..6b91f49c6608043df68c65f9a3338a76ac66bbcc 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -285,6 +285,9 @@ static void pam_test_setup_common(void) + pam_test_ctx->tctx->dom->name); + assert_non_null(pam_test_ctx->wrong_user_fqdn); + ++ /* integer values cannot be set by pam_params */ ++ pam_test_ctx->pctx->id_timeout = 5; ++ + /* Prime the cache with a valid user */ + ret = sysdb_add_user(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, +@@ -783,6 +786,13 @@ static int test_pam_wrong_pw_offline_auth_check(uint32_t status, + return test_pam_simple_check(status, body, blen); + } + ++static int test_pam_simple_check_success(uint32_t status, ++ uint8_t *body, size_t blen) ++{ ++ pam_test_ctx->exp_pam_status = PAM_SUCCESS; ++ return test_pam_simple_check(status, body, blen); ++} ++ + static int test_pam_creds_insufficient_check(uint32_t status, + uint8_t *body, size_t blen) + { +@@ -1752,7 +1762,11 @@ void test_pam_cert_auth(void **state) + mock_account_recv(0, 0, NULL, test_lookup_by_cert_cb, + discard_const(TEST_TOKEN_CERT)); + +- set_cmd_cb(test_pam_simple_check); ++ /* Assume backend cannot handle Smartcard credentials */ ++ pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; ++ ++ ++ set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); +-- +2.9.3 + diff --git a/0084-p11-return-name-of-PKCS-11-module-and-key-id-to-pam_.patch b/0084-p11-return-name-of-PKCS-11-module-and-key-id-to-pam_.patch new file mode 100644 index 0000000..94037bd --- /dev/null +++ b/0084-p11-return-name-of-PKCS-11-module-and-key-id-to-pam_.patch @@ -0,0 +1,550 @@ +From ead25e32c52c8c2f5fd9abd179e9e81de58f9ca3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 22 Feb 2017 17:58:15 +0100 +Subject: [PATCH 84/95] p11: return name of PKCS#11 module and key id to + pam_sss + +Reviewed-by: Jakub Hrozek +--- + src/p11_child/p11_child_nss.c | 55 ++++++++++++++++++++++++-- + src/responder/pam/pamsrv.h | 6 ++- + src/responder/pam/pamsrv_cmd.c | 8 +++- + src/responder/pam/pamsrv_p11.c | 86 ++++++++++++++++++++++++++++++++++++++--- + src/sss_client/pam_message.h | 2 + + src/sss_client/pam_sss.c | 49 ++++++++++++++++++++++- + src/tests/cmocka/test_pam_srv.c | 33 +++++++++++++++- + 7 files changed, 221 insertions(+), 18 deletions(-) + +diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c +index 84772692eadfb28d85298f924013761ae3ca7e82..f165b58e63d2b8a6f26acf8bd89e7b41713e7359 100644 +--- a/src/p11_child/p11_child_nss.c ++++ b/src/p11_child/p11_child_nss.c +@@ -72,13 +72,15 @@ static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg) + int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + enum op_mode mode, const char *pin, + struct cert_verify_opts *cert_verify_opts, +- char **cert, char **token_name_out) ++ char **cert, char **token_name_out, char **module_name_out, ++ char **key_id_out) + { + int ret; + SECStatus rv; + NSSInitContext *nss_ctx; + SECMODModuleList *mod_list; + SECMODModuleList *mod_list_item; ++ SECMODModule *module; + const char *slot_name; + const char *token_name; + uint32_t flags = NSS_INIT_READONLY +@@ -91,6 +93,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + PK11SlotInfo *slot = NULL; + CK_SLOT_ID slot_id; + SECMODModuleID module_id; ++ const char *module_name; + CERTCertList *cert_list = NULL; + CERTCertListNode *cert_list_node; + const PK11DefaultArrayEntry friendly_attr = { "Publicly-readable certs", +@@ -105,6 +108,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + CERTCertificate *found_cert = NULL; + PK11SlotList *list = NULL; + PK11SlotListElement *le; ++ SECItem *key_id = NULL; ++ char *key_id_str = NULL; + + + nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, ¶meters, flags); +@@ -187,8 +192,11 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + module_id = PK11_GetModuleID(slot); + slot_name = PK11_GetSlotName(slot); + token_name = PK11_GetTokenName(slot); +- DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d].\n", +- token_name, slot_name, (int) slot_id, (int) module_id); ++ module = PK11_GetModule(slot); ++ module_name = module->dllName == NULL ? "NSS-Internal" : module->dllName; ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d][%s].\n", ++ token_name, slot_name, (int) slot_id, (int) module_id, module_name); + + if (PK11_IsFriendly(slot)) { + DEBUG(SSSDBG_TRACE_ALL, "Token is friendly.\n"); +@@ -346,6 +354,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n"); + *cert = NULL; + *token_name_out = NULL; ++ *module_name_out = NULL; + ret = EOK; + goto done; + } +@@ -412,6 +421,30 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + "Certificate verified and validated.\n"); + } + ++ key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL); ++ if (key_id == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "PK11_GetLowLevelKeyIDForCert failed [%d].\n", ++ PR_GetError()); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ key_id_str = CERT_Hexify(key_id, PR_FALSE); ++ if (key_id_str == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n", PR_GetError()); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n", key_id_str); ++ ++ *key_id_out = talloc_strdup(mem_ctx, key_id_str); ++ if (*key_id_out == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy key id.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ + *cert = sss_base64_encode(mem_ctx, found_cert->derCert.data, + found_cert->derCert.len); + if (*cert == NULL) { +@@ -427,6 +460,13 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, + goto done; + } + ++ *module_name_out = talloc_strdup(mem_ctx, module_name); ++ if (*module_name_out == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy module_name_out name.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ + ret = EOK; + + done: +@@ -438,6 +478,9 @@ done: + CERT_DestroyCertList(cert_list); + } + ++ SECITEM_FreeItem(key_id, PR_TRUE); ++ PORT_Free(key_id_str); ++ + PORT_Free(signed_random_value.data); + + rv = NSS_ShutdownContext(nss_ctx); +@@ -502,6 +545,8 @@ int main(int argc, const char *argv[]) + char *pin = NULL; + char *slot_name_in = NULL; + char *token_name_out = NULL; ++ char *module_name_out = NULL; ++ char *key_id_out = NULL; + char *nss_db = NULL; + struct cert_verify_opts *cert_verify_opts; + char *verify_opts = NULL; +@@ -665,7 +710,7 @@ int main(int argc, const char *argv[]) + } + + ret = do_work(main_ctx, nss_db, slot_name_in, mode, pin, cert_verify_opts, +- &cert, &token_name_out); ++ &cert, &token_name_out, &module_name_out, &key_id_out); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n"); + goto fail; +@@ -673,6 +718,8 @@ int main(int argc, const char *argv[]) + + if (cert != NULL) { + fprintf(stdout, "%s\n", token_name_out); ++ fprintf(stdout, "%s\n", module_name_out); ++ fprintf(stdout, "%s\n", key_id_out); + fprintf(stdout, "%s\n", cert); + } + +diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h +index 389888eca389f793079ad169f7713151c896e90c..7860a99a8032fad383c94aaa948687512fd085e1 100644 +--- a/src/responder/pam/pamsrv.h ++++ b/src/responder/pam/pamsrv.h +@@ -92,10 +92,12 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, + const char *verify_opts, + struct pam_data *pd); + errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, +- char **cert, char **token_name); ++ char **cert, char **token_name, char **module_name, ++ char **key_id); + + errno_t add_pam_cert_response(struct pam_data *pd, const char *user, +- const char *token_name); ++ const char *token_name, const char *module_name, ++ const char *key_id); + + 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 529c74f703b3169676dc13b145b39243903ebb50..6b7a9493bc2f9f4043a5800530116f7cf6d47263 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1401,7 +1401,9 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) + struct pam_ctx *pctx = + talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx); + +- ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name); ++ ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name, ++ &preq->module_name, ++ &preq->key_id); + talloc_free(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n"); +@@ -2051,7 +2053,9 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) + } + + ret = add_pam_cert_response(preq->pd, cert_user, +- preq->token_name); ++ preq->token_name, ++ preq->module_name, ++ preq->key_id); + 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 570bfe09d4385a038e7e03fcb64c72dd794774a6..365300b9075983b603a6f9e91ba6f8f21706388f 100644 +--- a/src/responder/pam/pamsrv_p11.c ++++ b/src/responder/pam/pamsrv_p11.c +@@ -133,7 +133,8 @@ static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx, + + static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, + ssize_t buf_len, char **_cert, +- char **_token_name) ++ char **_token_name, char **_module_name, ++ char **_key_id) + { + int ret; + TALLOC_CTX *tmp_ctx = NULL; +@@ -141,6 +142,8 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, + uint8_t *pn; + char *cert = NULL; + char *token_name = NULL; ++ char *module_name = NULL; ++ char *key_id = NULL; + + if (buf_len < 0) { + DEBUG(SSSDBG_CRIT_FAILURE, +@@ -187,6 +190,54 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, + } + + if (pn == p) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Missing module name in p11_child response.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ module_name = talloc_strndup(tmp_ctx, (char *) p, (pn - p)); ++ if (module_name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n", module_name); ++ ++ p = ++pn; ++ pn = memchr(p, '\n', buf_len - (p - buf)); ++ if (pn == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Missing new-line in p11_child response.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (pn == p) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Missing key id in p11_child response.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ key_id = talloc_strndup(tmp_ctx, (char *) p, (pn - p)); ++ if (key_id == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", key_id); ++ ++ p = pn + 1; ++ pn = memchr(p, '\n', buf_len - (p - buf)); ++ if (pn == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Missing new-line in p11_child response.\n"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (pn == p) { + DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n"); + ret = EINVAL; + goto done; +@@ -206,6 +257,8 @@ done: + if (ret == EOK) { + *_token_name = talloc_steal(mem_ctx, token_name); + *_cert = talloc_steal(mem_ctx, cert); ++ *_module_name = talloc_steal(mem_ctx, module_name); ++ *_key_id = talloc_steal(mem_ctx, key_id); + } + + talloc_free(tmp_ctx); +@@ -222,6 +275,8 @@ struct pam_check_cert_state { + struct child_io_fds *io; + char *cert; + char *token_name; ++ char *module_name; ++ char *key_id; + }; + + static void p11_child_write_done(struct tevent_req *subreq); +@@ -296,6 +351,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, + state->child_status = EFAULT; + state->cert = NULL; + state->token_name = NULL; ++ state->module_name = NULL; + state->io = talloc(state, struct child_io_fds); + if (state->io == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n"); +@@ -459,7 +515,8 @@ static void p11_child_done(struct tevent_req *subreq) + PIPE_FD_CLOSE(state->io->read_from_child_fd); + + ret = parse_p11_child_response(state, buf, buf_len, &state->cert, +- &state->token_name); ++ &state->token_name, &state->module_name, ++ &state->key_id); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_respose failed.\n"); + tevent_req_error(req, ret); +@@ -486,7 +543,8 @@ static void p11_child_timeout(struct tevent_context *ev, + } + + errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, +- char **cert, char **token_name) ++ char **cert, char **token_name, char **module_name, ++ char **key_id) + { + struct pam_check_cert_state *state = + tevent_req_data(req, struct pam_check_cert_state); +@@ -501,6 +559,14 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + *token_name = talloc_steal(mem_ctx, state->token_name); + } + ++ if (module_name != NULL) { ++ *module_name = talloc_steal(mem_ctx, state->module_name); ++ } ++ ++ if (key_id != NULL) { ++ *key_id = talloc_steal(mem_ctx, state->key_id); ++ } ++ + return EOK; + } + +@@ -513,23 +579,29 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME" + + errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, +- const char *token_name) ++ const char *token_name, const char *module_name, ++ const char *key_id) + { + uint8_t *msg = NULL; + char *env = NULL; + size_t user_len; + size_t msg_len; + size_t slot_len; ++ size_t module_len; ++ size_t key_id_len; + int ret; + +- if (sysdb_username == NULL || token_name == NULL) { ++ if (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; + slot_len = strlen(token_name) + 1; +- msg_len = user_len + slot_len; ++ module_len = strlen(module_name) + 1; ++ key_id_len = strlen(key_id) + 1; ++ msg_len = user_len + slot_len + module_len + key_id_len; + + msg = talloc_zero_size(pd, msg_len); + if (msg == NULL) { +@@ -546,6 +618,8 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, + * being I think using sysdb_username is fine. */ + memcpy(msg, sysdb_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); + talloc_free(msg); +diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h +index 34889e0743decd85892dda311f010f9d7e3ba681..3f4a770ac08ee416ead2f215ab873e8eb277c9eb 100644 +--- a/src/sss_client/pam_message.h ++++ b/src/sss_client/pam_message.h +@@ -61,6 +61,8 @@ struct pam_items { + + char *cert_user; + char *token_name; ++ char *module_name; ++ char *key_id; + }; + + 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 8f97af77e3a0c2fa26c84962b6438fe774a75f1a..fa30889e787f9ea74dd2eded3cb92380397cdf38 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -162,6 +162,12 @@ static void overwrite_and_free_pam_items(struct pam_items *pi) + + free(pi->token_name); + pi->token_name = NULL; ++ ++ free(pi->module_name); ++ pi->module_name = NULL; ++ ++ free(pi->key_id); ++ pi->key_id = NULL; + } + + static int null_strcmp(const char *s1, const char *s2) { +@@ -1019,10 +1025,47 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + pi->token_name = strdup((char *) &buf[p + offset]); + if (pi->token_name == NULL) { + D(("strdup failed")); ++ free(pi->cert_user); ++ pi->cert_user = NULL; + break; + } +- D(("cert user: [%s] token name: [%s]", pi->cert_user, +- pi->token_name)); ++ ++ offset += strlen(pi->token_name) + 1; ++ if (offset >= len) { ++ D(("Cert message size mismatch")); ++ free(pi->cert_user); ++ pi->cert_user = NULL; ++ free(pi->token_name); ++ pi->token_name = NULL; ++ break; ++ } ++ free(pi->module_name); ++ pi->module_name = strdup((char *) &buf[p + offset]); ++ if (pi->module_name == NULL) { ++ D(("strdup failed")); ++ break; ++ } ++ ++ offset += strlen(pi->module_name) + 1; ++ if (offset >= len) { ++ D(("Cert message size mismatch")); ++ free(pi->cert_user); ++ pi->cert_user = NULL; ++ free(pi->token_name); ++ pi->token_name = NULL; ++ free(pi->module_name); ++ pi->module_name = NULL; ++ break; ++ } ++ free(pi->key_id); ++ pi->key_id = strdup((char *) &buf[p + offset]); ++ if (pi->key_id == NULL) { ++ D(("strdup failed")); ++ break; ++ } ++ D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]", ++ pi->cert_user, pi->token_name, pi->module_name, ++ pi->key_id)); + break; + case SSS_PASSWORD_PROMPTING: + D(("Password prompting available.")); +@@ -1120,6 +1163,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags, + + pi->cert_user = NULL; + pi->token_name = NULL; ++ pi->module_name = NULL; ++ pi->key_id = NULL; + + return PAM_SUCCESS; + } +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +index 6b91f49c6608043df68c65f9a3338a76ac66bbcc..cbc1d036720f85845387475390cfb141709d514e 100644 +--- a/src/tests/cmocka/test_pam_srv.c ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -48,6 +48,8 @@ + #define NSS_DB "sql:"NSS_DB_PATH + + #define TEST_TOKEN_NAME "SSSD Test Token" ++#define TEST_MODULE_NAME "NSS-Internal" ++#define TEST_KEY_ID "6822EDDD231DAB8DBCD83721BE16A13DD0E31C08" + #define TEST_TOKEN_CERT \ + "MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \ + "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \ +@@ -668,7 +670,10 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body, + 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))); ++ 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); +@@ -676,7 +681,17 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body, + + 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; + } + +@@ -707,7 +722,10 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) + 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))); ++ 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); +@@ -715,6 +733,17 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) + + 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; + } +-- +2.9.3 + diff --git a/0085-pam-enhance-Smartcard-authentication-token.patch b/0085-pam-enhance-Smartcard-authentication-token.patch new file mode 100644 index 0000000..c53fd44 --- /dev/null +++ b/0085-pam-enhance-Smartcard-authentication-token.patch @@ -0,0 +1,134 @@ +From 52f45837ded98564968da42229b37db6a36ad627 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 27 Sep 2016 16:03:22 +0200 +Subject: [PATCH 85/95] pam: enhance Smartcard authentication token + +Reviewed-by: Jakub Hrozek +--- + src/providers/krb5/krb5_child.c | 4 ++- + src/providers/krb5/krb5_child_handler.c | 2 ++ + src/responder/pam/pamsrv_cmd.c | 9 ++----- + src/sss_client/pam_sss.c | 45 ++++++++++++++++++++++++++++----- + 4 files changed, 45 insertions(+), 15 deletions(-) + +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index be31ddbc490b7fd03e7092c244a2cc34a41ee22a..031847089c035a18a6ff2859a0257e08228fb19e 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -1856,7 +1856,9 @@ static errno_t unpack_authtok(struct sss_auth_token *tok, + ret = sss_authtok_set_ccfile(tok, (char *)(buf + *p), 0); + break; + case SSS_AUTHTOK_TYPE_2FA: +- ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, (buf + *p), ++ case SSS_AUTHTOK_TYPE_SC_PIN: ++ case SSS_AUTHTOK_TYPE_SC_KEYPAD: ++ ret = sss_authtok_set(tok, auth_token_type, (buf + *p), + auth_token_length); + break; + default: +diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c +index 69636e0bc911a0e3dc7023ac815eb54fa81a2bf5..6a3dc9d739527caad2523d9359cd5ef60e622bab 100644 +--- a/src/providers/krb5/krb5_child_handler.c ++++ b/src/providers/krb5/krb5_child_handler.c +@@ -78,6 +78,8 @@ static errno_t pack_authtok(struct io_buffer *buf, size_t *rp, + auth_token_length = len + 1; + break; + case SSS_AUTHTOK_TYPE_2FA: ++ case SSS_AUTHTOK_TYPE_SC_PIN: ++ case SSS_AUTHTOK_TYPE_SC_KEYPAD: + data = (char *) sss_authtok_get_data(tok); + auth_token_length = sss_authtok_get_size(tok); + break; +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 6b7a9493bc2f9f4043a5800530116f7cf6d47263..e788a75a45e7cbfe3dd842310a2784b26aa6f7cc 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -160,15 +160,10 @@ static int extract_authtok_v2(struct sss_auth_token *tok, + } + break; + case SSS_AUTHTOK_TYPE_2FA: +- ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, +- auth_token_data, auth_token_length); +- break; + case SSS_AUTHTOK_TYPE_SC_PIN: +- ret = sss_authtok_set_sc_pin(tok, (const char *) auth_token_data, +- auth_token_length); +- break; + case SSS_AUTHTOK_TYPE_SC_KEYPAD: +- sss_authtok_set_sc_keypad(tok); ++ ret = sss_authtok_set(tok, auth_token_type, ++ auth_token_data, auth_token_length); + break; + default: + return EINVAL; +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index fa30889e787f9ea74dd2eded3cb92380397cdf38..a3d7a8a23b8dd2b25825f02c9b5d78f06731f36b 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -1476,6 +1476,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi) + char *answer = NULL; + char *prompt; + size_t size; ++ size_t needed_size; + + if (pi->token_name == NULL || *pi->token_name == '\0' + || pi->cert_user == NULL || *pi->cert_user == '\0') { +@@ -1509,18 +1510,48 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi) + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_authtok_size=0; + } else { +- pi->pam_authtok = strdup(answer); +- _pam_overwrite((void *)answer); +- free(answer); +- answer=NULL; ++ ++ ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0, ++ pi->module_name, 0, ++ pi->key_id, 0, ++ NULL, 0, &needed_size); ++ if (ret != EAGAIN) { ++ D(("sss_auth_pack_sc_blob failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ ++ pi->pam_authtok = malloc(needed_size); + if (pi->pam_authtok == NULL) { +- return PAM_BUF_ERR; ++ D(("malloc failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; + } ++ ++ ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0, ++ pi->module_name, 0, ++ pi->key_id, 0, ++ (uint8_t *) pi->pam_authtok, needed_size, ++ &needed_size); ++ if (ret != EOK) { ++ D(("sss_auth_pack_sc_blob failed.")); ++ free((void *)pi->pam_authtok); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_SC_PIN; +- pi->pam_authtok_size=strlen(pi->pam_authtok); ++ pi->pam_authtok_size = needed_size; + } + +- return PAM_SUCCESS; ++ ret = PAM_SUCCESS; ++ ++done: ++ _pam_overwrite((void *)answer); ++ free(answer); ++ answer=NULL; ++ ++ return ret; + } + + static int prompt_new_password(pam_handle_t *pamh, struct pam_items *pi) +-- +2.9.3 + diff --git a/0086-KRB5-allow-pkinit-pre-authentication.patch b/0086-KRB5-allow-pkinit-pre-authentication.patch new file mode 100644 index 0000000..3c58688 --- /dev/null +++ b/0086-KRB5-allow-pkinit-pre-authentication.patch @@ -0,0 +1,520 @@ +From 2d527aab0bab0c5323b7ea09c9a8c3820f4f8736 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 12 Oct 2015 11:52:56 +0200 +Subject: [PATCH 86/95] KRB5: allow pkinit pre-authentication + +Reviewed-by: Jakub Hrozek +--- + src/providers/krb5/krb5_auth.c | 18 +- + src/providers/krb5/krb5_child.c | 286 ++++++++++++++++++++++++++++++-- + src/providers/krb5/krb5_child_handler.c | 6 +- + src/sss_client/sss_cli.h | 6 + + 4 files changed, 303 insertions(+), 13 deletions(-) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index 0e685618ec2de1f923ffd9d78bf2a9d8816019e1..c2d6d7eeacc1f766024c4d629f25fd0f0be24e5e 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -344,8 +344,13 @@ static void krb5_auth_store_creds(struct sss_domain_info *domain, + domain->cache_credentials_min_ff_length); + ret = EINVAL; + } +- } else { ++ } else if (sss_authtok_get_type(pd->authtok) == ++ SSS_AUTHTOK_TYPE_PASSWORD) { + ret = sss_authtok_get_password(pd->authtok, &password, NULL); ++ } else { ++ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot cache authtok type [%d].\n", ++ sss_authtok_get_type(pd->authtok)); ++ ret = EINVAL; + } + break; + case SSS_PAM_CHAUTHTOK: +@@ -466,7 +471,9 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, + case SSS_PAM_AUTHENTICATE: + case SSS_PAM_CHAUTHTOK: + if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD +- && authtok_type != SSS_AUTHTOK_TYPE_2FA) { ++ && authtok_type != SSS_AUTHTOK_TYPE_2FA ++ && authtok_type != SSS_AUTHTOK_TYPE_SC_PIN ++ && authtok_type != SSS_AUTHTOK_TYPE_SC_KEYPAD) { + /* handle empty password gracefully */ + if (authtok_type == SSS_AUTHTOK_TYPE_EMPTY) { + DEBUG(SSSDBG_CRIT_FAILURE, +@@ -1023,6 +1030,12 @@ static void krb5_auth_done(struct tevent_req *subreq) + ret = EOK; + goto done; + ++ case ERR_NO_AUTH_METHOD_AVAILABLE: ++ state->pam_status = PAM_NO_MODULE_DATA; ++ state->dp_err = DP_ERR_OK; ++ ret = EOK; ++ goto done; ++ + default: + DEBUG(SSSDBG_IMPORTANT_INFO, + "The krb5_child process returned an error. Please inspect the " +@@ -1185,6 +1198,7 @@ krb5_pam_handler_send(TALLOC_CTX *mem_ctx, + + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: ++ case SSS_PAM_PREAUTH: + case SSS_CMD_RENEW: + case SSS_PAM_CHAUTHTOK_PRELIM: + case SSS_PAM_CHAUTHTOK: +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index 031847089c035a18a6ff2859a0257e08228fb19e..777a25f2a0ea434dde12d2396f6a35c2a1b86cd0 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -64,6 +64,7 @@ struct krb5_req { + krb5_creds *creds; + bool otp; + bool password_prompting; ++ bool pkinit_prompting; + char *otp_vendor; + char *otp_token_id; + char *otp_challenge; +@@ -587,6 +588,138 @@ done: + return ret; + } + ++static bool pkinit_identity_matches(const char *identity, ++ const char *token_name, ++ const char *module_name) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ char *str; ++ bool res = false; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n"); ++ return false; ++ } ++ ++ str = talloc_asprintf(tmp_ctx, "module_name=%s", module_name); ++ if (str == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ goto done; ++ } ++ ++ if (strstr(identity, str) == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "Identity [%s] does not contain [%s].\n", ++ identity, str); ++ goto done; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in identity [%s].\n", str, identity); ++ ++ str = talloc_asprintf(tmp_ctx, "token=%s", token_name); ++ if (str == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ goto done; ++ } ++ ++ if (strstr(identity, str) == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "Identity [%s] does not contain [%s].\n", ++ identity, str); ++ goto done; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in identity [%s].\n", str, identity); ++ ++ res = true; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return res; ++} ++ ++static krb5_error_code answer_pkinit(krb5_context ctx, ++ struct krb5_req *kr, ++ krb5_responder_context rctx) ++{ ++ krb5_error_code kerr; ++ const char *pin = NULL; ++ const char *token_name = NULL; ++ const char *module_name = NULL; ++ krb5_responder_pkinit_challenge *chl = NULL; ++ size_t c; ++ ++ kerr = krb5_responder_pkinit_get_challenge(ctx, rctx, &chl); ++ if (kerr != EOK || chl == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "krb5_responder_pkinit_get_challenge failed.\n"); ++ return kerr; ++ } ++ if (chl->identities == NULL || chl->identities[0] == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "No identities for pkinit!\n"); ++ kerr = EINVAL; ++ goto done; ++ } ++ ++ if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) { ++ for (c = 0; chl->identities[c] != NULL; c++) { ++ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Identity [%s] flags [%"PRId32"].\n", ++ c, chl->identities[c]->identity, ++ chl->identities[c]->token_flags); ++ } ++ } ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Setting pkinit_prompting.\n"); ++ kr->pkinit_prompting = true; ++ ++ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ++ && (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 = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL, ++ &token_name, NULL, ++ &module_name, NULL, ++ NULL, NULL); ++ if (kerr != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_authtok_get_sc failed.\n"); ++ goto done; ++ } ++ ++ for (c = 0; chl->identities[c] != NULL; c++) { ++ if (chl->identities[c]->identity != NULL ++ && pkinit_identity_matches(chl->identities[c]->identity, ++ token_name, module_name)) { ++ break; ++ } ++ } ++ ++ if (chl->identities[c] == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "No matching identity for [%s][%s] found in pkinit challenge.\n", ++ token_name, module_name); ++ kerr = EINVAL; ++ goto done; ++ } ++ ++ kerr = krb5_responder_pkinit_set_answer(ctx, rctx, ++ chl->identities[c]->identity, ++ pin); ++ if (kerr != 0) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "krb5_responder_set_answer failed.\n"); ++ } ++ ++ goto done; ++ } ++ ++ kerr = EOK; ++ ++done: ++ krb5_responder_pkinit_challenge_free(ctx, rctx, chl); ++ ++ return kerr; ++} ++ + static krb5_error_code sss_krb5_responder(krb5_context ctx, + void *data, + krb5_responder_context rctx) +@@ -634,6 +767,9 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx, + + return kerr; + } ++ } else if (strcmp(question_list[c], ++ KRB5_RESPONDER_QUESTION_PKINIT) == 0) { ++ return answer_pkinit(ctx, kr, rctx); + } + } + } +@@ -661,18 +797,23 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data, + size_t c; + struct krb5_req *kr = talloc_get_type(data, struct krb5_req); + ++ if (kr == NULL) { ++ return EINVAL; ++ } ++ + DEBUG(SSSDBG_TRACE_ALL, + "sss_krb5_prompter name [%s] banner [%s] num_prompts [%d] EINVAL.\n", + name, banner, num_prompts); + + if (num_prompts != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n"); + if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) { + for (c = 0; c < num_prompts; c++) { + DEBUG(SSSDBG_TRACE_ALL, "Prompt [%zu][%s].\n", c, + prompts[c].prompt); + } + } ++ ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n"); + return KRB5_LIBOS_CANTREADPWD; + } + +@@ -1036,6 +1177,63 @@ static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error) + return EOK; + } + ++static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx, ++ struct sss_auth_token *authtok, ++ char **_identity) ++{ ++ int ret; ++ char *identity; ++ const char *token_name; ++ const char *module_name; ++ const char *key_id; ++ ++ ret = sss_authtok_get_sc(authtok, NULL, NULL, ++ &token_name, NULL, ++ &module_name, NULL, ++ &key_id, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n"); ++ return ret; ++ } ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Got [%s][%s].\n", token_name, module_name); ++ ++ if (module_name == NULL || *module_name == '\0') { ++ module_name = "p11-kit-proxy.so"; ++ } ++ ++ identity = talloc_asprintf(mem_ctx, "PKCS11:module_name=%s", module_name); ++ if (identity == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ return ENOMEM; ++ } ++ ++ if (token_name != NULL && *token_name != '\0') { ++ identity = talloc_asprintf_append(identity, ":token=%s", ++ token_name); ++ if (identity == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "talloc_asprintf_append failed.\n"); ++ return ENOMEM; ++ } ++ } ++ ++ if (key_id != NULL && *key_id != '\0') { ++ identity = talloc_asprintf_append(identity, ":certid=%s", key_id); ++ if (identity == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "talloc_asprintf_append failed.\n"); ++ return ENOMEM; ++ } ++ } ++ ++ *_identity = identity; ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Using pkinit identity [%s].\n", identity); ++ ++ return EOK; ++} ++ + static errno_t add_ticket_times_and_upn_to_response(struct krb5_req *kr) + { + int ret; +@@ -1268,6 +1466,8 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + int realm_length; + krb5_error_code kerr; + char *cc_name; ++ int ret; ++ char *identity = NULL; + + kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options, + sss_krb5_expire_callback_func, +@@ -1284,6 +1484,30 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + return KRB5KRB_ERR_GENERIC; + } + ++ 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) { ++ DEBUG(SSSDBG_TRACE_ALL, ++ "Found Smartcard credentials, trying pkinit.\n"); ++ ++ ret = get_pkinit_identity(kr, kr->pd->authtok, &identity); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "get_pkinit_identity failed.\n"); ++ return ret; ++ } ++ ++ kerr = krb5_get_init_creds_opt_set_pa(kr->ctx, kr->options, ++ "X509_user_identity", identity); ++ talloc_free(identity); ++ if (kerr != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "krb5_get_init_creds_opt_set_pa failed.\n"); ++ return kerr; ++ } ++ ++ /* TODO: Maybe X509_anchors should be added here as well */ ++ } ++ + DEBUG(SSSDBG_TRACE_FUNC, + "Attempting kinit for realm [%s]\n",realm_name); + kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, +@@ -1294,12 +1518,25 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + /* Any errors are ignored during pre-auth, only data is collected to + * be send back to the client.*/ + DEBUG(SSSDBG_TRACE_FUNC, +- "krb5_get_init_creds_password returned [%d} during pre-auth.\n", ++ "krb5_get_init_creds_password returned [%d] during pre-auth.\n", + kerr); + return 0; + } else { + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); ++ ++ /* 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. ++ * ERR_NO_AUTH_METHOD_AVAILABLE is used to indicate to the ++ * frontend that local authentication might be tried. */ ++ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ++ && kerr == KRB5_PREAUTH_FAILED ++ && kr->password_prompting == false ++ && kr->otp == false ++ && kr->pkinit_prompting == false) { ++ return ERR_NO_AUTH_METHOD_AVAILABLE; ++ } + return kerr; + } + } +@@ -1367,6 +1604,12 @@ done: + + static errno_t map_krb5_error(krb5_error_code kerr) + { ++ /* just pass SSSD's internal error codes */ ++ if (kerr > 0 && IS_SSSD_ERROR(kerr)) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "[%d][%s].\n", kerr, sss_strerror(kerr)); ++ return kerr; ++ } ++ + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + } +@@ -1615,9 +1858,12 @@ static errno_t tgt_req_child(struct krb5_req *kr) + + DEBUG(SSSDBG_TRACE_LIBS, "Attempting to get a TGT\n"); + +- /* No password is needed for pre-auth, or if we have 2FA */ ++ /* No password is needed for pre-auth or if we have 2FA or SC */ + if (kr->pd->cmd != SSS_PAM_PREAUTH +- && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA) { ++ && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA ++ && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN ++ && sss_authtok_get_type(kr->pd->authtok) ++ != SSS_AUTHTOK_TYPE_SC_KEYPAD) { + ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL); + switch (ret) { + case EOK: +@@ -1641,7 +1887,12 @@ static errno_t tgt_req_child(struct krb5_req *kr) + if (kr->pd->cmd == SSS_PAM_PREAUTH) { + /* add OTP tokeninfo messge if available */ + if (kr->otp) { +- kerr = k5c_attach_otp_info_msg(kr); ++ ret = k5c_attach_otp_info_msg(kr); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "k5c_attach_otp_info_msg failed.\n"); ++ goto done; ++ } + } + + if (kr->password_prompting) { +@@ -1651,6 +1902,15 @@ static errno_t tgt_req_child(struct krb5_req *kr) + goto done; + } + } ++ ++ if (kr->pkinit_prompting) { ++ ret = pam_add_response(kr->pd, SSS_CERT_AUTH_PROMPTING, 0, ++ NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); ++ goto done; ++ } ++ } + } else { + if (kerr == 0) { + kerr = k5c_attach_ccname_msg(kr); +@@ -1918,6 +2178,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, + *offline ? "true" : "false", kr->upn ? kr->upn : "none"); + + if (pd->cmd == SSS_PAM_AUTHENTICATE || ++ pd->cmd == SSS_PAM_PREAUTH || + pd->cmd == SSS_CMD_RENEW || + pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || pd->cmd == SSS_PAM_CHAUTHTOK) { + SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p); +@@ -2801,11 +3062,16 @@ int main(int argc, const char *argv[]) + goto done; + } + +- kerr = become_user(kr->uid, kr->gid); +- if (kerr != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); +- ret = EFAULT; +- goto done; ++ /* pkinit need access to pcscd */ ++ 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); ++ if (kerr != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n"); ++ ret = EFAULT; ++ goto done; ++ } + } + + DEBUG(SSSDBG_TRACE_INTERNAL, +diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c +index 6a3dc9d739527caad2523d9359cd5ef60e622bab..680e67b089fcb32280352af24aae35af133a52f3 100644 +--- a/src/providers/krb5/krb5_child_handler.c ++++ b/src/providers/krb5/krb5_child_handler.c +@@ -90,7 +90,9 @@ static errno_t pack_authtok(struct io_buffer *buf, size_t *rp, + if (ret == EOK) { + SAFEALIGN_COPY_UINT32(&buf->data[*rp], &auth_token_type, rp); + SAFEALIGN_COPY_UINT32(&buf->data[*rp], &auth_token_length, rp); +- safealign_memcpy(&buf->data[*rp], data, auth_token_length, rp); ++ if (data != NULL) { ++ safealign_memcpy(&buf->data[*rp], data, auth_token_length, rp); ++ } + } + + return ret; +@@ -145,6 +147,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + buf->size = 8*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) { +@@ -187,6 +190,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr, + safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp); + + 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) { +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index b6610bc6d694fae385f462e1be53c78af0a3d8cb..8091e11515184dc9b7f32eed535055d9eee3143f 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -431,6 +431,12 @@ enum response_type { + * SSS_PAM_OTP_INFO to determine the type of + * prompting. There is no message. + * @param None. */ ++ SSS_CERT_AUTH_PROMPTING, /**< Indicates that on the server side ++ * Smartcard/certificate based authentication is ++ * available for the selected account. This might ++ * be used together with other prompting options ++ * to determine the type of prompting. ++ * @param None. */ + }; + + /** +-- +2.9.3 + diff --git a/0087-TESTS-Remove-unused-import.patch b/0087-TESTS-Remove-unused-import.patch new file mode 100644 index 0000000..d9754fc --- /dev/null +++ b/0087-TESTS-Remove-unused-import.patch @@ -0,0 +1,28 @@ +From 1b55ac98db6a319d45edae6c27ff3804f1f4d28a Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 22 Feb 2017 18:44:55 +0100 +Subject: [PATCH 87/95] TESTS: Remove unused import +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Fabiano Fidêncio +--- + src/tests/intg/test_ts_cache.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/tests/intg/test_ts_cache.py b/src/tests/intg/test_ts_cache.py +index ce0a7c78daf46fd33440aad55ad8f31fa68228a8..445cdf6086302c9436c5f2a9a73f0b04e81adbe6 100644 +--- a/src/tests/intg/test_ts_cache.py ++++ b/src/tests/intg/test_ts_cache.py +@@ -33,7 +33,6 @@ import ldap_ent + import sssd_ldb + import sssd_id + from util import unindent +-from util import run_shell + + LDAP_BASE_DN = "dc=example,dc=com" + SSSD_DOMAIN = "LDAP" +-- +2.9.3 + diff --git a/0088-DOC-Deprecate-README-add-README.md.patch b/0088-DOC-Deprecate-README-add-README.md.patch new file mode 100644 index 0000000..0252e4b --- /dev/null +++ b/0088-DOC-Deprecate-README-add-README.md.patch @@ -0,0 +1,111 @@ +From eed5bc53a0c823276523d32e76bc1c264db3837e Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 24 Feb 2017 09:22:20 +0100 +Subject: [PATCH 88/95] DOC: Deprecate README, add README.md +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To make it easier to display the contents of README on the project +homepage, this patch converts the README contents to README.md. + +The original README is removed so that we don't maintain two different +sources. + +The links to fedorahosted are retained until we migrate the wiki pages. + +Reviewed-by: Lukáš Slebodník +Reviewed-by: Fabiano Fidêncio +--- + README | 43 ------------------------------------------- + README.md | 28 ++++++++++++++++++++++++++++ + 2 files changed, 28 insertions(+), 43 deletions(-) + delete mode 100644 README + create mode 100644 README.md + +diff --git a/README b/README +deleted file mode 100644 +index 189f66fe5b20c85728ac2640d734e3b490e23817..0000000000000000000000000000000000000000 +--- a/README ++++ /dev/null +@@ -1,43 +0,0 @@ +- +- SSSD - System Security Services Daemon +- -------------------------------------- +- +- Introduction +- ------------ +- SSSD provides a set of daemons to manage access to remote directories and +- authentication mechanisms such as LDAP, Kerberos or FreeIPA. It provides +- an NSS and PAM interface toward the system and a pluggable backend system +- to connect to multiple different account sources. +- +- More information about SSSD can be found on its project page - +- +- +- Building and installation +- ------------------------- +- Please see the file BUILD.txt for details +- +- Documentation +- ------------- +- The most up-to-date documentation can be found at +- +- +- Licensing +- --------- +- Please see the file called COPYING. +- +- Contacts +- -------- +- There are several ways to contact us: +- +- * the sssd-devel mailing list: +- Development of the System Security Services Daemon +- +- +- * the sssd-users mailing list: +- End-user discussions about the System Security Services Daemon +- +- +- * the #sssd and #freeipa IRC channels on freenode: +- irc://irc.freenode.net/sssd +- irc://irc.freenode.net/freeipa +- +diff --git a/README.md b/README.md +new file mode 100644 +index 0000000000000000000000000000000000000000..40c88c08070b986a634496ac2d6ffba8643bd8a7 +--- /dev/null ++++ b/README.md +@@ -0,0 +1,28 @@ ++# SSSD - System Security Services Daemon ++ ++## Introduction ++SSSD provides a set of daemons to manage access to remote directories and ++authentication mechanisms such as LDAP, Kerberos or FreeIPA. It provides ++an NSS and PAM interface toward the system and a pluggable backend system ++to connect to multiple different account sources. ++ ++More information about SSSD can be found on its project page - ++https://pagure.io/SSSD/sssd/ ++ ++## Building and installation ++Please see the file BUILD.txt for details ++ ++## Documentation ++The most up-to-date documentation can be found at https://fedorahosted.org/sssd/wiki/Documentation ++ ++## Licensing ++Please see the file called COPYING. ++ ++## Contacts ++There are several ways to contact us: ++ ++* the sssd-devel mailing list: [Development of the System Security Services Daemon](https://fedorahosted.org/mailman/listinfo/sssd-devel) ++* the sssd-users mailing list: [End-user discussions about the System Security Services Daemon](https://fedorahosted.org/mailman/listinfo/sssd-users) ++* the #sssd and #freeipa IRC channels on freenode: ++ * irc://irc.freenode.net/sssd ++ * irc://irc.freenode.net/freeipa +-- +2.9.3 + diff --git a/0089-MONITOR-Enable-an-implicit-files-domain-if-one-is-no.patch b/0089-MONITOR-Enable-an-implicit-files-domain-if-one-is-no.patch new file mode 100644 index 0000000..17dbe14 --- /dev/null +++ b/0089-MONITOR-Enable-an-implicit-files-domain-if-one-is-no.patch @@ -0,0 +1,397 @@ +From 78bb3676fe8326e0fe2b60daad8bf524e4625d4e Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Feb 2017 16:34:45 +0100 +Subject: [PATCH 89/95] MONITOR: Enable an implicit files domain if one is not + configured +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If SSSD is compiled with --enable-files-domain, the loading of the +domains changes such that: + * if no domain with id_provider=files exists in the config file, an + implicit SSSD files domain is added + * this domain is always first in the list + +The administrator is free to create a files domain in the config file +himself and either place it at the end of the list or not enable it at +all. + +Resolves: +https://pagure.io/SSSD/sssd/issue/3112 + +Reviewed-by: Pavel Březina +--- + src/conf_macros.m4 | 13 +++ + src/confdb/confdb.c | 182 +++++++++++++++++++++++++++++++++++ + src/confdb/confdb.h | 4 + + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/SSSDConfigTest.py | 3 +- + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.conf | 1 + + src/man/Makefile.am | 7 +- + src/man/sssd.conf.5.xml | 17 ++++ + src/monitor/monitor.c | 11 +++ + 10 files changed, 238 insertions(+), 2 deletions(-) + +diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 +index 427b0e08d400d6e5628537b28bb93bc2fc6239a4..749e7694f4dd7086468e461194ef274be2094236 100644 +--- a/src/conf_macros.m4 ++++ b/src/conf_macros.m4 +@@ -903,3 +903,16 @@ AC_DEFUN([WITH_SECRETS_DB_PATH], + AC_SUBST(secdbpath) + AC_DEFINE_UNQUOTED(SECRETS_DB_PATH, "$config_secdbpath", [Path to the SSSD Secrets databases]) + ]) ++ ++AC_ARG_ENABLE([files-domain], ++ [AS_HELP_STRING([--enable-files-domain], ++ [If this feature is enabled, then SSSD always enables ++ a domain with id_provider=files even if the domain ++ is not specified in the config file ++ [default=no]])], ++ [enable_files_domain=$enableval], ++ [enable_files_domain=no]) ++AS_IF([test x$enable_files_domain = xyes], ++ AC_DEFINE_UNQUOTED([ADD_FILES_DOMAIN], [1], ++ [whether to build unconditionally enable files domain])) ++AM_CONDITIONAL([ADD_FILES_DOMAIN], [test x$enable_files_domain = xyes]) +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index c7afd683d7f21b513bb491adbf7f7bbe79786212..d82fd98ee02928b3c20df014528bd869ec946f92 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -1643,3 +1643,185 @@ done: + talloc_free(tmp_ctx); + return ret; + } ++ ++#ifdef ADD_FILES_DOMAIN ++static int confdb_has_files_domain(struct confdb_ctx *cdb) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct ldb_dn *dn = NULL; ++ struct ldb_result *res = NULL; ++ static const char *attrs[] = { CONFDB_DOMAIN_ID_PROVIDER, NULL }; ++ const char *id_provider = NULL; ++ int ret; ++ unsigned int i; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new(tmp_ctx, cdb->ldb, CONFDB_DOMAIN_BASEDN); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL, ++ attrs, NULL); ++ if (ret != LDB_SUCCESS) { ++ ret = EIO; ++ goto done; ++ } ++ ++ for (i = 0; i < res->count; i++) { ++ id_provider = ldb_msg_find_attr_as_string(res->msgs[i], ++ CONFDB_DOMAIN_ID_PROVIDER, ++ NULL); ++ if (id_provider == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "The object [%s] doesn't have a id_provider\n", ++ ldb_dn_get_linearized(res->msgs[i]->dn)); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ if (strcasecmp(id_provider, "files") == 0) { ++ break; ++ } ++ } ++ ++ ret = i < res->count ? EOK : ENOENT; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static int create_files_domain(struct confdb_ctx *cdb, ++ const char *name) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ errno_t ret; ++ char *cdb_path = NULL; ++ const char *val[2] = { NULL, NULL }; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); ++ return ENOMEM; ++ } ++ ++ cdb_path = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, name); ++ if (cdb_path == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ val[0] = "files"; ++ ret = confdb_add_param(cdb, true, cdb_path, "id_provider", val); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add id_provider [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++static int activate_files_domain(struct confdb_ctx *cdb, ++ const char *name) ++{ ++ errno_t ret; ++ TALLOC_CTX *tmp_ctx; ++ char *monitor_domlist; ++ const char *domlist[2] = { NULL, NULL }; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ ret = confdb_get_string(cdb, tmp_ctx, ++ CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_MONITOR_ACTIVE_DOMAINS, ++ NULL, ++ &monitor_domlist); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n"); ++ goto done; ++ } ++ ++ if (monitor_domlist != NULL) { ++ domlist[0] = talloc_asprintf(tmp_ctx, "%s,%s", name, monitor_domlist); ++ if (domlist[0] == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ } else { ++ domlist[0] = name; ++ } ++ ++ ret = confdb_add_param(cdb, true, ++ CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_MONITOR_ACTIVE_DOMAINS, ++ domlist); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot extend the domain list [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ return ret; ++ } ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++#endif /* ADD_FILES_DOMAIN */ ++ ++int confdb_ensure_files_domain(struct confdb_ctx *cdb, ++ const char *implicit_files_dom_name) ++{ ++#ifndef ADD_FILES_DOMAIN ++ return EOK; ++#else ++ errno_t ret; ++ bool enable_files; ++ ++ ret = confdb_get_bool(cdb, ++ CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_MONITOR_ENABLE_FILES_DOM, ++ true, &enable_files); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot get the value of %s assuming true\n", ++ CONFDB_MONITOR_ENABLE_FILES_DOM); ++ return ret; ++ } ++ ++ if (enable_files == false) { ++ DEBUG(SSSDBG_CONF_SETTINGS, "The implicit files domain is disabled\n"); ++ return EOK; ++ } ++ ++ ret = confdb_has_files_domain(cdb); ++ if (ret == EOK) { ++ DEBUG(SSSDBG_CONF_SETTINGS, "The files domain is already enabled\n"); ++ return EOK; ++ } else if (ret != ENOENT) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up the files domain\n"); ++ return ret; ++ } ++ ++ /* ENOENT, so let's add a files domain */ ++ ret = create_files_domain(cdb, implicit_files_dom_name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add an implicit files domain\n"); ++ return ret; ++ } ++ ++ return activate_files_domain(cdb, implicit_files_dom_name); ++#endif /* ADD_FILES_DOMAIN */ ++} +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 353dfd0a9afbcaba49fcfdc7930026bb2eebfc9e..89b89bf0d54af03cb2f28f421991231f0c7b755f 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -73,6 +73,7 @@ + #define CONFDB_MONITOR_USER_RUNAS "user" + #define CONFDB_MONITOR_CERT_VERIFICATION "certificate_verification" + #define CONFDB_MONITOR_DISABLE_NETLINK "disable_netlink" ++#define CONFDB_MONITOR_ENABLE_FILES_DOM "enable_files_domain" + + /* Both monitor and domains */ + #define CONFDB_NAME_REGEX "re_expression" +@@ -373,6 +374,9 @@ int confdb_get_domain(struct confdb_ctx *cdb, + int confdb_get_domains(struct confdb_ctx *cdb, + struct sss_domain_info **domains); + ++int confdb_ensure_files_domain(struct confdb_ctx *cdb, ++ const char *implicit_files_dom_name); ++ + /** + * 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/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index 8c23fd271334acca6dbaf7df5d5aab15b3af5a42..44fb777eccd24d511067fee850f01ba9f3c1f134 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -64,6 +64,7 @@ option_strings = { + 'certificate_verification' : _('Tune certificate verification'), + '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'), + + # [nss] + 'enum_cache_timeout' : _('Enumeration cache timeout length (seconds)'), +diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py +index 0da5d63a198bf7242daf7f985ede8e0bc3df3841..8cb03adcbb55c4ca350af1504c0cb3ea1a2d236d 100755 +--- a/src/config/SSSDConfigTest.py ++++ b/src/config/SSSDConfigTest.py +@@ -312,7 +312,8 @@ class SSSDConfigTestSSSDService(unittest.TestCase): + 'description', + 'certificate_verification', + 'override_space', +- 'disable_netlink'] ++ 'disable_netlink', ++ 'enable_files_domain'] + + 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 51981c3d0fc89d3f091dbe182cdd1d6de5618e1e..dd0f04b1a829c941215e96e1a160aeba7759e2bf 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -41,6 +41,7 @@ option = certificate_verification + option = override_space + option = config_file_version + option = disable_netlink ++option = enable_files_domain + + [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 56540066fde71cad53f909e3534b3a674633af3f..7d21d6b7016e51021d168243e5bfd36e999801e4 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -30,6 +30,7 @@ default_domain_suffix = str, None, false + certificate_verification = str, None, false + override_space = str, None, false + disable_netlink = bool, None, false ++enable_files_domain = str, None, false + + [nss] + # Name service +diff --git a/src/man/Makefile.am b/src/man/Makefile.am +index 760bb7831b5852e1bf3be497ad5babdb4f4318c2..215ce693b56e74db394dbc238c03c87f5f6efe99 100644 +--- a/src/man/Makefile.am ++++ b/src/man/Makefile.am +@@ -35,7 +35,12 @@ endif + if HAVE_SYSTEMD_UNIT + SYSTEMD_CONDS = ;have_systemd + endif +-CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS) ++if ADD_FILES_DOMAIN ++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) + + + #Special Rules: +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 782aef7bf62d9bcef3c3dab534a25e01d85c1764..2a2ef69ff2da817087d60e81cff8075e91736bae 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -525,6 +525,23 @@ + + + ++ ++ enable_files_domain (boolean) ++ ++ ++ When this option is enabled, SSSD ++ prepends an implicit domain with ++ id_provider=files before ++ any explicitly configured domains. ++ ++ ++ Default: false ++ ++ ++ Default: true ++ ++ ++ + + + +diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c +index 59bf70741b76871c4937ad75c448aaf776cc37eb..7e7b5a07d11aecf1c0b11592213b90d385fd5076 100644 +--- a/src/monitor/monitor.c ++++ b/src/monitor/monitor.c +@@ -90,6 +90,9 @@ + "that the file is accessible only by the "\ + "owner and owned by root.root.\n" + ++/* SSSD domain name that is used for the auto-configured files domain */ ++#define IMPLICIT_FILES_DOMAIN_NAME "implicit_files" ++ + int cmdline_debug_level; + int cmdline_debug_timestamps; + int cmdline_debug_microseconds; +@@ -1053,6 +1056,14 @@ static int get_monitor_config(struct mt_ctx *ctx) + return ret; + } + ++ ret = confdb_ensure_files_domain(ctx->cdb, IMPLICIT_FILES_DOMAIN_NAME); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Cannot add the implicit files domain [%d]: %s\n", ++ ret, strerror(ret)); ++ /* Not fatal */ ++ } ++ + 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/0090-TESTS-Enable-the-files-domain-for-all-integration-te.patch b/0090-TESTS-Enable-the-files-domain-for-all-integration-te.patch new file mode 100644 index 0000000..e1330c8 --- /dev/null +++ b/0090-TESTS-Enable-the-files-domain-for-all-integration-te.patch @@ -0,0 +1,62 @@ +From 76b6d7fb9f31f7836158d248161aec3558098659 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Feb 2017 21:05:25 +0100 +Subject: [PATCH 90/95] TESTS: Enable the files domain for all integration + tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is done to make sure that enabling the files domain doesn't break +existing functionality as well as making it possible to even that the +implicit domain, since all integration tests use the same configuration. + +Reviewed-by: Pavel Březina +--- + Makefile.am | 1 + + src/tests/intg/test_enumeration.py | 10 +++++++++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index e676e18415c9d20ffd5ba2ce825dddd62d50c909..30ee5e5904a06609a03343bb3dc5b78ef169d4b4 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -3239,6 +3239,7 @@ intgcheck-prepare: + --with-ldb-lib-dir="$$prefix"/lib/ldb \ + --enable-intgcheck-reqs \ + --without-semanage \ ++ --enable-files-domain \ + $(INTGCHECK_CONFIGURE_FLAGS); \ + $(MAKE) $(AM_MAKEFLAGS); \ + : Force single-thread install to workaround concurrency issues; \ +diff --git a/src/tests/intg/test_enumeration.py b/src/tests/intg/test_enumeration.py +index 5cb6c3e07435fea802f3f925e370605a0eb36d2c..47772dea288434c5b213eeba9b4eac904423d707 100644 +--- a/src/tests/intg/test_enumeration.py ++++ b/src/tests/intg/test_enumeration.py +@@ -98,7 +98,12 @@ SCHEMA_RFC2307_BIS = "rfc2307bis" + + + def format_basic_conf(ldap_conn, schema): +- """Format a basic SSSD configuration""" ++ """ ++ Format a basic SSSD configuration ++ ++ The files domain is defined but not enabled in order to avoid enumerating ++ users from the files domain that would otherwise by implicitly enabled ++ """ + schema_conf = "ldap_schema = " + schema + "\n" + if schema == SCHEMA_RFC2307_BIS: + schema_conf += "ldap_group_object_class = groupOfNames\n" +@@ -115,6 +120,9 @@ def format_basic_conf(ldap_conn, schema): + [pam] + debug_level = 0xffff + ++ [domain/files] ++ id_provider = files ++ + [domain/LDAP] + ldap_auth_disable_tls_never_use_in_production = true + debug_level = 0xffff +-- +2.9.3 + diff --git a/0091-TESTS-Test-the-files-domain-autoconfiguration.patch b/0091-TESTS-Test-the-files-domain-autoconfiguration.patch new file mode 100644 index 0000000..09a6457 --- /dev/null +++ b/0091-TESTS-Test-the-files-domain-autoconfiguration.patch @@ -0,0 +1,111 @@ +From 13294bedc56faf1011f5ba7b1ed9a53b08e71c00 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Tue, 21 Feb 2017 21:04:29 +0100 +Subject: [PATCH 91/95] TESTS: Test the files domain autoconfiguration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Adds tests that exercise the implicit files domain. + +Reviewed-by: Pavel Březina +--- + src/tests/intg/test_files_provider.py | 78 +++++++++++++++++++++++++++++++++++ + 1 file changed, 78 insertions(+) + +diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py +index 528b5e5acc5078cce5bfab9a5f9dab5509e580eb..abf836bcc7a1810ea505dcf84ad1edfadf0c382c 100644 +--- a/src/tests/intg/test_files_provider.py ++++ b/src/tests/intg/test_files_provider.py +@@ -125,6 +125,48 @@ def files_domain_only(request): + return None + + ++@pytest.fixture ++def no_sssd_domain(request): ++ conf = unindent("""\ ++ [sssd] ++ services = nss ++ """).format(**locals()) ++ create_conf_fixture(request, conf) ++ create_sssd_fixture(request) ++ return None ++ ++ ++@pytest.fixture ++def no_files_domain(request): ++ conf = unindent("""\ ++ [sssd] ++ domains = local ++ services = nss ++ ++ [domain/local] ++ id_provider = local ++ """).format(**locals()) ++ create_conf_fixture(request, conf) ++ create_sssd_fixture(request) ++ return None ++ ++ ++@pytest.fixture ++def disabled_files_domain(request): ++ conf = unindent("""\ ++ [sssd] ++ domains = local ++ services = nss ++ enable_files_domain = false ++ ++ [domain/local] ++ id_provider = local ++ """).format(**locals()) ++ create_conf_fixture(request, conf) ++ create_sssd_fixture(request) ++ return None ++ ++ + def setup_pw_with_list(request, user_list): + pwd_ops = passwd_ops_setup(request) + for user in user_list: +@@ -754,3 +796,39 @@ def test_realloc_groups(setup_gr_with_canary, files_domain_only): + check for off-by-one errors. + """ + realloc_groups(setup_gr_with_canary, FILES_REALLOC_CHUNK*3) ++ ++ ++# Files domain autoconfiguration tests ++def test_no_sssd_domain(add_user_with_canary, no_sssd_domain): ++ """ ++ Test that if no sssd domain is configured, sssd will add the implicit one ++ """ ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert user == USER1 ++ ++ ++def test_no_files_domain(add_user_with_canary, no_files_domain): ++ """ ++ Test that if no files domain is configured, sssd will add the implicit one ++ before any explicitly configured domains ++ """ ++ # Add a user with a different UID than the one in files ++ subprocess.check_call( ++ ["sss_useradd", "-u", "10009", "-M", USER1["name"]]) ++ ++ # Even though the local domain is the only one configured, ++ # files will be resolved first ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert user == USER1 ++ ++ ++def test_disable_files_domain(add_user_with_canary, disabled_files_domain): ++ """ ++ Test that if no files domain is configured, sssd will add the implicit one ++ before any explicitly configured domains ++ """ ++ # The local user will not be resolvable through nss_sss now ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res != NssReturnCode.SUCCESS +-- +2.9.3 + diff --git a/0092-CONFDB-Refactor-reading-the-config-file.patch b/0092-CONFDB-Refactor-reading-the-config-file.patch new file mode 100644 index 0000000..46c4752 --- /dev/null +++ b/0092-CONFDB-Refactor-reading-the-config-file.patch @@ -0,0 +1,247 @@ +From 5a660d3aa67403fba69a8047ecedfe8a4276fc30 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 23 Feb 2017 12:02:06 +0100 +Subject: [PATCH 92/95] CONFDB: Refactor reading the config file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is in preparation for creating a fallback configuration + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb_setup.c | 146 +++++++++++++++++++++++++++------------------- + 1 file changed, 85 insertions(+), 61 deletions(-) + +diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c +index a71d9dd1202824b3c9a7e69f1d8fa905ac1b8c02..c040bdf9e59f47541f2d598a37699d035eb9e566 100644 +--- a/src/confdb/confdb_setup.c ++++ b/src/confdb/confdb_setup.c +@@ -126,69 +126,39 @@ static int confdb_create_base(struct confdb_ctx *cdb) + return EOK; + } + +-static int confdb_init_db(const char *config_file, const char *config_dir, +- struct confdb_ctx *cdb) ++static int confdb_ldif_from_ini_file(TALLOC_CTX *mem_ctx, ++ const char *config_file, ++ const char *config_dir, ++ struct sss_ini_initdata *init_data, ++ const char **_timestr, ++ const char **_ldif) + { +- TALLOC_CTX *tmp_ctx; +- int ret; +- int sret = EOK; +- int version; ++ errno_t ret; + char timestr[21]; +- bool in_transaction = false; +- const char *config_ldif; +- const char *vals[2] = { timestr, NULL }; +- struct ldb_ldif *ldif; +- struct sss_ini_initdata *init_data; +- +- tmp_ctx = talloc_new(cdb); +- if (tmp_ctx == NULL) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n"); +- return ENOMEM; +- } +- +- init_data = sss_ini_initdata_init(tmp_ctx); +- if (!init_data) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n"); +- ret = ENOMEM; +- goto done; +- } +- +- /* Open config file */ +- ret = sss_ini_config_file_open(init_data, config_file); +- if (ret != EOK) { +- DEBUG(SSSDBG_TRACE_FUNC, +- "sss_ini_config_file_open failed: %s [%d]\n", strerror(ret), +- ret); +- if (ret == ENOENT) { +- /* sss specific error denoting missing configuration file */ +- ret = ERR_MISSING_CONF; +- } +- goto done; +- } ++ int version; + + ret = sss_ini_config_access_check(init_data); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Permission check on config file failed.\n"); +- ret = EPERM; +- goto done; ++ return EPERM; + } + + ret = sss_ini_get_stat(init_data); + if (ret != EOK) { +- DEBUG(SSSDBG_FATAL_FAILURE, +- "Status check on config file failed.\n"); + ret = errno; +- goto done; ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Status check on config file failed.\n"); ++ return ret; + } + + errno = 0; +- + ret = sss_ini_get_mtime(init_data, sizeof(timestr), timestr); + if (ret <= 0 || ret >= (int)sizeof(timestr)) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to convert time_t to string ??\n"); + ret = errno ? errno : EFAULT; ++ return ret; + } + + /* FIXME: Determine if the conf file or any snippet has changed +@@ -199,7 +169,7 @@ static int confdb_init_db(const char *config_file, const char *config_dir, + ret = sss_ini_get_config(init_data, config_file, config_dir); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to load configuration\n"); +- goto done; ++ return ret; + } + + ret = sss_ini_call_validators(init_data, +@@ -214,7 +184,7 @@ static int confdb_init_db(const char *config_file, const char *config_dir, + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Internal error determining config_file_version\n"); +- goto done; ++ return ret; + } + + ret = sss_ini_check_config_obj(init_data); +@@ -230,21 +200,82 @@ static int confdb_init_db(const char *config_file, const char *config_dir, + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Config file version could not be determined\n"); +- goto done; ++ return ret; + } else if (version < CONFDB_VERSION_INT) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Config file is an old version. " + "Please run configuration upgrade script.\n"); +- ret = EINVAL; +- goto done; ++ return EINVAL; + } else if (version > CONFDB_VERSION_INT) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Config file version is newer than confdb\n"); +- ret = EINVAL; +- goto done; ++ return EINVAL; + } + } + ++ ret = sss_confdb_create_ldif(mem_ctx, init_data, _ldif); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Could not create LDIF for confdb\n"); ++ return ret; ++ } ++ ++ *_timestr = talloc_strdup(mem_ctx, timestr); ++ if (*_timestr == NULL) { ++ return ENOMEM; ++ } ++ ++ return EOK; ++} ++ ++static int confdb_init_db(const char *config_file, const char *config_dir, ++ struct confdb_ctx *cdb) ++{ ++ TALLOC_CTX *tmp_ctx; ++ int ret; ++ int sret = EOK; ++ bool in_transaction = false; ++ const char *timestr = NULL; ++ const char *config_ldif; ++ const char *vals[2] = { NULL, NULL }; ++ struct ldb_ldif *ldif; ++ struct sss_ini_initdata *init_data; ++ ++ tmp_ctx = talloc_new(cdb); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n"); ++ return ENOMEM; ++ } ++ ++ init_data = sss_ini_initdata_init(tmp_ctx); ++ if (!init_data) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ /* Open config file */ ++ ret = sss_ini_config_file_open(init_data, config_file); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "sss_ini_config_file_open failed: %s [%d]\n", sss_strerror(ret), ++ ret); ++ goto done; ++ } ++ ++ ret = confdb_ldif_from_ini_file(tmp_ctx, ++ config_file, ++ config_dir, ++ init_data, ++ ×tr, ++ &config_ldif); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert INI to LDIF [%d]: [%s]\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ DEBUG(SSSDBG_CONF_SETTINGS, "LDIF file to import: \n%s\n", config_ldif); ++ + /* Set up a transaction to replace the configuration */ + ret = ldb_transaction_start(cdb->ldb); + if (ret != LDB_SUCCESS) { +@@ -264,20 +295,12 @@ static int confdb_init_db(const char *config_file, const char *config_dir, + goto done; + } + +- ret = sss_confdb_create_ldif(tmp_ctx, init_data, &config_ldif); +- if (ret != EOK) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Could not create LDIF for confdb\n"); +- goto done; +- } +- +- DEBUG(SSSDBG_TRACE_LIBS, "LDIF file to import: \n%s\n", config_ldif); +- + while ((ldif = ldb_ldif_read_string(cdb->ldb, &config_ldif))) { + ret = ldb_add(cdb->ldb, ldif->msg); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to initialize DB (%d,[%s]), aborting!\n", +- ret, ldb_errstring(cdb->ldb)); ++ "Failed to initialize DB (%d,[%s]), aborting!\n", ++ ret, ldb_errstring(cdb->ldb)); + ret = EIO; + goto done; + } +@@ -287,10 +310,11 @@ static int confdb_init_db(const char *config_file, const char *config_dir, + /* now store the lastUpdate time so that we do not re-init if nothing + * changed on restart */ + ++ vals[0] = timestr; + ret = confdb_add_param(cdb, true, "config", "lastUpdate", vals); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, +- "Failed to set last update time on db!\n"); ++ "Failed to set last update time on db!\n"); + goto done; + } + +-- +2.9.3 + diff --git a/0093-CONFDB-If-no-configuration-file-is-provided-create-a.patch b/0093-CONFDB-If-no-configuration-file-is-provided-create-a.patch new file mode 100644 index 0000000..f4eb971 --- /dev/null +++ b/0093-CONFDB-If-no-configuration-file-is-provided-create-a.patch @@ -0,0 +1,151 @@ +From a4837791f62283079e7be4b17efb769be8b2dfd1 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Thu, 23 Feb 2017 12:22:49 +0100 +Subject: [PATCH 93/95] CONFDB: If no configuration file is provided, create a + fallback configuration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This functionality is only enabled in case SSSD is configured with with +--enable-files-domain. If not, the behaviour is as it used to -- SSSD +returns an error, instructing the admin to create a configuration file. + +If the option is enabled, a very minimal confdb that only enables the +NSS responder is created. The confdb later adds the implicit files +domain. + +Resolves: +https://pagure.io/SSSD/sssd/issue/2229 + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb_setup.c | 60 +++++++++++++++++++++++++++-------- + src/tests/intg/test_files_provider.py | 16 ++++++++++ + 2 files changed, 63 insertions(+), 13 deletions(-) + +diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c +index c040bdf9e59f47541f2d598a37699d035eb9e566..7884eea6367390c41f64b7023fe505dd653b49db 100644 +--- a/src/confdb/confdb_setup.c ++++ b/src/confdb/confdb_setup.c +@@ -28,6 +28,14 @@ + #include "confdb_setup.h" + #include "util/sss_ini.h" + ++#ifndef SSSD_FALLBACK_CONFIG_LDIF ++#define SSSD_FALLBACK_CONFIG_LDIF \ ++"dn: cn=config\n" \ ++"version: 2\n\n" \ ++"dn: cn=sssd,cn=config\n" \ ++"cn: sssd\n" \ ++"services: nss\n\n" ++#endif /* SSSD_FALLBACK_CONFIG_LDIF */ + + static int confdb_test(struct confdb_ctx *cdb) + { +@@ -227,6 +235,23 @@ static int confdb_ldif_from_ini_file(TALLOC_CTX *mem_ctx, + return EOK; + } + ++static int confdb_fallback_ldif(TALLOC_CTX *mem_ctx, ++ const char **_timestr, ++ const char **_ldif) ++{ ++#ifndef ADD_FILES_DOMAIN ++ return ERR_MISSING_CONF; ++#else ++ *_timestr = talloc_strdup(mem_ctx, "1"); ++ *_ldif = talloc_strdup(mem_ctx, SSSD_FALLBACK_CONFIG_LDIF); ++ if (*_timestr == NULL || *_ldif == NULL) { ++ return ENOMEM; ++ } ++ ++ return EOK; ++#endif ++} ++ + static int confdb_init_db(const char *config_file, const char *config_dir, + struct confdb_ctx *cdb) + { +@@ -255,25 +280,34 @@ static int confdb_init_db(const char *config_file, const char *config_dir, + + /* Open config file */ + ret = sss_ini_config_file_open(init_data, config_file); +- if (ret != EOK) { ++ if (ret == EOK) { ++ ret = confdb_ldif_from_ini_file(tmp_ctx, ++ config_file, ++ config_dir, ++ init_data, ++ ×tr, ++ &config_ldif); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot convert INI to LDIF [%d]: [%s]\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ } else if (ret == ENOENT) { ++ ret = confdb_fallback_ldif(tmp_ctx, ×tr, &config_ldif); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Cannot create a fallback configuration [%d]: [%s]\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ } else { + DEBUG(SSSDBG_CONF_SETTINGS, + "sss_ini_config_file_open failed: %s [%d]\n", sss_strerror(ret), + ret); + goto done; + } + +- ret = confdb_ldif_from_ini_file(tmp_ctx, +- config_file, +- config_dir, +- init_data, +- ×tr, +- &config_ldif); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Cannot convert INI to LDIF [%d]: [%s]\n", +- ret, sss_strerror(ret)); +- goto done; +- } + DEBUG(SSSDBG_CONF_SETTINGS, "LDIF file to import: \n%s\n", config_ldif); + + /* Set up a transaction to replace the configuration */ +diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py +index abf836bcc7a1810ea505dcf84ad1edfadf0c382c..8748ac10b089087056b1b93950c8d890a190c8d0 100644 +--- a/src/tests/intg/test_files_provider.py ++++ b/src/tests/intg/test_files_provider.py +@@ -167,6 +167,12 @@ def disabled_files_domain(request): + return None + + ++@pytest.fixture ++def no_sssd_conf(request): ++ create_sssd_fixture(request) ++ return None ++ ++ + def setup_pw_with_list(request, user_list): + pwd_ops = passwd_ops_setup(request) + for user in user_list: +@@ -832,3 +838,13 @@ def test_disable_files_domain(add_user_with_canary, disabled_files_domain): + # The local user will not be resolvable through nss_sss now + res, user = sssd_getpwnam_sync(USER1["name"]) + assert res != NssReturnCode.SUCCESS ++ ++ ++def test_no_sssd_conf(add_user_with_canary, no_sssd_conf): ++ """ ++ Test that running without sssd.conf implicitly configures one with ++ id_provider=files ++ """ ++ res, user = sssd_getpwnam_sync(USER1["name"]) ++ assert res == NssReturnCode.SUCCESS ++ assert user == USER1 +-- +2.9.3 + diff --git a/0094-authtok-fix-tests-on-big-endian.patch b/0094-authtok-fix-tests-on-big-endian.patch new file mode 100644 index 0000000..d8aa265 --- /dev/null +++ b/0094-authtok-fix-tests-on-big-endian.patch @@ -0,0 +1,45 @@ +From 6b760226c0ed984b6b1a02f459e9cb1fc3d45ff9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 28 Feb 2017 15:54:06 +0100 +Subject: [PATCH] authtok: fix tests on big-endian + +Related to https://pagure.io/SSSD/sssd/issue/3270 +--- + src/tests/cmocka/test_authtok.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c +index b2ef08a1c75625253706910e0cc80f4d5d28be5d..4464230b34b1816ddeb98ec85abc1b59f1524e54 100644 +--- a/src/tests/cmocka/test_authtok.c ++++ b/src/tests/cmocka/test_authtok.c +@@ -604,8 +604,13 @@ void test_sss_authtok_sc_pin(void **state) + SSS_AUTHTOK_TYPE_SC_PIN); + size = sss_authtok_get_size(ts->authtoken); + assert_int_equal(size, 28); ++#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\0\0\0", + size); ++#else ++ assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\0\0\0\11\0\0\0\1\0\0\0\1\0\0\0\1" "12345678\0\0\0\0", ++ size); ++#endif + + ret = sss_authtok_set_sc_pin(ts->authtoken, "12345678", 5); + assert_int_equal(ret, EOK); +@@ -613,8 +618,13 @@ void test_sss_authtok_sc_pin(void **state) + SSS_AUTHTOK_TYPE_SC_PIN); + size = sss_authtok_get_size(ts->authtoken); + assert_int_equal(size, 25); ++#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\0\0\0", + size); ++#else ++ assert_memory_equal(sss_authtok_get_data(ts->authtoken), "\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\1" "12345\0\0\0\0", ++ size); ++#endif + + ret = sss_authtok_get_sc_pin(ts->authtoken, &pin, &len); + assert_int_equal(ret, EOK); +-- +2.9.3 + diff --git a/sssd.spec b/sssd.spec index 6ab321f..25b0a0e 100644 --- a/sssd.spec +++ b/sssd.spec @@ -26,7 +26,7 @@ Name: sssd Version: 1.15.0 -Release: 3%{?dist} +Release: 4%{?dist} Group: Applications/System Summary: System Security Services Daemon License: GPLv3+ @@ -35,7 +35,101 @@ Source0: https://fedorahosted.org/released/sssd/%{name}-%{version}.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) ### Patches ### -Patch0501: 0501-Partially-revert-CONFIG-Use-default-config-when-none.patch +Patch0001: 0001-Updating-the-version-to-track-the-1.15.1-release.patch +Patch0002: 0002-BUILD-Fix-linking-of-test_wbc_calls.patch +Patch0003: 0003-Suppres-implicit-fallthrough-from-gcc-7.patch +Patch0004: 0004-pam_sss-Suppress-warning-format-truncation.patch +Patch0005: 0005-TOOLS-Fix-warning-format-truncation.patch +Patch0006: 0006-sssctl-Fix-warning-may-be-used-uninitialized.patch +Patch0007: 0007-SBUS-remove-unused-symbols.patch +Patch0008: 0008-SBUS-use-sss_ptr_hash-for-opath-table.patch +Patch0009: 0009-SBUS-use-sss_ptr_hash-for-nodes-table.patch +Patch0010: 0010-SBUS-use-sss_ptr_hash-for-signals-table.patch +Patch0011: 0011-ldap_child-Fix-use-after-free.patch +Patch0012: 0012-FAILOVER-Improve-port-status-log-messages.patch +Patch0013: 0013-IFP-Update-ifp_iface_generated.c.patch +Patch0014: 0014-SYSTEMD-Update-journald-drop-in-file.patch +Patch0015: 0015-Partially-revert-CONFIG-Use-default-config-when-none.patch +Patch0016: 0016-SUDO-Add-skip_entry-boolean-to-sudo-conversions.patch +Patch0017: 0017-TESTS-Add-to-IPA-DN-test.patch +Patch0018: 0018-LDAP-Better-logging-message.patch +Patch0019: 0019-SYSDB-Removing-of-sysdb_try_to_find_expected_dn.patch +Patch0020: 0020-TEST-create_multidom_test_ctx-extending.patch +Patch0021: 0021-TESTS-Tests-for-sdap_search_initgr_user_in_batch.patch +Patch0022: 0022-ssh-fix-number-of-output-certificates.patch +Patch0023: 0023-ssh-do-not-create-again-fq-name.patch +Patch0024: 0024-sss_parse_inp_send-provide-default_domain-as-paramet.patch +Patch0025: 0025-cache_req-add-ability-to-not-use-default-domain-suff.patch +Patch0026: 0026-cache_req-search-user-by-name-with-attrs.patch +Patch0027: 0027-cache_req-add-api-to-create-ldb_result-from-message.patch +Patch0028: 0028-cache_req-move-dp-request-to-plugin.patch +Patch0029: 0029-cache_req-add-host-by-name-search.patch +Patch0030: 0030-ssh-rewrite-ssh-responder-to-use-cache_req.patch +Patch0031: 0031-AD-Use-ad_domain-to-match-forest-root-domain-not-the.patch +Patch0032: 0032-BUILD-Fix-linking-of-test_sdap_initgr.patch +Patch0033: 0033-ssh-fix-typo.patch +Patch0034: 0034-cache_req-always-go-to-dp-first-when-looking-up-host.patch +Patch0035: 0035-MONITOR-Wrap-up-sending-sd_notify-ready-into-a-new-f.patch +Patch0036: 0036-MONITOR-Don-t-timeout-if-using-local-provider-socket.patch +Patch0037: 0037-SUDO-Only-store-lowercased-attribute-value-once.patch +Patch0038: 0038-NEGCACHE-Add-API-to-reset-all-users-and-groups.patch +Patch0039: 0039-NSS-Add-sbus-interface-to-clear-memory-cache.patch +Patch0040: 0040-NSS-Rename-the-interface-to-invalidate-memory-cache-.patch +Patch0041: 0041-UTIL-Add-a-new-domain-state-called-DOM_INCONSISTENT.patch +Patch0042: 0042-RESPONDER-Add-a-responder-sbus-interface-to-set-doma.patch +Patch0043: 0043-RESPONDER-A-sbus-interface-to-reset-negatively-cache.patch +Patch0044: 0044-DP-Add-internal-DP-interface-to-set-domain-state.patch +Patch0045: 0045-DP-Add-internal-interface-to-reset-negative-cache-fr.patch +Patch0046: 0046-DP-Add-internal-interface-to-invalidate-memory-cache.patch +Patch0047: 0047-RESPONDER-Use-the-NEED_CHECK_DOMAIN-macro.patch +Patch0048: 0048-RESPONDER-Include-the-files-provider-in-NEEDS_CHECK_.patch +Patch0049: 0049-RESPONDER-Contact-inconsistent-domains.patch +Patch0050: 0050-UTIL-Add-a-generic-inotify-module.patch +Patch0051: 0051-CONFDB-Re-enable-the-files-provider.patch +Patch0052: 0052-FILES-Add-the-files-provider.patch +Patch0053: 0053-CONFDB-The-files-provider-always-enumerates.patch +Patch0054: 0054-CONFDB-Make-pwfield-configurable-per-domain.patch +Patch0055: 0055-CONFDB-The-files-domain-defaults-to-x-as-pwfield.patch +Patch0056: 0056-MAN-Document-the-pwfield-configuration-option.patch +Patch0057: 0057-TESTS-move-helper-fixtures-to-back-up-and-restore-a-.patch +Patch0058: 0058-TESTS-add-a-helper-module-with-shared-NSS-constants.patch +Patch0059: 0059-TESTS-Add-a-module-to-call-nss_sss-s-getpw-from-test.patch +Patch0060: 0060-TESTS-Add-a-module-to-call-nss_sss-s-getgr-from-test.patch +Patch0061: 0061-TESTS-Add-files-provider-integration-tests.patch +Patch0062: 0062-MONITOR-Remove-checks-for-sssd.conf-changes.patch +Patch0063: 0063-MONITOR-Use-the-common-inotify-code-to-watch-resolv..patch +Patch0064: 0064-MAN-Add-documentation-for-the-files-provider.patch +Patch0065: 0065-EXAMPLES-Do-not-point-to-id_provider-local.patch +Patch0066: 0066-SBUS-Document-how-to-free-the-result-of-sbus_create_.patch +Patch0067: 0067-IPA_SUDO-Unused-value-fix.patch +Patch0068: 0068-intg-Fix-python3-issues.patch +Patch0069: 0069-DYNDNS-Update-PTR-record-after-non-fatal-error.patch +Patch0070: 0070-DYNDNS-Correct-debug-log-message-of-realm.patch +Patch0071: 0071-sdap_extend_map-make-sure-memory-can-be-freed.patch +Patch0072: 0072-check_duplicate-check-name-member-before-using-it.patch +Patch0073: 0073-FILES-Fix-reallocation-logic.patch +Patch0074: 0074-pam_sss-check-conversation-callback.patch +Patch0075: 0075-MONITOR-Don-t-return-an-error-in-case-we-fail-to-reg.patch +Patch0076: 0076-FILES-Remove-unnecessary-check.patch +Patch0077: 0077-PAM-store-user-object-in-the-preq-context.patch +Patch0078: 0078-PAM-fix-memory-leak-in-pam_sss.patch +Patch0079: 0079-PAM-use-sentinel-error-code-in-PAM-tests.patch +Patch0080: 0080-utils-new-error-codes.patch +Patch0081: 0081-LDAP-proxy-tell-frontend-that-Smartcard-auth-is-not-.patch +Patch0082: 0082-authtok-enhance-support-for-Smartcard-auth-blobs.patch +Patch0083: 0083-PAM-forward-Smartcard-credentials-to-backends.patch +Patch0084: 0084-p11-return-name-of-PKCS-11-module-and-key-id-to-pam_.patch +Patch0085: 0085-pam-enhance-Smartcard-authentication-token.patch +Patch0086: 0086-KRB5-allow-pkinit-pre-authentication.patch +Patch0087: 0087-TESTS-Remove-unused-import.patch +Patch0088: 0088-DOC-Deprecate-README-add-README.md.patch +Patch0089: 0089-MONITOR-Enable-an-implicit-files-domain-if-one-is-no.patch +Patch0090: 0090-TESTS-Enable-the-files-domain-for-all-integration-te.patch +Patch0091: 0091-TESTS-Test-the-files-domain-autoconfiguration.patch +Patch0092: 0092-CONFDB-Refactor-reading-the-config-file.patch +Patch0093: 0093-CONFDB-If-no-configuration-file-is-provided-create-a.patch +Patch0094: 0094-authtok-fix-tests-on-big-endian.patch + Patch0502: 0502-SYSTEMD-Use-capabilities.patch ### Dependencies ### @@ -575,6 +669,7 @@ autoreconf -ivf --with-initscript=systemd \ --with-syslog=journald \ --enable-sss-default-nss-plugin \ + --enable-files-domain \ %{?with_cifs_utils_plugin_option} \ %{?enable_systemtap_opt} \ @@ -733,6 +828,7 @@ done %dir %{_libdir}/%{name} %{_libdir}/%{name}/libsss_simple.so +%{_libdir}/%{name}/libsss_files.so #Internal shared libraries %{_libdir}/%{name}/libsss_child.so @@ -783,6 +879,7 @@ done %{_mandir}/man1/sss_ssh_knownhostsproxy.1* %{_mandir}/man5/sssd.conf.5* %{_mandir}/man5/sssd-simple.5* +%{_mandir}/man5/sssd-files.5* %{_mandir}/man5/sssd-sudo.5* %{_mandir}/man5/sssd-secrets.5* %{_mandir}/man5/sss_rpcidmapd.5* @@ -1122,6 +1219,13 @@ fi %{_libdir}/%{name}/modules/libwbclient.so %changelog +* Wed Feb 22 2017 Jakub Hrozek - 1.15.0-4 +- Cherry-pick patches from upstream that enable the files provider +- Enable the files domain +- Retire patch 0501-Partially-revert-CONFIG-Use-default-config-when-none.patch + which is superseded by the files domain autoconfiguration +- Related: rhbz#1357418 - SSSD fast cache for local users + * Tue Feb 14 2017 Lukas Slebodnik - 1.15.0-3 - Add missing %%license macro