diff --git a/SOURCES/0009-kcm-decode-base64-encoded-secret-on-upgrade-path.patch b/SOURCES/0009-kcm-decode-base64-encoded-secret-on-upgrade-path.patch
new file mode 100644
index 0000000..032f1c4
--- /dev/null
+++ b/SOURCES/0009-kcm-decode-base64-encoded-secret-on-upgrade-path.patch
@@ -0,0 +1,43 @@
+From 18b98836ef8e337992f0ecb239a32b9c3cedb750 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Wed, 9 Dec 2020 14:07:22 +0100
+Subject: [PATCH] kcm: decode base64 encoded secret on upgrade path
+
+Previous unefficient code encoded the secret multiple times:
+  secret -> base64 -> masterkey -> base64
+
+To allow smooth upgrade for already existant ccache we need to also decode
+the secret if it is still in the old format (type == simple). Otherwise
+users are not able to log in.
+
+Resolves: https://github.com/SSSD/sssd/issues/5349
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/responder/kcm/kcmsrv_ccache_secdb.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c
+index 726711ac4..ea5c8f9ee 100644
+--- a/src/responder/kcm/kcmsrv_ccache_secdb.c
++++ b/src/responder/kcm/kcmsrv_ccache_secdb.c
+@@ -59,6 +59,16 @@ static errno_t sec_get(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    if (strcmp(datatype, "simple") == 0) {
++        /* The secret is stored in b64 encoding, we need to decode it first. */
++        data = sss_base64_decode(tmp_ctx, (const char*)data, &len);
++        if (data == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot decode secret from base64\n");
++            ret = EIO;
++            goto done;
++        }
++    }
++
+     buf = sss_iobuf_init_steal(tmp_ctx, data, len);
+     if (buf == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init the iobuf\n");
+-- 
+2.21.3
+
diff --git a/SOURCES/0010-nss-check-if-groups-are-filtered-during-initgroups.patch b/SOURCES/0010-nss-check-if-groups-are-filtered-during-initgroups.patch
new file mode 100644
index 0000000..8e76f9a
--- /dev/null
+++ b/SOURCES/0010-nss-check-if-groups-are-filtered-during-initgroups.patch
@@ -0,0 +1,112 @@
+From c87b2208b9a58c12eeceb5b8ccf9c34dcd835b8d Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 17 Nov 2020 12:59:23 +0100
+Subject: [PATCH] nss: check if groups are filtered during initgroups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If groups are filtered, i.e. SSSD should not handle them, they should
+not appear in the group list returned by an initgroups request.
+
+Resolves: https://github.com/SSSD/sssd/issues/5403
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+---
+ src/responder/nss/nss_protocol_grent.c | 35 ++++++++++++++++++++++++++
+ src/tests/intg/test_ldap.py            | 12 +++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
+index 8f1d3fe81..135b392f7 100644
+--- a/src/responder/nss/nss_protocol_grent.c
++++ b/src/responder/nss/nss_protocol_grent.c
+@@ -326,6 +326,34 @@ done:
+     return EOK;
+ }
+ 
++static bool is_group_filtered(struct sss_nc_ctx *ncache,
++                              struct sss_domain_info *domain,
++                              const char *grp_name, gid_t gid)
++{
++    int ret;
++
++    if (grp_name == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Group with gid [%"SPRIgid"] has no name, this should never "
++              "happen, trying to continue without.\n", gid);
++    } else {
++        ret = sss_ncache_check_group(ncache, domain, grp_name);
++        if (ret == EEXIST) {
++            DEBUG(SSSDBG_TRACE_FUNC, "Group [%s] is filtered out! "
++                                     "(negative cache)", grp_name);
++            return true;
++        }
++    }
++    ret = sss_ncache_check_gid(ncache, domain, gid);
++    if (ret == EEXIST) {
++        DEBUG(SSSDBG_TRACE_FUNC, "Group [%"SPRIgid"] is filtered out! "
++                                 "(negative cache)", gid);
++        return true;
++    }
++
++    return false;
++}
++
+ errno_t
+ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+                          struct nss_cmd_ctx *cmd_ctx,
+@@ -344,6 +372,7 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+     size_t body_len;
+     size_t rp;
+     gid_t gid;
++    const char *grp_name;
+     gid_t orig_gid;
+     errno_t ret;
+     int i;
+@@ -392,6 +421,8 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+         gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM,
+                                                    0);
+         posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL);
++        grp_name = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_NAME,
++                                                        NULL);
+ 
+         if (gid == 0) {
+             if (posix != NULL && strcmp(posix, "FALSE") == 0) {
+@@ -404,6 +435,10 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+             }
+         }
+ 
++        if (is_group_filtered(nss_ctx->rctx->ncache, domain, grp_name, gid)) {
++            continue;
++        }
++
+         SAFEALIGN_COPY_UINT32(&body[rp], &gid, &rp);
+         num_results++;
+ 
+diff --git a/src/tests/intg/test_ldap.py b/src/tests/intg/test_ldap.py
+index 194d7d9cc..6a78c960f 100644
+--- a/src/tests/intg/test_ldap.py
++++ b/src/tests/intg/test_ldap.py
+@@ -1190,6 +1190,18 @@ def test_nss_filters(ldap_conn, sanity_nss_filter):
+     with pytest.raises(KeyError):
+         grp.getgrgid(14)
+ 
++    # test initgroups - user1 is member of group_two_one_user_groups (2019)
++    # which is filtered out
++    (res, errno, gids) = sssd_id.call_sssd_initgroups("user1", 2001)
++    assert res == sssd_id.NssReturnCode.SUCCESS
++
++    user_with_group_ids = [2001, 2012, 2015, 2017, 2018]
++    assert sorted(gids) == sorted(user_with_group_ids), \
++        "result: %s\n expected %s" % (
++            ", ".join(["%s" % s for s in sorted(gids)]),
++            ", ".join(["%s" % s for s in sorted(user_with_group_ids)])
++        )
++
+ 
+ @pytest.fixture
+ def sanity_nss_filter_cached(request, ldap_conn):
+-- 
+2.21.3
+
diff --git a/SOURCES/0011-ifp-fix-use-after-free.patch b/SOURCES/0011-ifp-fix-use-after-free.patch
new file mode 100644
index 0000000..8e42b4d
--- /dev/null
+++ b/SOURCES/0011-ifp-fix-use-after-free.patch
@@ -0,0 +1,36 @@
+From 81e757b7b1d69893b5725f9c148c55d89c779e7b Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 3 Nov 2020 10:12:15 +0100
+Subject: [PATCH] ifp: fix use-after-free
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The variable fqdn is pointing to some data from state->res->msgs[0]. But
+before fqdn is used in the next search state->res and the memory
+hierarchy below is freed. As a result the location where fqdn is pointing
+to might hold the expected data or other data and the search will fail
+intermittently.
+
+Resolves: https://github.com/SSSD/sssd/issues/5382
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+---
+ src/responder/ifp/ifpsrv_cmd.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
+index 9f20bf2db..d95618127 100644
+--- a/src/responder/ifp/ifpsrv_cmd.c
++++ b/src/responder/ifp/ifpsrv_cmd.c
+@@ -128,6 +128,7 @@ static void ifp_user_get_attr_done(struct tevent_req *subreq)
+         tevent_req_error(req, ERR_INTERNAL);
+         return;
+     }
++    fqdn = talloc_steal(state, fqdn);
+ 
+     if (state->search_type == SSS_DP_USER) {
+         /* throw away the result and perform attr search */
+-- 
+2.21.3
+
diff --git a/SOURCES/0012-ifp-fix-original-fix-use-after-free.patch b/SOURCES/0012-ifp-fix-original-fix-use-after-free.patch
new file mode 100644
index 0000000..8e87526
--- /dev/null
+++ b/SOURCES/0012-ifp-fix-original-fix-use-after-free.patch
@@ -0,0 +1,38 @@
+From 3b158934cbb8f87cbfaf1650389b8dcd654b92ca Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 19 Nov 2020 18:05:00 +0100
+Subject: [PATCH] ifp: fix original fix use-after-free
+
+The original fix stole the fqdn too earlier. Only for SSS_DP_USER
+requests the steal is important. For other request where the first
+result is returned to the caller the original version
+might even cause issues since the name does not belong to the memory
+hierarchy of the result anymore.
+
+Resolves: https://github.com/SSSD/sssd/issues/5382
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/responder/ifp/ifpsrv_cmd.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
+index d95618127..8cf1ec84c 100644
+--- a/src/responder/ifp/ifpsrv_cmd.c
++++ b/src/responder/ifp/ifpsrv_cmd.c
+@@ -128,10 +128,10 @@ static void ifp_user_get_attr_done(struct tevent_req *subreq)
+         tevent_req_error(req, ERR_INTERNAL);
+         return;
+     }
+-    fqdn = talloc_steal(state, fqdn);
+ 
+     if (state->search_type == SSS_DP_USER) {
+-        /* throw away the result and perform attr search */
++        /* throw away the result but keep the fqdn and perform attr search */
++        fqdn = talloc_steal(state, fqdn);
+         talloc_zfree(state->res);
+ 
+         ret = sysdb_get_user_attr_with_views(state, state->dom, fqdn,
+-- 
+2.21.3
+
diff --git a/SOURCES/0013-pam_sss-use-unique-id-for-gdm-choice-list.patch b/SOURCES/0013-pam_sss-use-unique-id-for-gdm-choice-list.patch
new file mode 100644
index 0000000..c374782
--- /dev/null
+++ b/SOURCES/0013-pam_sss-use-unique-id-for-gdm-choice-list.patch
@@ -0,0 +1,68 @@
+From 1b9b7f5a635ede8eee90d13bfe0e1f87e51191a9 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 13 Nov 2020 12:59:39 +0100
+Subject: [PATCH 13/16] pam_sss: use unique id for gdm choice list
+
+Currently the key-id read from the Smartcard is used as key value for
+the gdm choice list dialog. Since it might be possible that multiple
+certificates use the same key and hence the same key-id this is not a
+suitable value.
+
+With this patch the string representation of a numerical counter is used.
+
+Resolves: https://github.com/SSSD/sssd/issues/5400
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/sss_client/pam_sss.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
+index b844d257e..04dfdb55d 100644
+--- a/src/sss_client/pam_sss.c
++++ b/src/sss_client/pam_sss.c
+@@ -128,6 +128,7 @@ struct cert_auth_info {
+     char *key_id;
+     char *prompt_str;
+     char *pam_cert_user;
++    char *choice_list_id;
+     struct cert_auth_info *prev;
+     struct cert_auth_info *next;
+ };
+@@ -141,6 +142,7 @@ static void free_cai(struct cert_auth_info *cai)
+         free(cai->module_name);
+         free(cai->key_id);
+         free(cai->prompt_str);
++        free(cai->choice_list_id);
+         free(cai);
+     }
+ }
+@@ -1698,7 +1700,15 @@ static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
+             ret = ENOMEM;
+             goto done;
+         }
+-        request->list.items[c].key = cai->key_id;
++        free(cai->choice_list_id);
++        ret = asprintf(&cai->choice_list_id, "%zu", c);
++        if (ret == -1) {
++            cai->choice_list_id = NULL;
++            ret = ENOMEM;
++            goto done;
++        }
++
++        request->list.items[c].key = cai->choice_list_id;
+         request->list.items[c++].text = prompt;
+     }
+ 
+@@ -1719,7 +1729,7 @@ static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
+     }
+ 
+     DLIST_FOR_EACH(cai, pi->cert_list) {
+-        if (strcmp(response->key, cai->key_id) == 0) {
++        if (strcmp(response->key, cai->choice_list_id) == 0) {
+             pam_info(pamh, "Certificate ‘%s’ selected", cai->key_id);
+             pi->selected_cert = cai;
+             ret = 0;
+-- 
+2.21.3
+
diff --git a/SOURCES/0014-authtok-add-label-to-Smartcard-token.patch b/SOURCES/0014-authtok-add-label-to-Smartcard-token.patch
new file mode 100644
index 0000000..741fc5d
--- /dev/null
+++ b/SOURCES/0014-authtok-add-label-to-Smartcard-token.patch
@@ -0,0 +1,1072 @@
+From 8b6be52e95e953ae0431676de0b8c8be7a3262bc Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 13 Nov 2020 18:05:14 +0100
+Subject: [PATCH 14/16] authtok: add label to Smartcard token
+
+The key-id might not be sufficient to identify a certificate on a
+Smartcard since it is possible that multiple certificates will use the
+same key.
+
+This patch adds the certificate label to the Smartcard authtok item to
+resolve the ambiguity if the key-id is used for multiple certificates.
+
+Resolves: https://github.com/SSSD/sssd/issues/5400
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/p11_child/p11_child.h         |  3 +-
+ src/p11_child/p11_child_common.c  | 12 +++--
+ src/p11_child/p11_child_openssl.c | 16 +++++--
+ src/providers/krb5/krb5_child.c   | 14 +++++-
+ src/responder/pam/pamsrv_cmd.c    |  5 +-
+ src/responder/pam/pamsrv_p11.c    |  8 +++-
+ src/sss_client/pam_sss.c          |  3 ++
+ src/tests/cmocka/test_authtok.c   | 36 +++++++++------
+ src/tests/cmocka/test_pam_srv.c   | 65 ++++++++++++++------------
+ src/util/authtok-utils.c          | 30 ++++++++++--
+ src/util/authtok-utils.h          | 11 ++++-
+ src/util/authtok.c                | 77 +++++++++++++++++++++++++------
+ src/util/authtok.h                | 14 +++++-
+ 13 files changed, 214 insertions(+), 80 deletions(-)
+
+diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h
+index 0b53e70c5..9c0cefe05 100644
+--- a/src/p11_child/p11_child.h
++++ b/src/p11_child/p11_child.h
+@@ -68,7 +68,8 @@ bool do_verification_b64(struct p11_ctx *p11_ctx, const char *cert_b64);
+ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
+                 enum op_mode mode, const char *pin,
+                 const char *module_name_in, const char *token_name_in,
+-                const char *key_id_in, const char *uri, char **_multi);
++                const char *key_id_in, const char *label,
++                const char *uri, char **_multi);
+ 
+ errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts,
+                                struct cert_verify_opts **cert_verify_opts);
+diff --git a/src/p11_child/p11_child_common.c b/src/p11_child/p11_child_common.c
+index 236d7dac4..f17de1a9e 100644
+--- a/src/p11_child/p11_child_common.c
++++ b/src/p11_child/p11_child_common.c
+@@ -60,7 +60,8 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db,
+                    bool wait_for_card,
+                    const char *cert_b64, const char *pin,
+                    const char *module_name, const char *token_name,
+-                   const char *key_id, const char *uri, char **multi)
++                   const char *key_id, const char *label, const char *uri,
++                   char **multi)
+ {
+     int ret;
+     struct p11_ctx *p11_ctx;
+@@ -91,7 +92,7 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db,
+         }
+     } else {
+         ret = do_card(mem_ctx, p11_ctx, mode, pin,
+-                      module_name, token_name, key_id, uri, multi);
++                      module_name, token_name, key_id, label, uri, multi);
+     }
+ 
+ done:
+@@ -158,6 +159,7 @@ int main(int argc, const char *argv[])
+     char *module_name = NULL;
+     char *token_name = NULL;
+     char *key_id = NULL;
++    char *label = NULL;
+     char *cert_b64 = NULL;
+     bool wait_for_card = false;
+     char *uri = NULL;
+@@ -194,6 +196,8 @@ int main(int argc, const char *argv[])
+          _("Token name for authentication"), NULL},
+         {"key_id", 0, POPT_ARG_STRING, &key_id, 0,
+          _("Key ID for authentication"), NULL},
++        {"label", 0, POPT_ARG_STRING, &label, 0,
++         _("Label for authentication"), NULL},
+         {"certificate", 0, POPT_ARG_STRING, &cert_b64, 0,
+          _("certificate to verify, base64 encoded"), NULL},
+         {"uri", 0, POPT_ARG_STRING, &uri, 0,
+@@ -340,6 +344,7 @@ int main(int argc, const char *argv[])
+     }
+     talloc_steal(main_ctx, debug_prg_name);
+ 
++    /* We do not require the label, but it is recommended */
+     if (mode == OP_AUTH && (module_name == NULL || token_name == NULL
+                                 || key_id == NULL)) {
+         DEBUG(SSSDBG_FATAL_FAILURE,
+@@ -369,7 +374,8 @@ int main(int argc, const char *argv[])
+     }
+ 
+     ret = do_work(main_ctx, mode, ca_db, cert_verify_opts, wait_for_card,
+-                  cert_b64, pin, module_name, token_name, key_id, uri, &multi);
++                  cert_b64, pin, module_name, token_name, key_id, label, uri,
++                  &multi);
+     if (ret != 0) {
+         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
+         goto fail;
+diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
+index 04b3e1467..d81a1a9ea 100644
+--- a/src/p11_child/p11_child_openssl.c
++++ b/src/p11_child/p11_child_openssl.c
+@@ -1587,7 +1587,8 @@ static errno_t wait_for_card(CK_FUNCTION_LIST *module, CK_SLOT_ID *slot_id)
+ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
+                 enum op_mode mode, const char *pin,
+                 const char *module_name_in, const char *token_name_in,
+-                const char *key_id_in, const char *uri_str, char **_multi)
++                const char *key_id_in, const char *label_in,
++                const char *uri_str, char **_multi)
+ {
+     int ret;
+     size_t c;
+@@ -1845,11 +1846,13 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
+     DLIST_FOR_EACH(item, all_cert_list) {
+         /* Check if we found the certificates we needed for authentication or
+          * the requested ones for pre-auth. For authentication all attributes
+-         * must be given and match, for pre-auth only the given ones must
+-         * match. */
+-        DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s.\n",
++         * except the label must be given and match. The label is optional for
++         * authentication but if given it must match as well. For pre-auth
++         * only the given ones must match. */
++        DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s %s.\n",
+               module_name_in, module_file_name, token_name_in, token_name,
+-              key_id_in, item->id);
++              key_id_in, label_in == NULL ? "- no label given-" : label_in,
++              item->id);
+ 
+         if ((mode == OP_AUTH
+                 && module_name_in != NULL
+@@ -1857,6 +1860,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
+                 && key_id_in != NULL
+                 && item->id != NULL
+                 && strcmp(key_id_in, item->id) == 0
++                && (label_in == NULL
++                    || (label_in != NULL && item->label != NULL
++                        && strcmp(label_in, item->label) == 0))
+                 && strcmp(token_name_in, token_name) == 0
+                 && strcmp(module_name_in, module_file_name) == 0)
+             || (mode == OP_PREAUTH
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 6e2bf6d75..cab7b27a2 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -714,7 +714,7 @@ static krb5_error_code answer_pkinit(krb5_context ctx,
+         kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
+                                  &token_name, NULL,
+                                  &module_name, NULL,
+-                                 NULL, NULL);
++                                 NULL, NULL, NULL, NULL);
+         if (kerr != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE,
+                   "sss_authtok_get_sc failed.\n");
+@@ -1226,11 +1226,12 @@ static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx,
+     const char *token_name;
+     const char *module_name;
+     const char *key_id;
++    const char *label;
+ 
+     ret = sss_authtok_get_sc(authtok, NULL, NULL,
+                              &token_name, NULL,
+                              &module_name, NULL,
+-                             &key_id, NULL);
++                             &key_id, NULL, &label, NULL);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+         return ret;
+@@ -1267,6 +1268,15 @@ static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx,
+         }
+     }
+ 
++    if (label != NULL && *label != '\0') {
++        identity = talloc_asprintf_append(identity, ":certlabel=%s", label);
++        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);
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index 9ea488be4..d3f092b2b 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -1258,7 +1258,7 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
+                     || sss_authtok_get_type(pd->authtok)
+                                                == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
+             ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, NULL, NULL, NULL,
+-                                     NULL, &key_id, NULL);
++                                     NULL, &key_id, NULL, NULL, NULL);
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+                 goto done;
+@@ -2274,7 +2274,8 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
+                                  SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
+                                  sss_cai_get_token_name(preq->current_cert), 0,
+                                  sss_cai_get_module_name(preq->current_cert), 0,
+-                                 sss_cai_get_key_id(preq->current_cert), 0);
++                                 sss_cai_get_key_id(preq->current_cert), 0,
++                                 sss_cai_get_label(preq->current_cert), 0);
+                         if (ret != EOK) {
+                             DEBUG(SSSDBG_OP_FAILURE,
+                                   "sss_authtok_set_sc failed, Smartcard "
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index abc987804..23f94927a 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -727,6 +727,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+     const char *module_name = NULL;
+     const char *token_name = NULL;
+     const char *key_id = NULL;
++    const char *label = NULL;
+ 
+     req = tevent_req_create(mem_ctx, &state, struct pam_check_cert_state);
+     if (req == NULL) {
+@@ -766,7 +767,8 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+     if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+             || sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+         ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, &token_name, NULL,
+-                                 &module_name, NULL, &key_id, NULL);
++                                 &module_name, NULL, &key_id, NULL,
++                                 &label, NULL);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+             goto done;
+@@ -784,6 +786,10 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+             extra_args[arg_c++] = key_id;
+             extra_args[arg_c++] = "--key_id";
+         }
++        if (label != NULL && *label != '\0') {
++            extra_args[arg_c++] = label;
++            extra_args[arg_c++] = "--label";
++        }
+     }
+ 
+     if (pd->cmd == SSS_PAM_AUTHENTICATE) {
+diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
+index 04dfdb55d..cffbfa770 100644
+--- a/src/sss_client/pam_sss.c
++++ b/src/sss_client/pam_sss.c
+@@ -126,6 +126,7 @@ struct cert_auth_info {
+     char *token_name;
+     char *module_name;
+     char *key_id;
++    char *label;
+     char *prompt_str;
+     char *pam_cert_user;
+     char *choice_list_id;
+@@ -1962,6 +1963,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
+         ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
+                                     cai->module_name, 0,
+                                     cai->key_id, 0,
++                                    cai->label, 0,
+                                     NULL, 0, &needed_size);
+         if (ret != EAGAIN) {
+             D(("sss_auth_pack_sc_blob failed."));
+@@ -1979,6 +1981,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
+         ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
+                                     cai->module_name, 0,
+                                     cai->key_id, 0,
++                                    cai->label, 0,
+                                     (uint8_t *) pi->pam_authtok, needed_size,
+                                     &needed_size);
+         if (ret != EOK) {
+diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c
+index a8f5bdee7..a31014eb6 100644
+--- a/src/tests/cmocka/test_authtok.c
++++ b/src/tests/cmocka/test_authtok.c
+@@ -451,25 +451,27 @@ void test_sss_authtok_sc_blobs(void **state)
+     size_t module_name_len;
+     const char *key_id;
+     size_t key_id_len;
++    const char *label;
++    size_t label_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);
++                                "stuvw", 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);
++                                "stuvw", 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",
++    assert_memory_equal(buf, "\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\6\0\0\0abc\0defg\0hijkl\0mnopqr\0stuvw\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",
++    assert_memory_equal(buf, "\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\6abc\0defg\0hijkl\0mnopqr\0stuvw\0",
+                         needed_size);
+ #endif
+ 
+@@ -485,7 +487,8 @@ void test_sss_authtok_sc_blobs(void **state)
+     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);
++                             &key_id, &key_id_len,
++                             &label, &label_len);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(pin_len, 3);
+     assert_string_equal(pin, "abc");
+@@ -495,11 +498,14 @@ void test_sss_authtok_sc_blobs(void **state)
+     assert_string_equal(module_name, "hijkl");
+     assert_int_equal(key_id_len, 6);
+     assert_string_equal(key_id, "mnopqr");
++    assert_int_equal(label_len, 5);
++    assert_string_equal(label, "stuvw");
+ 
+     ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL,
+                              &token_name, &token_name_len,
+                              &module_name, &module_name_len,
+-                             &key_id, &key_id_len);
++                             &key_id, &key_id_len,
++                             &label, &label_len);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(token_name_len, 4);
+     assert_string_equal(token_name, "defg");
+@@ -507,15 +513,19 @@ void test_sss_authtok_sc_blobs(void **state)
+     assert_string_equal(module_name, "hijkl");
+     assert_int_equal(key_id_len, 6);
+     assert_string_equal(key_id, "mnopqr");
++    assert_int_equal(label_len, 5);
++    assert_string_equal(label, "stuvw");
+ 
+     ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL,
+                              &token_name, NULL,
+                              &module_name, NULL,
+-                             &key_id, NULL);
++                             &key_id, NULL,
++                             &label, NULL);
+     assert_int_equal(ret, EOK);
+     assert_string_equal(token_name, "defg");
+     assert_string_equal(module_name, "hijkl");
+     assert_string_equal(key_id, "mnopqr");
++    assert_string_equal(label, "stuvw");
+ 
+     sss_authtok_set_empty(ts->authtoken);
+     talloc_free(buf);
+@@ -608,14 +618,14 @@ 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, 28);
++    assert_int_equal(size, 33);
+ #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",
++                        "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\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",
++                        "\0\0\0\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1" "12345678\0\0\0\0\0",
+                         size);
+ #endif
+ 
+@@ -624,14 +634,14 @@ 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, 25);
++    assert_int_equal(size, 30);
+ #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",
++                        "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\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",
++                        "\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1" "12345\0\0\0\0\0",
+                         size);
+ #endif
+ 
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index 326deaf1f..cb05042de 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -536,7 +536,7 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx,
+ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
+                                 const char *pin, const char *token_name,
+                                 const char *module_name, const char *key_id,
+-                                const char *service,
++                                const char *label, const char *service,
+                                 acct_cb_t acct_cb, const char *cert)
+ {
+     size_t buf_size;
+@@ -556,14 +556,14 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
+ 
+     if (pin != NULL) {
+         ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0,
+-                                    key_id, 0, NULL, 0, &needed_size);
++                                    key_id, 0, label, 0, NULL, 0, &needed_size);
+         assert_int_equal(ret, EAGAIN);
+ 
+         pi.pam_authtok = malloc(needed_size);
+         assert_non_null(pi.pam_authtok);
+ 
+         ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0,
+-                                    key_id, 0,
++                                    key_id, 0, label, 0,
+                                     (uint8_t *)pi.pam_authtok, needed_size,
+                                     &needed_size);
+         assert_int_equal(ret, EOK);
+@@ -1766,7 +1766,7 @@ void test_pam_preauth_no_logon_name(void **state)
+     int ret;
+ 
+     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+-                        NULL);
++                        NULL, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -1862,7 +1862,7 @@ void test_pam_preauth_cert_nocert(void **state)
+     unsetenv("SOFTHSM2_CONF");
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        NULL, NULL);
++                        NULL, NULL, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2004,7 +2004,7 @@ void test_pam_preauth_cert_nomatch(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, NULL);
++                        NULL, test_lookup_by_cert_cb, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2026,7 +2026,7 @@ void test_pam_preauth_cert_match(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2048,7 +2048,7 @@ void test_pam_preauth_cert_match_gdm_smartcard(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+                         "gdm-smartcard", test_lookup_by_cert_cb,
+                         SSSD_TEST_CERT_0001);
+ 
+@@ -2072,7 +2072,7 @@ void test_pam_preauth_cert_match_wrong_user(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_wrong_user_cb,
++                        NULL, test_lookup_by_cert_wrong_user_cb,
+                         SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2104,7 +2104,7 @@ void test_pam_preauth_cert_no_logon_name(void **state)
+      * request will be done with the username found by the certificate
+      * lookup. */
+     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+     mock_account_recv_simple();
+     mock_parse_inp("pamuser", NULL, EOK);
+     mock_parse_inp("pamuser", NULL, EOK);
+@@ -2134,7 +2134,7 @@ void test_pam_preauth_cert_no_logon_name_with_hint(void **state)
+      * during pre-auth and there is no need for an extra mocked response as in
+      * test_pam_preauth_cert_no_logon_name. */
+     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2155,7 +2155,7 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2178,7 +2178,7 @@ void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+     pam_test_ctx->rctx->domains->user_name_hint = true;
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2201,7 +2201,7 @@ void test_pam_preauth_no_cert_no_logon_name(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, "/no/path");
+ 
+     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+-                        NULL);
++                        NULL, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2223,7 +2223,7 @@ void test_pam_preauth_cert_no_logon_name_no_match(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+     mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, NULL);
++                        NULL, test_lookup_by_cert_cb, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2252,7 +2252,8 @@ void test_pam_cert_auth(void **state)
+      * in the cache and no second request to the backend is needed. */
+     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
+                         TEST_MODULE_NAME,
+-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
++                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
++                        "SSSD test cert 0001", NULL,
+                         test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2289,7 +2290,8 @@ void test_pam_ecc_cert_auth(void **state)
+     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
+                         "SSSD Test ECC Token",
+                         TEST_MODULE_NAME,
+-                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", NULL,
++                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB",
++                        "SSSD test ECC cert 0001", NULL,
+                         test_lookup_by_cert_cb, SSSD_TEST_ECC_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2324,7 +2326,8 @@ void test_pam_cert_auth_no_logon_name(void **state)
+      * in the cache and no second request to the backend is needed. */
+     mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
+                         TEST_MODULE_NAME,
+-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
++                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
++                        "SSSD test cert 0001", NULL,
+                         test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+ 
+     mock_account_recv_simple();
+@@ -2360,7 +2363,7 @@ void test_pam_cert_auth_no_logon_name_no_key_id(void **state)
+      * to the user entry the lookup by certificate will already find the user
+      * in the cache and no second request to the backend is needed. */
+     mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
+-                        TEST_MODULE_NAME, NULL, NULL,
++                        TEST_MODULE_NAME, NULL, NULL, NULL,
+                         NULL, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2387,7 +2390,8 @@ void test_pam_cert_auth_double_cert(void **state)
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
+                         TEST_MODULE_NAME,
+-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
++                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
++                        "SSSD test cert 0001", NULL,
+                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2416,7 +2420,7 @@ void test_pam_cert_preauth_2certs_one_mapping(void **state)
+     ret = test_lookup_by_cert_cb(discard_const(SSSD_TEST_CERT_0001));
+     assert_int_equal(ret, EOK);
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, NULL);
++                        NULL, test_lookup_by_cert_cb, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2439,7 +2443,7 @@ void test_pam_cert_preauth_2certs_two_mappings(void **state)
+     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_two.conf"));
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb_2nd_cert_same_user,
++                        NULL, test_lookup_by_cert_cb_2nd_cert_same_user,
+                         SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2464,7 +2468,8 @@ void test_pam_cert_auth_2certs_one_mapping(void **state)
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
+                         TEST_MODULE_NAME,
+-                        "C554C9F82C2A9D58B70921C143304153A8A42F17", NULL,
++                        "C554C9F82C2A9D58B70921C143304153A8A42F17",
++                        "SSSD test cert 0001", NULL,
+                         test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2498,7 +2503,7 @@ void test_pam_cert_preauth_uri_token1(void **state)
+     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf"));
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2528,7 +2533,7 @@ void test_pam_cert_preauth_uri_token2(void **state)
+     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf"));
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0002);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0002);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2567,7 +2572,7 @@ void test_pam_preauth_expired_crl_file(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        NULL, NULL);
++                        NULL, NULL, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2599,7 +2604,7 @@ void test_pam_preauth_expired_crl_file_soft(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2632,7 +2637,7 @@ void test_pam_preauth_ocsp(void **state)
+     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf"));
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        NULL, NULL);
++                        NULL, NULL, NULL);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2674,7 +2679,7 @@ void test_pam_preauth_ocsp_no_ocsp(void **state)
+     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf"));
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2708,7 +2713,7 @@ void test_pam_preauth_ocsp_soft_ocsp(void **state)
+     putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf"));
+ 
+     mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+-                        test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
++                        NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0005);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+diff --git a/src/util/authtok-utils.c b/src/util/authtok-utils.c
+index e50f86741..e76bd17c5 100644
+--- a/src/util/authtok-utils.c
++++ b/src/util/authtok-utils.c
+@@ -77,6 +77,7 @@ 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,
++                              const char *label, size_t label_len,
+                               uint8_t *buf, size_t buf_len,
+                               size_t *_sc_blob_len)
+ {
+@@ -88,7 +89,8 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+             || (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)) {
++            || (key_id_len != 0 && key_id == NULL)
++            || (label_len != 0 && label == NULL)) {
+         return EINVAL;
+     }
+ 
+@@ -113,6 +115,11 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+         key_id_len = 0;
+     }
+ 
++    if (label == NULL) {
++        label = "";
++        label_len = 0;
++    }
++
+     /* len should not include the trailing \0 */
+     if (pin_len == 0 || pin[pin_len - 1] == '\0') {
+         pin_len = strlen(pin);
+@@ -130,8 +137,12 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+         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 (label_len == 0 || label[label_len - 1] == '\0') {
++        label_len = strlen(label);
++    }
++
++    *_sc_blob_len = pin_len + token_name_len + module_name_len + key_id_len
++                            + label_len + 5 + 5 * sizeof(uint32_t);
+     if (buf == NULL || buf_len < *_sc_blob_len) {
+         return EAGAIN;
+     }
+@@ -145,6 +156,8 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+     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);
++    tmp_uint32_t = (uint32_t) label_len + 1;
++    SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c);
+ 
+     memcpy(buf + c, pin, pin_len);
+     buf[c + pin_len] = '\0';
+@@ -160,6 +173,10 @@ errno_t sss_auth_pack_sc_blob(const char *pin, size_t pin_len,
+ 
+     memcpy(buf + c, key_id, key_id_len);
+     buf[c + key_id_len] = '\0';
++    c += key_id_len +1;
++
++    memcpy(buf + c, label, label_len);
++    buf[c + label_len] = '\0';
+ 
+     return 0;
+ }
+@@ -171,6 +188,7 @@ const char *sss_auth_get_pin_from_sc_blob(uint8_t *blob, size_t blob_len)
+     uint32_t token_name_len;
+     uint32_t module_name_len;
+     uint32_t key_id_len;
++    uint32_t label_len;
+ 
+     if (blob == NULL || blob_len == 0) {
+         return NULL;
+@@ -184,9 +202,11 @@ const char *sss_auth_get_pin_from_sc_blob(uint8_t *blob, size_t blob_len)
+     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);
++    SAFEALIGN_COPY_UINT32(&label_len, blob + c, &c);
+ 
+-    if (blob_len != 4 * sizeof(uint32_t) + pin_len + token_name_len
+-                                         + module_name_len + key_id_len) {
++    if (blob_len != 5 * sizeof(uint32_t) + pin_len + token_name_len
++                                         + module_name_len + key_id_len
++                                         + label_len) {
+         return NULL;
+     }
+ 
+diff --git a/src/util/authtok-utils.h b/src/util/authtok-utils.h
+index 714c8187e..f3b268f78 100644
+--- a/src/util/authtok-utils.h
++++ b/src/util/authtok-utils.h
+@@ -39,6 +39,9 @@
+  * @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]  label       Label of the certificate
++ * @param[in]  label_len   Length of the label 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
+  *
+@@ -53,6 +56,7 @@ 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,
++                              const char *label, size_t label_len,
+                               uint8_t *buf, size_t buf_len,
+                               size_t *_sc_blob_len);
+ /**
+@@ -112,6 +116,10 @@ errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx,
+  * @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
++ * @param[out] _key_id           Key ID of the certificate, null terminated
++ * @param[out] _key_id_len       Length of the key ID
++ * @param[out] _labe l           Label of the certificate, null terminated
++ * @param[out] _label_len        Length of the label
+  *
+  * @return     EOK       on success
+  *             EINVAL    if input data is not consistent
+@@ -122,7 +130,8 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+                                  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);
++                                 char **key_id, size_t *_key_id_len,
++                                 char **label, size_t *_label_len);
+ 
+ /**
+  * @brief Return a pointer to the PIN string in the memory buffer
+diff --git a/src/util/authtok.c b/src/util/authtok.c
+index f8b44d6d6..7254ed1da 100644
+--- a/src/util/authtok.c
++++ b/src/util/authtok.c
+@@ -503,7 +503,8 @@ errno_t sss_authtok_set_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)
++                           const char *key_id, size_t key_id_len,
++                           const char *label, size_t label_len)
+ {
+     int ret;
+     size_t needed_size;
+@@ -518,7 +519,7 @@ errno_t sss_authtok_set_sc(struct sss_auth_token *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,
++                                key_id, key_id_len, label, label_len, NULL, 0,
+                                 &needed_size);
+     if (ret != EAGAIN) {
+         DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
+@@ -533,7 +534,7 @@ errno_t sss_authtok_set_sc(struct sss_auth_token *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, tok->data,
++                                key_id, key_id_len, label, label_len, tok->data,
+                                 needed_size, &needed_size);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_sc_blob failed.\n");
+@@ -560,6 +561,8 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+     size_t module_name_len;
+     char *key_id = NULL;
+     size_t key_id_len;
++    char *label = NULL;
++    size_t label_len;
+     TALLOC_CTX *tmp_ctx;
+ 
+     if (tok == NULL) {
+@@ -579,7 +582,7 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+     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);
++                                  &key_id, &key_id_len, &label, &label_len);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_sc_blob failed.\n");
+         goto done;
+@@ -588,7 +591,7 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+     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);
++                             key_id, key_id_len, label, label_len);
+ 
+ done:
+     talloc_free(tmp_ctx);
+@@ -607,7 +610,7 @@ errno_t sss_authtok_set_sc_pin(struct sss_auth_token *tok, const char *pin,
+     }
+ 
+     return sss_authtok_set_sc(tok, SSS_AUTHTOK_TYPE_SC_PIN, pin, len,
+-                              NULL, 0, NULL, 0, NULL, 0);
++                              NULL, 0, NULL, 0, NULL, 0, NULL, 0);
+ }
+ 
+ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
+@@ -625,7 +628,8 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
+         return ENOENT;
+     case SSS_AUTHTOK_TYPE_SC_PIN:
+         ret = sss_authtok_get_sc(tok, &pin, &pin_len,
+-                                 NULL, NULL, NULL, NULL, NULL, NULL);
++                                 NULL, NULL, NULL, NULL, NULL, NULL,
++                                 NULL, NULL);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+             return ret;
+@@ -663,13 +667,15 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+                                  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)
++                                 char **key_id, size_t *_key_id_len,
++                                 char **label, size_t *_label_len)
+ {
+     size_t c;
+     uint32_t pin_len;
+     uint32_t token_name_len;
+     uint32_t module_name_len;
+     uint32_t key_id_len;
++    uint32_t label_len;
+ 
+     c = 0;
+ 
+@@ -678,14 +684,16 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+         token_name_len = 0;
+         module_name_len = 0;
+         key_id_len = 0;
++        label_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;
++        label_len = 0;
+     } else {
+-        if (blob_len < 4 * sizeof(uint32_t)) {
++        if (blob_len < 5 * sizeof(uint32_t)) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+             return EINVAL;
+         }
+@@ -694,9 +702,11 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+         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);
++        SAFEALIGN_COPY_UINT32(&label_len, blob + c, &c);
+ 
+-        if (blob_len != 4 * sizeof(uint32_t) + pin_len + token_name_len
+-                                             + module_name_len + key_id_len) {
++        if (blob_len != 5 * sizeof(uint32_t) + pin_len + token_name_len
++                                             + module_name_len + key_id_len
++                                             + label_len) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+             return EINVAL;
+         }
+@@ -756,6 +766,25 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+         *key_id = NULL;
+     }
+ 
++    if (label_len != 0) {
++        *label = talloc_strndup(mem_ctx,
++                                      (const char *) blob + c + pin_len
++                                                              + token_name_len
++                                                              + module_name_len
++                                                              + key_id_len,
++                                      label_len);
++        if (*label == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            talloc_free(*pin);
++            talloc_free(*token_name);
++            talloc_free(*module_name);
++            talloc_free(*key_id);
++            return ENOMEM;
++        }
++    } else {
++        *label = 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);
+@@ -771,6 +800,10 @@ errno_t sss_auth_unpack_sc_blob(TALLOC_CTX *mem_ctx,
+         *_key_id_len = (*key_id == NULL) ? 0 : strlen(*key_id);
+     }
+ 
++    if (_label_len != NULL) {
++        *_label_len = (*label == NULL) ? 0 : strlen(*label);
++    }
++
+     return EOK;
+ }
+ 
+@@ -778,13 +811,15 @@ 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)
++                           const char **_key_id, size_t *_key_id_len,
++                           const char **_label, size_t *_label_len)
+ {
+     size_t c = 0;
+     size_t pin_len;
+     size_t token_name_len;
+     size_t module_name_len;
+     size_t key_id_len;
++    size_t label_len;
+     uint32_t tmp_uint32_t;
+ 
+     if (!tok) {
+@@ -796,7 +831,7 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+         return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES;
+     }
+ 
+-    if (tok->length < 4 * sizeof(uint32_t)) {
++    if (tok->length < 5 * sizeof(uint32_t)) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n");
+         return EINVAL;
+     }
+@@ -809,9 +844,12 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+     module_name_len = tmp_uint32_t -1;
+     SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
+     key_id_len = tmp_uint32_t -1;
++    SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c);
++    label_len = tmp_uint32_t -1;
+ 
+-    if (tok->length != 4 * sizeof(uint32_t) +  4 + pin_len + token_name_len
+-                                         + module_name_len + key_id_len) {
++    if (tok->length != 5 * sizeof(uint32_t) +  5 + pin_len + token_name_len
++                                         + module_name_len + key_id_len
++                                         + label_len) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n");
+         return EINVAL;
+     }
+@@ -846,5 +884,14 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
+         *_key_id_len = key_id_len;
+     }
+ 
++    if (_label != NULL) {
++        *_label = (const char *) tok->data + c + pin_len + 1
++                               + token_name_len + 1 + module_name_len + 1
++                               + key_id_len + 1;
++    }
++    if (_label_len != NULL) {
++        *_label_len = label_len;
++    }
++
+     return EOK;
+ }
+diff --git a/src/util/authtok.h b/src/util/authtok.h
+index f70c9da13..6fd3e9ef0 100644
+--- a/src/util/authtok.h
++++ b/src/util/authtok.h
+@@ -296,6 +296,10 @@ void sss_authtok_set_sc_keypad(struct sss_auth_token *tok);
+  *                        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
++ * @param label           A pointer to a const char *, that will point to a null
++ *                        terminated string containing the PKCS#11 label
++ * @param label_len       The length of the label string, if set to 0 it will be
++ *                        calculated
+  *
+  * @return       EOK on success
+  *               EINVAL unexpected or inval input
+@@ -306,7 +310,8 @@ errno_t sss_authtok_set_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);
++                           const char *key_id, size_t key_id_len,
++                           const char *label, size_t label_len);
+ /**
+  * @brief Set a Smart Card authentication data, replacing any previous data
+  *
+@@ -342,6 +347,10 @@ errno_t sss_authtok_set_sc_from_blob(struct sss_auth_token *tok,
+  *                              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
++ * @param[out] _label           A pointer to a const char *, that will point to
++ *                              a null terminated string holding the PKCS#11
++ *                              label, may not be modified or freed
++ * @param[out] _label_len       Length of the PKCS#11 label
+  *
+  * Any of the output pointers may be NULL if the caller does not need the
+  * specific item.
+@@ -356,7 +365,8 @@ 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);
++                           const char **_key_id, size_t *_key_id_len,
++                           const char **_label, size_t *_label_len);
+ 
+ 
+ /**
+-- 
+2.21.3
+
diff --git a/SOURCES/0015-pam_sss-add-certificate-label-to-reply-to-pam_sss.patch b/SOURCES/0015-pam_sss-add-certificate-label-to-reply-to-pam_sss.patch
new file mode 100644
index 0000000..88fcc9f
--- /dev/null
+++ b/SOURCES/0015-pam_sss-add-certificate-label-to-reply-to-pam_sss.patch
@@ -0,0 +1,208 @@
+From b8800d3e1b43f2eb28b2df7adb2bcb323bf2d1f1 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Sat, 14 Nov 2020 17:52:35 +0100
+Subject: [PATCH 15/16] pam_sss: add certificate label to reply to pam_sss
+
+Add the certificate label to the data send back and forth to the pam
+module to avoid the ambiguity if two certificates use the same key.
+
+Resolves: https://github.com/SSSD/sssd/issues/5400
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/responder/pam/pamsrv_p11.c  | 13 ++++++++++---
+ src/sss_client/pam_sss.c        | 15 +++++++++++++++
+ src/tests/cmocka/test_pam_srv.c | 20 ++++++++++++++++----
+ 3 files changed, 41 insertions(+), 7 deletions(-)
+
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index 23f94927a..e1fd72e64 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -1086,11 +1086,13 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+     const char *token_name;
+     const char *module_name;
+     const char *key_id;
++    const char *label;
+     char *prompt;
+     size_t user_len;
+     size_t token_len;
+     size_t module_len;
+     size_t key_id_len;
++    size_t label_len;
+     size_t prompt_len;
+     size_t nss_name_len;
+     const char *username = "";
+@@ -1113,16 +1115,18 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+     token_name = sss_cai_get_token_name(cert_info);
+     module_name = sss_cai_get_module_name(cert_info);
+     key_id = sss_cai_get_key_id(cert_info);
++    label = sss_cai_get_label(cert_info);
+ 
+     user_len = strlen(username) + 1;
+     token_len = strlen(token_name) + 1;
+     module_len = strlen(module_name) + 1;
+     key_id_len = strlen(key_id) + 1;
++    label_len = strlen(label) + 1;
+     prompt_len = strlen(prompt) + 1;
+     nss_name_len = strlen(nss_username) +1;
+ 
+-    msg_len = user_len + token_len + module_len + key_id_len + prompt_len
+-                       + nss_name_len;
++    msg_len = user_len + token_len + module_len + key_id_len + label_len
++                       + prompt_len + nss_name_len;
+ 
+     msg = talloc_zero_size(mem_ctx, msg_len);
+     if (msg == NULL) {
+@@ -1136,8 +1140,11 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+     memcpy(msg + user_len + token_len, module_name, module_len);
+     memcpy(msg + user_len + token_len + module_len, key_id, key_id_len);
+     memcpy(msg + user_len + token_len + module_len + key_id_len,
++           label, label_len);
++    memcpy(msg + user_len + token_len + module_len + key_id_len + label_len,
+            prompt, prompt_len);
+-    memcpy(msg + user_len + token_len + module_len + key_id_len + prompt_len,
++    memcpy(msg + user_len + token_len + module_len + key_id_len + label_len
++               + prompt_len,
+            nss_username, nss_name_len);
+     talloc_free(prompt);
+ 
+diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
+index cffbfa770..c539d6de6 100644
+--- a/src/sss_client/pam_sss.c
++++ b/src/sss_client/pam_sss.c
+@@ -142,6 +142,7 @@ static void free_cai(struct cert_auth_info *cai)
+         free(cai->token_name);
+         free(cai->module_name);
+         free(cai->key_id);
++        free(cai->label);
+         free(cai->prompt_str);
+         free(cai->choice_list_id);
+         free(cai);
+@@ -936,6 +937,20 @@ static int parse_cert_info(struct pam_items *pi, uint8_t *buf, size_t len,
+         goto done;
+     }
+ 
++    cai->label = strdup((char *) &buf[*p + offset]);
++    if (cai->label == NULL) {
++        D(("strdup failed"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    offset += strlen(cai->label) + 1;
++    if (offset >= len) {
++        D(("Cert message size mismatch"));
++        ret = EINVAL;
++        goto done;
++    }
++
+     cai->prompt_str = strdup((char *) &buf[*p + offset]);
+     if (cai->prompt_str == NULL) {
+         D(("strdup failed"));
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index cb05042de..5506fbf34 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -62,13 +62,16 @@
+ #define TEST_TOKEN_NAME "SSSD Test Token"
+ #define TEST_TOKEN2_NAME "SSSD Test Token Number 2"
+ #define TEST_KEY_ID "C554C9F82C2A9D58B70921C143304153A8A42F17"
++#define TEST_LABEL "SSSD test cert 0001"
+ #define TEST_MODULE_NAME SOFTHSM2_PATH
+ #define TEST_PROMPT "SSSD test cert 0001\nCN=SSSD test cert 0001,OU=SSSD test,O=SSSD"
+ #define TEST2_PROMPT "SSSD test cert 0002\nCN=SSSD test cert 0002,OU=SSSD test,O=SSSD"
+ #define TEST5_PROMPT "SSSD test cert 0005\nCN=SSSD test cert 0005,OU=SSSD test,O=SSSD"
+ 
+ #define TEST2_KEY_ID "5405842D56CF31F0BB025A695C5F3E907051C5B9"
++#define TEST2_LABEL "SSSD test cert 0002"
+ #define TEST5_KEY_ID "1195833C424AB00297F582FC43FFFFAB47A64CC9"
++#define TEST5_LABEL "SSSD test cert 0005"
+ 
+ static char CACHED_AUTH_TIMEOUT_STR[] = "4";
+ static const int CACHED_AUTH_TIMEOUT = 4;
+@@ -673,6 +676,7 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+                                 + sizeof(TEST_TOKEN_NAME)
+                                 + sizeof(TEST_MODULE_NAME)
+                                 + sizeof(TEST_KEY_ID)
++                                + sizeof(TEST_LABEL)
+                                 + sizeof(TEST_PROMPT)
+                                 + sizeof("pamuser")));
+ 
+@@ -692,6 +696,10 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+     assert_string_equal(body + rp, TEST_KEY_ID);
+     rp += sizeof(TEST_KEY_ID);
+ 
++    assert_int_equal(*(body + rp + sizeof(TEST_LABEL) - 1), 0);
++    assert_string_equal(body + rp, TEST_LABEL);
++    rp += sizeof(TEST_LABEL);
++
+     assert_int_equal(*(body + rp + sizeof(TEST_PROMPT) - 1), 0);
+     assert_string_equal(body + rp, TEST_PROMPT);
+     rp += sizeof(TEST_PROMPT);
+@@ -740,6 +748,7 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+                                     TEST_TOKEN_NAME,
+                                     TEST_MODULE_NAME,
+                                     TEST_KEY_ID,
++                                    TEST_LABEL,
+                                     TEST_PROMPT,
+                                     NULL,
+                                     NULL };
+@@ -749,6 +758,7 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+                                      TEST_TOKEN_NAME,
+                                      TEST_MODULE_NAME,
+                                      TEST2_KEY_ID,
++                                     TEST2_LABEL,
+                                      TEST2_PROMPT,
+                                      NULL,
+                                      NULL };
+@@ -756,10 +766,10 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+     assert_int_equal(status, 0);
+ 
+     check_strings[0] = name;
+-    check_strings[5] = nss_name;
++    check_strings[6] = nss_name;
+     check_len = check_string_array_len(check_strings);
+     check2_strings[0] = name;
+-    check2_strings[5] = nss_name;
++    check2_strings[6] = nss_name;
+     check2_len = check_string_array_len(check2_strings);
+ 
+ 
+@@ -843,6 +853,7 @@ static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body,
+                                      TEST_TOKEN2_NAME,
+                                      TEST_MODULE_NAME,
+                                      TEST2_KEY_ID,
++                                     TEST2_LABEL,
+                                      TEST2_PROMPT,
+                                      NULL,
+                                      NULL };
+@@ -850,7 +861,7 @@ static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body,
+     assert_int_equal(status, 0);
+ 
+     check2_strings[0] = name;
+-    check2_strings[5] = nss_name;
++    check2_strings[6] = nss_name;
+     check2_len = check_string_array_len(check2_strings);
+ 
+     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+@@ -895,7 +906,7 @@ static int test_pam_cert_X_token_X_check_ex(uint32_t status, uint8_t *body,
+     assert_int_equal(status, 0);
+ 
+     check_strings[0] = name;
+-    check_strings[5] = nss_name;
++    check_strings[6] = nss_name;
+     check_len = check_string_array_len(check_strings);
+ 
+     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+@@ -946,6 +957,7 @@ static int test_pam_cert5_check(uint32_t status, uint8_t *body, size_t blen)
+                                      TEST_TOKEN_NAME,
+                                      TEST_MODULE_NAME,
+                                      TEST5_KEY_ID,
++                                     TEST5_LABEL,
+                                      TEST5_PROMPT,
+                                      NULL,
+                                      NULL };
+-- 
+2.21.3
+
diff --git a/SOURCES/0016-add-tests-multiple-certs-same-id.patch b/SOURCES/0016-add-tests-multiple-certs-same-id.patch
new file mode 100644
index 0000000..cd9cefd
--- /dev/null
+++ b/SOURCES/0016-add-tests-multiple-certs-same-id.patch
@@ -0,0 +1,265 @@
+From f633f37e712cb0f7524a2ee257e15f34468149b4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 3 Nov 2020 09:58:52 +0100
+Subject: [PATCH 16/16] add tests multiple certs same id
+
+Add unit test for the case that two certificates use the same key.
+
+Resolves: https://github.com/SSSD/sssd/issues/5400
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/tests/cmocka/test_pam_srv.c              | 116 +++++++++++++++++++
+ src/tests/test_CA/Makefile.am                |  26 ++++-
+ src/tests/test_CA/SSSD_test_cert_0006.config |  20 ++++
+ 3 files changed, 161 insertions(+), 1 deletion(-)
+ create mode 100644 src/tests/test_CA/SSSD_test_cert_0006.config
+
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index 5506fbf34..8ca5abd43 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -40,12 +40,14 @@
+ #include "tests/test_CA/SSSD_test_cert_x509_0001.h"
+ #include "tests/test_CA/SSSD_test_cert_x509_0002.h"
+ #include "tests/test_CA/SSSD_test_cert_x509_0005.h"
++#include "tests/test_CA/SSSD_test_cert_x509_0006.h"
+ 
+ #include "tests/test_ECC_CA/SSSD_test_ECC_cert_x509_0001.h"
+ #else
+ #define SSSD_TEST_CERT_0001 ""
+ #define SSSD_TEST_CERT_0002 ""
+ #define SSSD_TEST_CERT_0005 ""
++#define SSSD_TEST_CERT_0006 ""
+ 
+ #define SSSD_TEST_ECC_CERT_0001 ""
+ #endif
+@@ -1093,6 +1095,13 @@ static int test_pam_creds_insufficient_check(uint32_t status,
+     return EOK;
+ }
+ 
++static int test_pam_auth_err_check(uint32_t status, uint8_t *body, size_t blen)
++{
++    /* PAM_AUTH_ERR is returned for different types of error, we use different
++     * names for the check functions to make the purpose more clear. */
++    return test_pam_wrong_pw_offline_auth_check(status, body, blen);
++}
++
+ static int test_pam_user_unknown_check(uint32_t status,
+                                        uint8_t *body, size_t blen)
+ {
+@@ -2500,6 +2509,107 @@ void test_pam_cert_auth_2certs_one_mapping(void **state)
+     assert_int_equal(ret, EOK);
+ }
+ 
++/* The following three tests cover a use case where multiple certificates are
++ * using the same key-pair. According to PKCS#11 specs "The CKA_ID field is
++ * intended to distinguish among multiple keys. In the case of public and
++ * private keys, this field assists in handling multiple keys held by the same
++ * subject; the key identifier for a public key and its corresponding private
++ * key should be the same. The key identifier should also be the same as for
++ * the corresponding certificate, if one exists. Cryptoki does not enforce
++ * these associations, however." As a result certificates sharing the same
++ * key-pair will have the same id on the Smartcard. This means a second
++ * parameter is needed to distinguish them. We use the label here.
++ *
++ * The first test makes sure authentication fails is the label is missing, the
++ * second and third test make sure that each certificate can be selected with
++ * the proper label. */
++void test_pam_cert_auth_2certs_same_id_no_label(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
++    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf"));
++
++    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
++                        TEST_MODULE_NAME,
++                        "11111111",
++                        NULL, NULL,
++                        NULL, SSSD_TEST_CERT_0001);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    /* Assume backend cannot handle Smartcard credentials */
++    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
++
++    set_cmd_cb(test_pam_auth_err_check);
++    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
++                          pam_test_ctx->pam_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_pam_cert_auth_2certs_same_id_with_label_1(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
++    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf"));
++
++    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
++                        TEST_MODULE_NAME,
++                        "11111111",
++                        "SSSD test cert 0001", NULL,
++                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    /* 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);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_pam_cert_auth_2certs_same_id_with_label_6(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
++    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf"));
++
++    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
++                        TEST_MODULE_NAME,
++                        "11111111",
++                        "SSSD test cert 0006", NULL,
++                        test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0006);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    /* 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);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
+ void test_pam_cert_preauth_uri_token1(void **state)
+ {
+     int ret;
+@@ -3179,6 +3289,12 @@ int main(int argc, const char *argv[])
+                                         pam_test_setup, pam_test_teardown),
+         cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_one_mapping,
+                                         pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_no_label,
++                                        pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_with_label_1,
++                                        pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_with_label_6,
++                                        pam_test_setup, pam_test_teardown),
+         cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name,
+                                         pam_test_setup, pam_test_teardown),
+         cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id,
+diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am
+index 0e0122737..8765d0fd6 100644
+--- a/src/tests/test_CA/Makefile.am
++++ b/src/tests/test_CA/Makefile.am
+@@ -6,6 +6,7 @@ dist_noinst_DATA = \
+     SSSD_test_cert_0003.config \
+     SSSD_test_cert_0004.config \
+     SSSD_test_cert_0005.config \
++    SSSD_test_cert_0006.config \
+     SSSD_test_cert_key_0001.pem \
+     SSSD_test_cert_key_0002.pem \
+     SSSD_test_cert_key_0003.pem \
+@@ -25,7 +26,7 @@ pubkeys = $(addprefix SSSD_test_cert_pubsshkey_,$(addsuffix .pub,$(ids)))
+ pubkeys_h = $(addprefix SSSD_test_cert_pubsshkey_,$(addsuffix .h,$(ids)))
+ pkcs12 = $(addprefix SSSD_test_cert_pkcs12_,$(addsuffix .pem,$(ids)))
+ 
+-extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens softhsm2_ocsp
++extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens softhsm2_ocsp softhsm2_2certs_same_id
+ if HAVE_FAKETIME
+ extra += SSSD_test_CA_expired_crl.pem
+ endif
+@@ -41,6 +42,14 @@ $(pwdfile):
+ SSSD_test_CA.pem: $(openssl_ca_key) $(openssl_ca_config) serial
+ 	$(OPENSSL) req -batch -config ${openssl_ca_config} -x509 -new -nodes -key $< -sha256 -days 1024 -set_serial 0 -extensions v3_ca -out $@
+ 
++# SSSD_test_cert_0006 should use the same key as SSSD_test_cert_0001
++.INTERMEDIATE: SSSD_test_cert_req_0006.pem
++SSSD_test_cert_req_0006.pem: $(srcdir)/SSSD_test_cert_key_0001.pem $(srcdir)/SSSD_test_cert_0006.config
++	if [ $(shell grep -c req_exts $(srcdir)/SSSD_test_cert_0006.config) -eq 0 ]; then \
++		$(OPENSSL) req -new -nodes -key $< -config $(srcdir)/SSSD_test_cert_0006.config -out $@ ; \
++	else \
++		$(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_cert_0006.config -out $@ ; \
++	fi
+ 
+ SSSD_test_cert_req_%.pem: $(srcdir)/SSSD_test_cert_key_%.pem $(srcdir)/SSSD_test_cert_%.config
+ 	if [ $(shell grep -c req_exts $(srcdir)/SSSD_test_cert_$*.config) -eq 0 ]; then \
+@@ -52,6 +61,9 @@ SSSD_test_cert_req_%.pem: $(srcdir)/SSSD_test_cert_key_%.pem $(srcdir)/SSSD_test
+ SSSD_test_cert_x509_%.pem: SSSD_test_cert_req_%.pem $(openssl_ca_config) SSSD_test_CA.pem
+ 	$(OPENSSL) ca -config ${openssl_ca_config} -batch -notext -keyfile $(openssl_ca_key) -in $< -days 200 -extensions usr_cert -out $@
+ 
++SSSD_test_cert_pkcs12_0006.pem: SSSD_test_cert_x509_0006.pem $(srcdir)/SSSD_test_cert_key_0001.pem $(pwdfile)
++	$(OPENSSL) pkcs12 -export -in SSSD_test_cert_x509_0006.pem -inkey $(srcdir)/SSSD_test_cert_key_0001.pem -nodes -passout file:$(pwdfile) -out $@
++
+ SSSD_test_cert_pkcs12_%.pem: SSSD_test_cert_x509_%.pem $(srcdir)/SSSD_test_cert_key_%.pem $(pwdfile)
+ 	$(OPENSSL) pkcs12 -export -in SSSD_test_cert_x509_$*.pem -inkey $(srcdir)/SSSD_test_cert_key_$*.pem -nodes -passout file:$(pwdfile) -out $@
+ 
+@@ -130,6 +142,18 @@ softhsm2_ocsp.conf:
+ 	@echo "objectstore.backend = file" >> $@
+ 	@echo "slots.removable = true" >> $@
+ 
++softhsm2_2certs_same_id: softhsm2_2certs_same_id.conf SSSD_test_cert_x509_0001.pem SSSD_test_cert_x509_0006.pem
++	mkdir $@
++	SOFTHSM2_CONF=./$< $(SOFTHSM2_UTIL) --init-token  --label "SSSD Test Token" --pin 123456 --so-pin 123456 --free
++	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0006.pem --login  --label 'SSSD test cert 0006' --id '11111111'
++	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0001.pem --login  --label 'SSSD test cert 0001' --id '11111111'
++	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --load-privkey=$(srcdir)/SSSD_test_cert_key_0001.pem --login  --label 'SSSD test cert 0001' --id '11111111'
++
++softhsm2_2certs_same_id.conf:
++	@echo "directories.tokendir = "$(abs_top_builddir)"/src/tests/test_CA/softhsm2_2certs_same_id" > $@
++	@echo "objectstore.backend = file" >> $@
++	@echo "slots.removable = true" >> $@
++
+ CLEANFILES = \
+     index.txt  index.txt.attr \
+     index.txt.attr.old  index.txt.old \
+diff --git a/src/tests/test_CA/SSSD_test_cert_0006.config b/src/tests/test_CA/SSSD_test_cert_0006.config
+new file mode 100644
+index 000000000..762de55cd
+--- /dev/null
++++ b/src/tests/test_CA/SSSD_test_cert_0006.config
+@@ -0,0 +1,20 @@
++# This certificate is used in
++# - src/tests/cmocka/test_pam_srv.c
++# and should use the same key-pair as SSSD_test_cert_0001
++[ req ]
++distinguished_name = req_distinguished_name
++prompt = no
++
++[ req_distinguished_name ]
++O = SSSD
++OU = SSSD test
++CN = SSSD test cert 0006
++
++[ req_exts ]
++basicConstraints = CA:FALSE
++nsCertType = client, email
++nsComment = "SSSD test Certificate"
++subjectKeyIdentifier = hash
++keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
++extendedKeyUsage = clientAuth, emailProtection
++subjectAltName = email:sssd-devel@lists.fedorahosted.org,URI:https://github.com/SSSD/sssd//
+-- 
+2.21.3
+
diff --git a/SOURCES/0017-data_provider_be-Add-random-offset-default.patch b/SOURCES/0017-data_provider_be-Add-random-offset-default.patch
new file mode 100644
index 0000000..7574eec
--- /dev/null
+++ b/SOURCES/0017-data_provider_be-Add-random-offset-default.patch
@@ -0,0 +1,53 @@
+From 1e9abd508ea5627465d528788645d4dbe53d7d31 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pawe=C5=82=20Po=C5=82awski?= <ppolawsk@redhat.com>
+Date: Wed, 2 Dec 2020 03:00:26 +0100
+Subject: [PATCH 17/18] data_provider_be: Add random offset default
+
+Replace hardcoded default value of 30 with more meaningful
+OFFLINE_TIMEOUT_RANDOM_OFFSET define.
+
+This value is used to calculate task timeout during offline
+status checking by formula (from SSSD MAN page):
+
+new_interval = (old_interval * 2) + random_offset
+
+As it is explicite mentioned in documentation it should
+be expressed in the code similar way.
+
+Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
+---
+ src/providers/data_provider_be.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
+index 4c10d6b48..10421c6b4 100644
+--- a/src/providers/data_provider_be.c
++++ b/src/providers/data_provider_be.c
+@@ -51,6 +51,7 @@
+ #define ONLINE_CB_RETRY 3
+ #define ONLINE_CB_RETRY_MAX_DELAY 4
+ 
++#define OFFLINE_TIMEOUT_RANDOM_OFFSET 30
+ #define OFFLINE_TIMEOUT_DEFAULT 60
+ #define OFFLINE_TIMEOUT_MAX_DEFAULT 3600
+ 
+@@ -152,9 +153,13 @@ void be_mark_offline(struct be_ctx *ctx)
+         offline_timeout = get_offline_timeout(ctx);
+         offline_timeout_max = get_offline_timeout_max(ctx);
+ 
+-        ret = be_ptask_create_sync(ctx, ctx,
+-                                   offline_timeout, offline_timeout,
+-                                   offline_timeout, 30, offline_timeout,
++        ret = be_ptask_create_sync(ctx,
++                                   ctx,
++                                   offline_timeout,
++                                   offline_timeout,
++                                   offline_timeout,
++                                   OFFLINE_TIMEOUT_RANDOM_OFFSET,
++                                   offline_timeout,
+                                    offline_timeout_max,
+                                    try_to_go_online,
+                                    ctx, "Check if online (periodic)",
+-- 
+2.21.3
+
diff --git a/SOURCES/0018-data_provider_be-MAN-page-update.patch b/SOURCES/0018-data_provider_be-MAN-page-update.patch
new file mode 100644
index 0000000..15e4168
--- /dev/null
+++ b/SOURCES/0018-data_provider_be-MAN-page-update.patch
@@ -0,0 +1,59 @@
+From 171b664ec4a7c94583b35597bd7e1e72bf89d217 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pawe=C5=82=20Po=C5=82awski?= <ppolawsk@redhat.com>
+Date: Wed, 2 Dec 2020 03:10:50 +0100
+Subject: [PATCH 18/18] data_provider_be: MAN page update
+
+Updated description of parameters:
+* offline_timeout
+* offline_timeout_max
+
+MAN page now explains that in some circumstances
+corelation of offline_timeout and offline_timeout_max values
+may lead to offline checking interval not incrementing.
+This is a false positive error as in fact the value
+just saturates almost instantly.
+
+Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
+---
+ src/man/sssd.conf.5.xml | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index d637e2eaa..8b330de58 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -739,12 +739,12 @@
+                              offline_timeout + random_offset
+                         </para>
+                         <para>
+-                            The random offset can increment up to 30 seconds.
++                            The random offset value is from 0 to 30.
+                             After each unsuccessful attempt to go online,
+                             the new interval is recalculated by the following:
+                         </para>
+                         <para>
+-                            new_interval = old_interval*2 + random_offset
++                            new_interval = (old_interval * 2) + random_offset
+                         </para>
+                         <para>
+                             Note that the maximum length of each interval
+@@ -769,6 +769,16 @@
+                         <para>
+                             A value of 0 disables the incrementing behaviour.
+                         </para>
++                        <para>
++                            The value of this parameter should be set in correlation
++                            to offline_timeout parameter value.
++                        </para>
++                        <para>
++                            With offline_timeout set to 60 (default value) there is no point
++                            in setting offlinet_timeout_max to less than 120 as it will
++                            saturate instantly. General rule here should be to set
++                            offline_timeout_max to at least 4 times offline_timeout.
++                        </para>
+                         <para>
+                             Although a value between 0 and offline_timeout may be
+                             specified, it has the effect of overriding the
+-- 
+2.21.3
+
diff --git a/SOURCES/0019-logs-review.patch b/SOURCES/0019-logs-review.patch
new file mode 100644
index 0000000..54fc132
--- /dev/null
+++ b/SOURCES/0019-logs-review.patch
@@ -0,0 +1,3410 @@
+From 69ef1cf763fca6b2c7174ddacf3f510c73cc27e6 Mon Sep 17 00:00:00 2001
+From: Alexey Tikhonov <atikhono@redhat.com>
+Date: Mon, 28 Dec 2020 19:36:48 +0100
+Subject: [PATCH] Squashed commit of the following:
+
+commit bd2f38abe95645b9b16b12d12dac6008b0d2a03b
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Tue Dec 15 18:47:25 2020 +0100
+
+    UTIL: find_domain_by_object_name_ex() changed log level
+
+    It's up to user of this function to judge if fail to parse fqname is
+    a critical error.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 0db68a1f95612fcbad18ca8107a4b170f446dd59
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Tue Dec 15 17:26:09 2020 +0100
+
+    LDAP: sdap_save_grpmem(): log level changed
+
+    There are legitimate reasons when sdap_save_grpmem() can be called
+    with `ignore_group_members = true`
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 00e3ac4a4f9b6c8da27daa3ed8c18664c99256bb
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Dec 13 23:21:37 2020 +0100
+
+    LDAP: reduce log level in case of fail to store members of missing group (it might be built-in skipped intentionally)
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit dba7de0db3cbaee43ef06a1b7c847fbcf48f3708
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Dec 13 22:37:44 2020 +0100
+
+    SYSDB: changed logging in sysdb_get_real_name()
+
+    Missing cache entry isn't an error.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit e86599ba079611ed324ff1493a7173d11c1a7961
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Dec 13 22:22:36 2020 +0100
+
+    IPA: changed logging in ipa_get_subdom_acct_send()
+
+    Frontends do not know what kind of lookup the backends support
+    so it is expected that they might send unsupported requests.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit bf873598a9d4ac8256b20859c0d92fb509861b6b
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Dec 13 20:29:07 2020 +0100
+
+    IPA: ignore failed group search in certain cases
+
+    It's currently expected to see those messages with sudo or HBAC rules in play.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 60b17be9e4f4865fe1774076808a6c783a7ec906
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Dec 13 19:36:56 2020 +0100
+
+    SYSDB: changed log level in sysdb_update_members_ex()
+
+    Fail to add already existing member isn't critical.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 9390af3c2d1b33e2b5ded0ea0c6c436b9776cedc
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sat Dec 12 21:29:06 2020 +0100
+
+    IPA: reduce log level in apply_subdomain_homedir()
+
+    Missing UID for SYSDB_GROUP_CLASS is not an error
+    (see commit message of e66517dcf63f1d4aaf866c22371dac7740ce0a48 for
+    additional details)
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 9215cf4e2519d5f085bf97f26a74d499090e46e1
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sat Dec 12 20:46:40 2020 +0100
+
+    CERTMAP: removed stray debug message
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 0986cf6ced8c4e09b8031d19eddffca679aca30c
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Thu Dec 3 21:06:31 2020 +0100
+
+    UTIL: fixed bug in server_setup() that prevented setting debug level to 0 explicitly
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 644453f8d93540a91236683015f3418d29c6d95a
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Tue Dec 1 13:03:03 2020 +0100
+
+    LOGS: default log level changed to <= SSSDBG_OP_FAILURE
+
+    :config: New default value of `debug_level` is 0x0070
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 4fe060abbe958c2f9b5aa44e489620063029aa0b
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 22:19:46 2020 +0100
+
+    FILES: reduced debug level in refresh_override_attrs() if case "No overrides, nothing to do"
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 29f243fd5b256efe3c7f4e4f0940c7d0ae6b4fa1
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 22:07:01 2020 +0100
+
+    AD: reduced log level in case check_if_pac_is_available() can't find user entry. This is typical situation when, for example, INITGROUPS lookup is executed for uncached user.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit ed6ec569780ad8203c4990faed5a9f0dc27dd12b
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 21:13:28 2020 +0100
+
+    SDAP: reduced log level in case group without members
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 26fdc3c8f0ae6493442ea291d9bf36ba148ef209
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 21:06:19 2020 +0100
+
+    CACHE_REQ: reduced log level in cache_req_object_by_name_well_known() Non fqdn input isn't necessarily an error here.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit a7b145b99b9f71ad3d02251fff5b587041c9f1ab
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 20:27:44 2020 +0100
+
+    LDAP: reduced log level in hosts_get_done()
+
+    Absent host in LDAP server isn't SSSD failure.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 6e3b4d745fc8d2de14d69aa30bc21aa549a435f8
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 16:45:51 2020 +0100
+
+    SBUS: reduced log level in case of unexpected signal
+
+    Most probably module is not fully initialized yet.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 90dae38d7442757b8a51f91a6ba3fb83f99320a1
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 11:39:56 2020 +0100
+
+    RESPONDER: reduce log level in sss_parse_inp_done() in case of "Unknown domain" since this might be search by UPN
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 69aa3e8c4b82a06e45ba59eb1c17af252aa971ce
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 01:05:52 2020 +0100
+
+    DP: do not log failure in case provider doesn't support check_online method
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 1af89925e62cccacb2957f55b16988a5e71fe5e1
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 00:28:08 2020 +0100
+
+    IPA: corrected confusing message
+
+    Log message like:
+    ```
+    sysdb_getpwnam() got more users than expected. Expected [1], got [0]
+    ```
+    looks a bit confusing.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit a419b7e673d2de571d873b79be31b1ae2fa89832
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 30 00:13:31 2020 +0100
+
+    SSS_IFACE: corrected misleading return code
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 99e44d9db41f5bb56281ed65d815c32139195931
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Nov 29 22:55:07 2020 +0100
+
+    LDAP: added missed \n in log message
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 52dc85540e621b00f358fea94e2e390d580948d8
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Nov 29 21:42:08 2020 +0100
+
+    SYSDB: reduce log level in sysdb_update_members_ex() in case failed attempt to DEL unexisting attribute
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit a7b6413d9fb870f51f09955bdceee01952442c63
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Nov 29 21:32:46 2020 +0100
+
+    UTIL: sss_ldb_error_to_errno() improved
+
+    LDB_ERR_NO_SUCH_ATTRIBUTE error code was added to mapping and log level
+    for unknown error code was reduced.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit ac22859006b5658017b2720ca3e02d34c5beecdd
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Sun Nov 29 17:03:58 2020 +0100
+
+    PAM: reduce log level in may_do_cert_auth()
+
+    Reduce log level in may_do_cert_auth() as this is not a critical failure
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 5068655a67f88cb1730f28689c5effee264321ad
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 21:45:53 2020 +0100
+
+    UTIL: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 3cbd0465b52f9bbb7e20b0b12e154f51bab0866e
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 21:12:16 2020 +0100
+
+    PAM: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit f028253ff87bf11ed034ad5acf1f67e8863bed60
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 20:59:13 2020 +0100
+
+    NSS: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit f457a1a69240381ad7637a09dc66c1aeb78e1d18
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 20:33:11 2020 +0100
+
+    IFP: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 058644f2ef6d1958db657d371158d2df7798dd49
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 20:21:55 2020 +0100
+
+    RESPONDER: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 01ba32f250a0e51771471c52440c11f6f05f2a48
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 20:15:22 2020 +0100
+
+    CACHE_REQ: debug message correction
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 018c08acbb3bbb836c9acefaf5c384eb9231a60a
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 20:05:06 2020 +0100
+
+    AUTOFS: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit fb052a4c9843ce518a7202d842c43631f8bbfd2d
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 19:57:00 2020 +0100
+
+    RESOLV: debug message correction
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit d91409df456f9ad7aad39d0cad0ed053cf1f3653
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 27 19:49:14 2020 +0100
+
+    PROXY: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit ff8f44ce2d2eedb098d980793a949f7f7e55576a
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 20 19:46:28 2020 +0100
+
+    LDAP: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 9244820af59ba6b947cf9aa1269d03bb6f2e4f38
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Fri Nov 20 19:22:36 2020 +0100
+
+    KRB5: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 667b983aaee380c50d50ef07542b004e60041581
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Thu Nov 19 18:31:28 2020 +0100
+
+    IPA: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 2f70695a874dcb84d4b86773138a5a6b6259958f
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 22:12:21 2020 +0100
+
+    DP: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit d6f6f053d7a97a220b52ce92fd653eef8cec5a74
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 21:37:38 2020 +0100
+
+    AD: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 85d8adc4d24f09e47f2a9c0fa595d90c61036b18
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 19:09:33 2020 +0100
+
+    P11_CHILD: severity level of few debug messages adjusted
+
+    Severity level of few debug messages was adjusted and journal message
+    in case of disabled certificate verification was added.
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit fe0530ef96baa8fd39ce6b87c0c760e17c5eb6f8
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 16:28:43 2020 +0100
+
+    MONITOR: severity level of few debug messages adjusted
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit daa5454f870a5436a554091a1333cc8be0cbc566
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 16:02:23 2020 +0100
+
+    SYSDB:views: few debug message corrections
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 82dc14b027f9115cabafce71d2b385d5c7d1dd4f
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 15:56:46 2020 +0100
+
+    SYSDB:upgrade: debug message corrected
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit e731368ed9cea9b35d0ae654e1534084c6ef4642
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 15:50:08 2020 +0100
+
+    SYSDB:service: severity level of few debug messages adjusted
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit f55c9599068c43037a8b666af92ba9b8a044f735
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 15:32:21 2020 +0100
+
+    SYSDB:selinux: debug message severity level was adjusted
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 744582419abfd6e5665315748d44e732f1d56f13
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 15:30:45 2020 +0100
+
+    SYSDB:search: few debug messages were corrected
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit 033c31a2a4994367edea1ded8303a0d2dbc59b1c
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 15:19:46 2020 +0100
+
+    SYSDB:ops: few debug messages were corrected
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit a73df70ee0bcc8f1b80a2e20132592724bd5f675
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Wed Nov 18 13:19:25 2020 +0100
+
+    SYSDB:ipnetworks: severity level of few debug messages adjusted
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit b4acf71d0a81aeeb2754645d2798ce1e927121f3
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 16 21:18:14 2020 +0100
+
+    SYSDB:iphosts: severity level of few debug messages adjusted
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit d8af1db84b48193a546bbeec84a7dd7e2b132244
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 16 20:05:12 2020 +0100
+
+    SYSDB:sudo: changed debug message to be consistent
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit df723cb98b406b0262f04d0e43e8e5bf0030074f
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 16 19:10:41 2020 +0100
+
+    SYSDB: wrong debug message corrected
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+
+commit e350d917e6d48c1d13502ab2849d3e2a0815215e
+Author: Alexey Tikhonov <atikhono@redhat.com>
+Date:   Mon Nov 16 18:13:26 2020 +0100
+
+    SYSDB:autofs: cosmetic updates
+
+    Reviewed-by: Pawel Polawski <ppolawsk@redhat.com>
+    Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/db/sysdb.c                                |  2 +-
+ src/db/sysdb_autofs.c                         |  4 +-
+ src/db/sysdb_iphosts.c                        | 10 ++---
+ src/db/sysdb_ipnetworks.c                     |  6 +--
+ src/db/sysdb_ops.c                            | 37 ++++++++++++------
+ src/db/sysdb_search.c                         | 17 ++++++---
+ src/db/sysdb_selinux.c                        |  2 +-
+ src/db/sysdb_services.c                       |  6 +--
+ src/db/sysdb_sudo.c                           |  3 +-
+ src/db/sysdb_upgrade.c                        |  2 +-
+ src/db/sysdb_views.c                          |  6 +--
+ src/lib/certmap/sss_certmap_krb5_match.c      |  1 -
+ src/man/include/debug_levels.xml              |  3 +-
+ src/man/include/debug_levels_tools.xml        |  3 +-
+ src/monitor/monitor.c                         | 14 +++----
+ src/p11_child/p11_child_common.c              |  2 +-
+ src/p11_child/p11_child_common_utils.c        |  3 ++
+ src/p11_child/p11_child_openssl.c             |  4 +-
+ src/providers/ad/ad_cldap_ping.c              |  2 +-
+ src/providers/ad/ad_common.c                  |  7 ++--
+ src/providers/ad/ad_dyndns.c                  |  6 +--
+ src/providers/ad/ad_gpo.c                     | 16 +++++---
+ src/providers/ad/ad_machine_pw_renewal.c      |  7 ++--
+ src/providers/ad/ad_pac.c                     |  6 ++-
+ src/providers/ad/ad_subdomains.c              |  2 +-
+ src/providers/be_dyndns.c                     |  3 +-
+ src/providers/be_ptask.c                      |  2 +-
+ src/providers/be_refresh.c                    |  3 +-
+ src/providers/data_provider/dp.c              |  4 +-
+ src/providers/data_provider/dp_target_sudo.c  | 10 +++--
+ src/providers/data_provider_be.c              |  5 +--
+ src/providers/data_provider_fo.c              |  2 +-
+ src/providers/data_provider_opts.c            |  6 +--
+ src/providers/data_provider_req.h             |  1 +
+ src/providers/files/files_ops.c               |  2 +-
+ src/providers/ipa/ipa_access.c                |  2 +-
+ src/providers/ipa/ipa_common.c                |  5 +--
+ src/providers/ipa/ipa_hbac_common.c           |  2 +-
+ src/providers/ipa/ipa_hbac_services.c         |  4 +-
+ src/providers/ipa/ipa_hbac_users.c            |  4 +-
+ src/providers/ipa/ipa_id.c                    |  2 +-
+ src/providers/ipa/ipa_init.c                  |  4 +-
+ src/providers/ipa/ipa_s2n_exop.c              |  3 +-
+ src/providers/ipa/ipa_selinux.c               |  4 +-
+ src/providers/ipa/ipa_session.c               |  4 +-
+ src/providers/ipa/ipa_subdomains_ext_groups.c |  3 +-
+ src/providers/ipa/ipa_subdomains_id.c         | 38 +++++++++++++------
+ src/providers/ipa/ipa_subdomains_server.c     | 11 +++---
+ src/providers/ipa/ipa_sudo.c                  | 14 +++----
+ src/providers/ipa/ipa_sudo_async.c            | 10 ++---
+ src/providers/ipa/ipa_sudo_conversion.c       |  6 +--
+ src/providers/ipa/ipa_views.c                 |  4 +-
+ src/providers/krb5/krb5_access.c              |  3 +-
+ src/providers/krb5/krb5_auth.c                |  4 +-
+ src/providers/krb5/krb5_child.c               | 25 ++++++------
+ src/providers/krb5/krb5_child_handler.c       |  4 +-
+ src/providers/krb5/krb5_common.c              |  6 +--
+ .../krb5/krb5_delayed_online_authentication.c |  4 +-
+ src/providers/krb5/krb5_renew_tgt.c           |  4 +-
+ src/providers/krb5/krb5_utils.c               |  2 +-
+ src/providers/ldap/ldap_auth.c                | 12 +++---
+ src/providers/ldap/ldap_child.c               |  2 +-
+ src/providers/ldap/ldap_init.c                |  4 +-
+ src/providers/ldap/ldap_options.c             |  8 ++--
+ src/providers/ldap/sdap.c                     | 28 +++++++++-----
+ src/providers/ldap/sdap_access.c              | 11 +++---
+ src/providers/ldap/sdap_async.c               |  9 +++--
+ src/providers/ldap/sdap_async_autofs.c        |  2 +-
+ src/providers/ldap/sdap_async_connection.c    |  6 +--
+ src/providers/ldap/sdap_async_groups.c        | 27 ++++++++-----
+ src/providers/ldap/sdap_async_initgroups.c    |  6 ++-
+ src/providers/ldap/sdap_async_initgroups_ad.c |  2 +-
+ src/providers/ldap/sdap_async_sudo.c          |  4 +-
+ src/providers/ldap/sdap_child_helpers.c       |  6 +--
+ src/providers/ldap/sdap_hostid.c              |  2 +-
+ src/providers/ldap/sdap_id_op.c               |  2 +-
+ src/providers/proxy/proxy_auth.c              |  6 +--
+ src/providers/proxy/proxy_child.c             |  8 ++--
+ src/providers/proxy/proxy_client.c            |  2 +-
+ src/providers/proxy/proxy_id.c                |  6 +--
+ src/resolv/async_resolv.c                     |  2 +-
+ src/responder/autofs/autofssrv.c              |  2 +-
+ src/responder/autofs/autofssrv_cmd.c          |  6 +--
+ src/responder/common/cache_req/cache_req.c    |  2 +-
+ .../plugins/cache_req_object_by_name.c        |  4 +-
+ src/responder/common/responder_common.c       |  4 +-
+ src/responder/common/responder_get_domains.c  |  2 +-
+ src/responder/common/responder_iface.c        |  4 +-
+ src/responder/ifp/ifp_iface/ifp_iface.c       |  2 +-
+ src/responder/ifp/ifpsrv.c                    |  8 ++--
+ src/responder/ifp/ifpsrv_util.c               |  2 +-
+ src/responder/nss/nss_cmd.c                   | 20 +++++-----
+ src/responder/nss/nss_iface.c                 |  4 +-
+ src/responder/nss/nss_protocol_netgr.c        |  2 +-
+ src/responder/nss/nsssrv.c                    |  2 +-
+ src/responder/pam/pamsrv_cmd.c                |  2 +-
+ src/responder/pam/pamsrv_p11.c                |  4 +-
+ src/sbus/router/sbus_router_handler.c         |  3 +-
+ src/sss_iface/sss_iface.c                     |  4 +-
+ src/util/child_common.c                       |  2 +-
+ src/util/debug.h                              |  4 +-
+ src/util/domain_info_utils.c                  |  2 +-
+ src/util/server.c                             | 15 +++++---
+ src/util/sss_sockets.c                        |  2 +-
+ src/util/string_utils.c                       |  2 +-
+ src/util/util_errors.c                        |  3 +-
+ 106 files changed, 364 insertions(+), 279 deletions(-)
+
+diff --git a/src/db/sysdb.c b/src/db/sysdb.c
+index d0052d99b..d78991e36 100644
+--- a/src/db/sysdb.c
++++ b/src/db/sysdb.c
+@@ -1489,7 +1489,7 @@ errno_t sysdb_attrs_primary_name(struct sysdb_ctx *sysdb,
+          * decide which name is correct.
+          */
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Cannot save entry. Unable to determine groupname\n");
++              "Can't match the name to the RDN\n");
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/db/sysdb_autofs.c b/src/db/sysdb_autofs.c
+index 413b00722..1febdaec5 100644
+--- a/src/db/sysdb_autofs.c
++++ b/src/db/sysdb_autofs.c
+@@ -243,14 +243,14 @@ sysdb_get_map_byname(TALLOC_CTX *mem_ctx,
+               "Error looking up autofs map [%s]\n", safe_map_name);
+         goto done;
+     } else if (ret == ENOENT) {
+-        DEBUG(SSSDBG_TRACE_FUNC, "No such map\n");
++        DEBUG(SSSDBG_TRACE_FUNC, "No such map [%s]\n", safe_map_name);
+         *_map = NULL;
+         goto done;
+     }
+ 
+     if (count != 1) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "More than one map named %s\n", safe_map_name);
++              "More than one map named [%s]\n", safe_map_name);
+         goto done;
+     }
+ 
+diff --git a/src/db/sysdb_iphosts.c b/src/db/sysdb_iphosts.c
+index b82279787..d3ee8f1a9 100644
+--- a/src/db/sysdb_iphosts.c
++++ b/src/db/sysdb_iphosts.c
+@@ -222,14 +222,14 @@ sysdb_store_host(struct sss_domain_info *domain,
+                  * sort it out.
+                  */
+                 for (j = 0; j < res->count; j++) {
+-                    DEBUG(SSSDBG_TRACE_FUNC,
++                    DEBUG(SSSDBG_CRIT_FAILURE,
+                           "Corrupt cache entry [%s] detected. Deleting\n",
+                            ldb_dn_canonical_string(tmp_ctx,
+                                                    res->msgs[j]->dn));
+ 
+                     ret = sysdb_delete_entry(sysdb, res->msgs[j]->dn, true);
+                     if (ret != EOK) {
+-                        DEBUG(SSSDBG_MINOR_FAILURE,
++                        DEBUG(SSSDBG_OP_FAILURE,
+                               "Could not delete corrupt cache entry [%s]\n",
+                                ldb_dn_canonical_string(tmp_ctx,
+                                                        res->msgs[j]->dn));
+@@ -262,7 +262,7 @@ sysdb_store_host(struct sss_domain_info *domain,
+ 
+                     ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+                     if (ret != EOK) {
+-                        DEBUG(SSSDBG_MINOR_FAILURE,
++                        DEBUG(SSSDBG_OP_FAILURE,
+                               "Could not delete cache entry [%s]\n",
+                                ldb_dn_canonical_string(tmp_ctx,
+                                                        res->msgs[0]->dn));
+@@ -298,7 +298,7 @@ sysdb_store_host(struct sss_domain_info *domain,
+ 
+                 ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+                 if (ret != EOK) {
+-                    DEBUG(SSSDBG_MINOR_FAILURE,
++                    DEBUG(SSSDBG_OP_FAILURE,
+                           "Could not delete corrupt cache entry [%s]\n",
+                            ldb_dn_canonical_string(tmp_ctx,
+                                                    res->msgs[i]->dn));
+@@ -318,7 +318,7 @@ sysdb_store_host(struct sss_domain_info *domain,
+                     /* Delete the entry from the previous pass */
+                     ret = sysdb_delete_entry(sysdb, update_dn, true);
+                     if (ret != EOK) {
+-                        DEBUG(SSSDBG_MINOR_FAILURE,
++                        DEBUG(SSSDBG_OP_FAILURE,
+                               "Could not delete cache entry [%s]\n",
+                                ldb_dn_canonical_string(tmp_ctx,
+                                                        update_dn));
+diff --git a/src/db/sysdb_ipnetworks.c b/src/db/sysdb_ipnetworks.c
+index 326f984b7..9da4d9b23 100644
+--- a/src/db/sysdb_ipnetworks.c
++++ b/src/db/sysdb_ipnetworks.c
+@@ -261,7 +261,7 @@ sysdb_store_ipnetwork(struct sss_domain_info *domain,
+ 
+                 ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+                 if (ret != EOK) {
+-                    DEBUG(SSSDBG_MINOR_FAILURE,
++                    DEBUG(SSSDBG_OP_FAILURE,
+                             "Could not delete cache entry [%s]\n",
+                             ldb_dn_canonical_string(tmp_ctx,
+                                                     res->msgs[0]->dn));
+@@ -296,7 +296,7 @@ sysdb_store_ipnetwork(struct sss_domain_info *domain,
+ 
+                 ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+                 if (ret != EOK) {
+-                    DEBUG(SSSDBG_MINOR_FAILURE,
++                    DEBUG(SSSDBG_OP_FAILURE,
+                           "Could not delete corrupt cache entry [%s]\n",
+                            ldb_dn_canonical_string(tmp_ctx,
+                                                    res->msgs[i]->dn));
+@@ -315,7 +315,7 @@ sysdb_store_ipnetwork(struct sss_domain_info *domain,
+                     /* Delete the entry from the previous pass */
+                     ret = sysdb_delete_entry(sysdb, update_dn, true);
+                     if (ret != EOK) {
+-                        DEBUG(SSSDBG_MINOR_FAILURE,
++                        DEBUG(SSSDBG_OP_FAILURE,
+                               "Could not delete cache entry [%s]\n",
+                                ldb_dn_canonical_string(tmp_ctx,
+                                                        update_dn));
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 3412b9cd1..585708abe 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -157,7 +157,7 @@ static int sysdb_delete_cache_entry(struct ldb_context *ldb,
+         /* fall through */
+         SSS_ATTRIBUTE_FALLTHROUGH;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "LDB Error: %s(%d)\nError Message: [%s]\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "LDB Error: %s (%d); error message: [%s]\n",
+                   ldb_strerror(ret), ret, ldb_errstring(ldb));
+         return sysdb_error_to_errno(ret);
+     }
+@@ -3420,7 +3420,7 @@ int sysdb_search_custom(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+     if (!ldb_dn_validate(basedn)) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create DN.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Syntactically invalid subtree DN.\n");
+         ret = EINVAL;
+         goto done;
+     }
+@@ -3463,7 +3463,7 @@ int sysdb_search_custom_by_name(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+     if (!ldb_dn_validate(basedn)) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create DN.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Syntactically invalid DN.\n");
+         ret = EINVAL;
+         goto done;
+     }
+@@ -3545,7 +3545,7 @@ errno_t sysdb_search_by_orig_dn(TALLOC_CTX *mem_ctx,
+     default:
+         DEBUG(SSSDBG_CRIT_FAILURE,
+               "Trying to perform a search by orig_dn using a "
+-              "non-supported type\n");
++              "non-supported type %d\n", type);
+         ret = EINVAL;
+         goto done;
+     }
+@@ -3690,8 +3690,9 @@ int sysdb_delete_custom(struct sss_domain_info *domain,
+         break;
+ 
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "LDB Error: %s(%d)\nError Message: [%s]\n",
+-                  ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "ldb_delete failed: %s (%d); error Message: [%s]\n",
++              ldb_strerror(ret), ret, ldb_errstring(domain->sysdb->ldb));
+         ret = sysdb_error_to_errno(ret);
+         break;
+     }
+@@ -4927,9 +4928,15 @@ static errno_t sysdb_update_members_ex(struct sss_domain_info *domain,
+             ret = sysdb_add_group_member(domain, add_groups[i],
+                                          member, type, is_dn);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Could not add member [%s] to group [%s]. "
+-                          "Skipping.\n", member, add_groups[i]);
++                if (ret != EEXIST) {
++                    DEBUG(SSSDBG_CRIT_FAILURE,
++                          "Could not add member [%s] to group [%s]. "
++                              "Skipping.\n", member, add_groups[i]);
++                } else {
++                    DEBUG(SSSDBG_FUNC_DATA,
++                          "Group [%s] already has member [%s]. Skipping.\n",
++                          add_groups[i], member);
++                }
+                 /* Continue on, we should try to finish the rest */
+             }
+         }
+@@ -4941,9 +4948,15 @@ static errno_t sysdb_update_members_ex(struct sss_domain_info *domain,
+             ret = sysdb_remove_group_member(domain, del_groups[i],
+                                             member, type, is_dn);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Could not remove member [%s] from group [%s]. "
+-                          "Skipping\n", member, del_groups[i]);
++                if (ret != ENOENT) {
++                    DEBUG(SSSDBG_CRIT_FAILURE,
++                          "Could not remove member [%s] from group [%s]. "
++                              "Skipping\n", member, del_groups[i]);
++                } else {
++                    DEBUG(SSSDBG_FUNC_DATA,
++                          "No member [%s] in group [%s]. "
++                              "Skipping\n", member, del_groups[i]);
++                }
+                 /* Continue on, we should try to finish the rest */
+             }
+         }
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index 4ff65c1ae..0cd8321cb 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -2393,7 +2393,7 @@ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+     } else if (mtype == SYSDB_MEMBER_GROUP) {
+         dn = sysdb_group_strdn(tmp_ctx, dom->name, name);
+     } else {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown member type\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown member type %d\n", mtype);
+         ret = EINVAL;
+         goto done;
+     }
+@@ -2453,13 +2453,14 @@ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+         tmp_str = ldb_msg_find_attr_as_string(direct_sysdb_groups[i],
+                                                 SYSDB_NAME, NULL);
+         if (!tmp_str) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "A group with no name?\n");
+             /* This should never happen, but if it does, just continue */
+             continue;
+         }
+ 
+         direct_parents[pi] = talloc_strdup(direct_parents, tmp_str);
+         if (!direct_parents[pi]) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "A group with no name?\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+             ret = EIO;
+             goto done;
+         }
+@@ -2522,8 +2523,13 @@ errno_t sysdb_get_real_name(TALLOC_CTX *mem_ctx,
+         }
+         if (ret != EOK) {
+             /* User cannot be found in cache */
+-            DEBUG(SSSDBG_OP_FAILURE, "Cannot find user [%s] in cache\n",
+-                                     name_or_upn_or_sid);
++            if (ret != ENOENT) {
++                DEBUG(SSSDBG_OP_FAILURE, "Failed to find user [%s] in cache: %d\n",
++                                         name_or_upn_or_sid, ret);
++            } else {
++                DEBUG(SSSDBG_TRACE_FUNC, "User [%s] is missing in cache\n",
++                                         name_or_upn_or_sid);
++            }
+             goto done;
+         }
+     } else if (res->count == 1) {
+@@ -2537,7 +2543,8 @@ errno_t sysdb_get_real_name(TALLOC_CTX *mem_ctx,
+ 
+     cname = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+     if (!cname) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "User '%s' without a name?\n", name_or_upn_or_sid);
+         ret = ENOENT;
+         goto done;
+     }
+diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c
+index 88ac88786..535411950 100644
+--- a/src/db/sysdb_selinux.c
++++ b/src/db/sysdb_selinux.c
+@@ -234,7 +234,7 @@ errno_t sysdb_delete_usermaps(struct sss_domain_info *domain)
+     ret = sysdb_delete_recursive(sysdb, dn, true);
+     talloc_free(dn);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
+         return ret;
+     }
+ 
+diff --git a/src/db/sysdb_services.c b/src/db/sysdb_services.c
+index 8118fef00..ac17f4704 100644
+--- a/src/db/sysdb_services.c
++++ b/src/db/sysdb_services.c
+@@ -252,7 +252,7 @@ sysdb_store_service(struct sss_domain_info *domain,
+ 
+                 ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true);
+                 if (ret != EOK) {
+-                    DEBUG(SSSDBG_MINOR_FAILURE,
++                    DEBUG(SSSDBG_OP_FAILURE,
+                           "Could not delete cache entry [%s]\n",
+                            ldb_dn_canonical_string(tmp_ctx,
+                                                    res->msgs[0]->dn));
+@@ -290,7 +290,7 @@ sysdb_store_service(struct sss_domain_info *domain,
+ 
+                 ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true);
+                 if (ret != EOK) {
+-                    DEBUG(SSSDBG_MINOR_FAILURE,
++                    DEBUG(SSSDBG_OP_FAILURE,
+                           "Could not delete corrupt cache entry [%s]\n",
+                            ldb_dn_canonical_string(tmp_ctx,
+                                                    res->msgs[i]->dn));
+@@ -310,7 +310,7 @@ sysdb_store_service(struct sss_domain_info *domain,
+                     /* Delete the entry from the previous pass */
+                     ret = sysdb_delete_entry(sysdb, update_dn, true);
+                     if (ret != EOK) {
+-                        DEBUG(SSSDBG_MINOR_FAILURE,
++                        DEBUG(SSSDBG_OP_FAILURE,
+                               "Could not delete cache entry [%s]\n",
+                                ldb_dn_canonical_string(tmp_ctx,
+                                                        update_dn));
+diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
+index 03eec9c70..1626b612d 100644
+--- a/src/db/sysdb_sudo.c
++++ b/src/db/sysdb_sudo.c
+@@ -480,7 +480,8 @@ sysdb_get_sudo_user_info(TALLOC_CTX *mem_ctx,
+                     sss_get_cased_name(sysdb_groupnames, groupname,
+                                        domain->case_sensitive);
+                 if (sysdb_groupnames[num_groups] == NULL) {
+-                    DEBUG(SSSDBG_MINOR_FAILURE, "Cannot strdup %s\n", groupname);
++                    DEBUG(SSSDBG_CRIT_FAILURE,
++                          "sss_get_cased_name() failed for '%s'\n", groupname);
+                     continue;
+                 }
+                 num_groups++;
+diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
+index 03a0e6173..99213260c 100644
+--- a/src/db/sysdb_upgrade.c
++++ b/src/db/sysdb_upgrade.c
+@@ -2455,7 +2455,7 @@ int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver)
+ 
+     ret = add_object_category(sysdb->ldb, ctx);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "add_object_category failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "add_object_category failed: %d\n", ret);
+         goto done;
+     }
+ 
+diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
+index 00da74047..269dab70f 100644
+--- a/src/db/sysdb_views.c
++++ b/src/db/sysdb_views.c
+@@ -556,12 +556,12 @@ errno_t sysdb_store_override(struct sss_domain_info *domain,
+         if (ret == ENOENT) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "Object to override does not exists.\n");
+         } else {
+-            DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_entry failed.\n");
+         }
+         goto done;
+     }
+     if (count != 1) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Base searched returned more than one object.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Base search returned more than one object.\n");
+         ret = EINVAL;
+         goto done;
+     }
+@@ -660,7 +660,7 @@ errno_t sysdb_store_override(struct sss_domain_info *domain,
+                                      SYSDB_OVERRIDE_GROUP_CLASS);
+             break;
+         default:
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected object type.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected object type %d.\n", type);
+             ret = EINVAL;
+             goto done;
+         }
+diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c
+index 640930747..ab566ac99 100644
+--- a/src/lib/certmap/sss_certmap_krb5_match.c
++++ b/src/lib/certmap/sss_certmap_krb5_match.c
+@@ -220,7 +220,6 @@ static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx,
+ 
+     for (c = 0; eku_list[c] != NULL; c++) {
+         for (k = 0; sss_ext_key_usage[k].name != NULL; k++) {
+-CM_DEBUG(ctx, "[%s][%s].", eku_list[c], sss_ext_key_usage[k].name);
+             if (strcasecmp(eku_list[c], sss_ext_key_usage[k].name) == 0) {
+                 comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
+                                                       sss_ext_key_usage[k].oid);
+diff --git a/src/man/include/debug_levels.xml b/src/man/include/debug_levels.xml
+index b5e13ba3e..0d9cc17be 100644
+--- a/src/man/include/debug_levels.xml
++++ b/src/man/include/debug_levels.xml
+@@ -100,6 +100,7 @@
+         introduced in 1.7.0.
+     </para>
+     <para>
+-        <emphasis>Default</emphasis>: 0
++        <emphasis>Default</emphasis>: 0x0070 (i.e. fatal, critical and serious
++        failures; corresponds to setting 2 in decimal notation)
+     </para>
+ </listitem>
+diff --git a/src/man/include/debug_levels_tools.xml b/src/man/include/debug_levels_tools.xml
+index b592d50fc..46a3c7d29 100644
+--- a/src/man/include/debug_levels_tools.xml
++++ b/src/man/include/debug_levels_tools.xml
+@@ -81,6 +81,7 @@
+         introduced in 1.7.0.
+     </para>
+     <para>
+-        <emphasis>Default</emphasis>: 0
++        <emphasis>Default</emphasis>: 0x0070 (i.e. fatal, critical and serious
++        failures; corresponds to setting 2 in decimal notation)
+     </para>
+ </listitem>
+diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
+index d9da05a51..9c2381c81 100644
+--- a/src/monitor/monitor.c
++++ b/src/monitor/monitor.c
+@@ -1435,7 +1435,7 @@ static void monitor_quit(struct mt_ctx *mt_ctx, int ret)
+                         DEBUG(SSSDBG_CRIT_FAILURE,
+                               "Child [%s] terminated with a signal\n", svc->name);
+                     } else {
+-                        DEBUG(SSSDBG_FATAL_FAILURE,
++                        DEBUG(SSSDBG_CRIT_FAILURE,
+                               "Child [%s] did not exit cleanly\n", svc->name);
+                         /* Forcibly kill this child */
+                         kill(-svc->pid, SIGKILL);
+@@ -2059,7 +2059,7 @@ static void monitor_sbus_connected(struct tevent_req *req)
+ 
+     ret = sbus_connection_add_path_map(ctx->sbus_conn, paths);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+               ret, sss_strerror(ret));
+         goto done;
+     }
+@@ -2271,7 +2271,7 @@ static void mt_svc_restart(struct tevent_context *ev,
+         add_new_provider(svc->mt_ctx, svc->name, svc->restarts + 1);
+     } else {
+         /* Invalid type? */
+-        DEBUG(SSSDBG_CRIT_FAILURE,
++        DEBUG(SSSDBG_FATAL_FAILURE,
+               "BUG: Invalid child process type [%d]\n", svc->type);
+     }
+ 
+@@ -2580,14 +2580,14 @@ int main(int argc, const char *argv[])
+         switch (ret) {
+         case EPERM:
+         case EACCES:
+-            DEBUG(SSSDBG_CRIT_FAILURE,
++            DEBUG(SSSDBG_FATAL_FAILURE,
+                   CONF_FILE_PERM_ERROR_MSG, config_file);
+-            sss_log(SSS_LOG_ALERT, CONF_FILE_PERM_ERROR_MSG, config_file);
++            sss_log(SSS_LOG_CRIT, CONF_FILE_PERM_ERROR_MSG, config_file);
+             break;
+         default:
+-            DEBUG(SSSDBG_CRIT_FAILURE,
++            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "SSSD couldn't load the configuration database.\n");
+-            sss_log(SSS_LOG_ALERT,
++            sss_log(SSS_LOG_CRIT,
+                    "SSSD couldn't load the configuration database [%d]: %s.\n",
+                     ret, strerror(ret));
+             break;
+diff --git a/src/p11_child/p11_child_common.c b/src/p11_child/p11_child_common.c
+index f17de1a9e..704ced4b6 100644
+--- a/src/p11_child/p11_child_common.c
++++ b/src/p11_child/p11_child_common.c
+@@ -125,7 +125,7 @@ static errno_t p11c_recv_data(TALLOC_CTX *mem_ctx, int fd, char **pin)
+ 
+     str = talloc_strndup(mem_ctx, (char *) buf, len);
+     if (str == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
+         return ENOMEM;
+     }
+ 
+diff --git a/src/p11_child/p11_child_common_utils.c b/src/p11_child/p11_child_common_utils.c
+index 50cfebb4c..c5f324625 100644
+--- a/src/p11_child/p11_child_common_utils.c
++++ b/src/p11_child/p11_child_common_utils.c
+@@ -107,6 +107,9 @@ errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts,
+                   "Found 'no_verification' option, "
+                   "disabling verification completely. "
+                   "This should not be used in production.\n");
++            sss_log(SSS_LOG_CRIT,
++                    "Smart card certificate verification disabled completely. "
++                    "This should not be used in production.");
+             cert_verify_opts->do_verification = false;
+         } else if (strncasecmp(opts[c], OCSP_DEFAUL_RESPONDER,
+                                OCSP_DEFAUL_RESPONDER_LEN) == 0) {
+diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
+index d81a1a9ea..879b05b65 100644
+--- a/src/p11_child/p11_child_openssl.c
++++ b/src/p11_child/p11_child_openssl.c
+@@ -226,7 +226,7 @@ static char *get_issuer_subject_str(TALLOC_CTX *mem_ctx, X509 *cert)
+ 
+     bio_mem = BIO_new(BIO_s_mem());
+     if (bio_mem == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "BIO_new failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "BIO_new failed.\n");
+         return NULL;
+     }
+ 
+@@ -591,7 +591,7 @@ errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *ca_db,
+     ret = SSL_library_init();
+ #endif
+     if (ret != 1) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize OpenSSL.\n");
++        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to initialize OpenSSL.\n");
+         return EIO;
+     }
+ 
+diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c
+index ab234f4d7..7722af98a 100644
+--- a/src/providers/ad/ad_cldap_ping.c
++++ b/src/providers/ad/ad_cldap_ping.c
+@@ -467,7 +467,7 @@ ad_cldap_ping_domain_send(TALLOC_CTX *mem_ctx,
+     domains[0] = discovery_domain;
+     domains[1] = NULL;
+     if (domains[0] == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Bad argument (discovery_domain)");
+         ret = ENOMEM;
+         goto done;
+     }
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index 624313942..eaa920ca0 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -1072,15 +1072,14 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
+     }
+ 
+     if (!service->gc->uri) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to append to URI\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "NULL GC URI\n");
+         ret = ENOMEM;
+         goto done;
+     }
+     DEBUG(SSSDBG_CONF_SETTINGS, "Constructed GC uri '%s'\n", service->gc->uri);
+ 
+     if (service->gc->sockaddr == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-                "resolv_get_sockaddr_address failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "NULL GC sockaddr\n");
+         ret = EIO;
+         goto done;
+     }
+@@ -1100,7 +1099,7 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
+ done:
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Error: [%s]\n", strerror(ret));
++              "Error: %d [%s]\n", ret, strerror(ret));
+     }
+     talloc_free(tmp_ctx);
+     return;
+diff --git a/src/providers/ad/ad_dyndns.c b/src/providers/ad/ad_dyndns.c
+index 71ef16c0b..19fc8acef 100644
+--- a/src/providers/ad/ad_dyndns.c
++++ b/src/providers/ad/ad_dyndns.c
+@@ -63,7 +63,7 @@ errno_t ad_dyndns_init(struct be_ctx *be_ctx,
+      */
+     ret = ad_get_dyndns_options(be_ctx, ad_opts);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set AD options\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Could not get AD dyndns options\n");
+         return ret;
+     }
+ 
+@@ -209,8 +209,8 @@ static void ad_dyndns_update_connect_done(struct tevent_req *subreq)
+ 
+     ret = ldap_url_parse(ctx->service->sdap->uri, &lud);
+     if (ret != LDAP_SUCCESS) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Failed to parse ldap URI (%s)!\n", ctx->service->sdap->uri);
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse ldap URI '%s': %d\n",
++              ctx->service->sdap->uri, ret);
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
+index 0eb5416ac..b15e0f345 100644
+--- a/src/providers/ad/ad_gpo.c
++++ b/src/providers/ad/ad_gpo.c
+@@ -671,7 +671,9 @@ ad_gpo_ace_includes_client_sid(const char *user_sid,
+ 
+     err = sss_idmap_sid_to_smb_sid(idmap_ctx, user_sid, &user_dom_sid);
+     if (err != IDMAP_SUCCESS) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize idmap context.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "sss_idmap_sid_to_smb_sid() failed for user_sid '%s': %d\n",
++              user_sid, err);
+         return EFAULT;
+     }
+ 
+@@ -684,7 +686,9 @@ ad_gpo_ace_includes_client_sid(const char *user_sid,
+ 
+     err = sss_idmap_sid_to_smb_sid(idmap_ctx, host_sid, &host_dom_sid);
+     if (err != IDMAP_SUCCESS) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize idmap context.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "sss_idmap_sid_to_smb_sid() failed for host_sid '%s': %d\n",
++              host_sid, err);
+         return EFAULT;
+     }
+ 
+@@ -698,7 +702,9 @@ ad_gpo_ace_includes_client_sid(const char *user_sid,
+     for (i = 0; i < group_size; i++) {
+         err = sss_idmap_sid_to_smb_sid(idmap_ctx, group_sids[i], &group_dom_sid);
+         if (err != IDMAP_SUCCESS) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize idmap context.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "sss_idmap_sid_to_smb_sid() failed for group_sid '%s': %d\n",
++              group_sids[i], err);
+             return EFAULT;
+         }
+         included = ad_gpo_dom_sid_equal(&ace_dom_sid, group_dom_sid);
+@@ -4777,14 +4783,14 @@ gpo_fork_child(struct tevent_req *req)
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", errno, strerror(errno));
++              "pipe (from) failed [%d][%s].\n", errno, strerror(errno));
+         goto fail;
+     }
+     ret = pipe(pipefd_to_child);
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", errno, strerror(errno));
++              "pipe (to) failed [%d][%s].\n", errno, strerror(errno));
+         goto fail;
+     }
+ 
+diff --git a/src/providers/ad/ad_machine_pw_renewal.c b/src/providers/ad/ad_machine_pw_renewal.c
+index ce9bbe6f3..6e7137a86 100644
+--- a/src/providers/ad/ad_machine_pw_renewal.c
++++ b/src/providers/ad/ad_machine_pw_renewal.c
+@@ -171,14 +171,14 @@ ad_machine_account_password_renewal_send(TALLOC_CTX *mem_ctx,
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", ret, strerror(ret));
++              "pipe (from) failed [%d][%s].\n", ret, strerror(ret));
+         goto done;
+     }
+     ret = pipe(pipefd_to_child);
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", ret, strerror(ret));
++              "pipe (to) failed [%d][%s].\n", ret, strerror(ret));
+         goto done;
+     }
+ 
+@@ -354,7 +354,8 @@ errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx,
+     }
+ 
+     if (opt_list_size != 2) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Wrong number of renewal options.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Wrong number of renewal options %d\n",
++              opt_list_size);
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/providers/ad/ad_pac.c b/src/providers/ad/ad_pac.c
+index 80424b44e..aff47304e 100644
+--- a/src/providers/ad/ad_pac.c
++++ b/src/providers/ad/ad_pac.c
+@@ -120,7 +120,11 @@ errno_t check_if_pac_is_available(TALLOC_CTX *mem_ctx,
+ 
+     ret = find_user_entry(mem_ctx, dom, ar, &msg);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "find_user_entry failed.\n");
++        if (ret == ENOENT) {
++            DEBUG(SSSDBG_FUNC_DATA, "find_user_entry didn't find user entry.\n");
++        } else {
++            DEBUG(SSSDBG_OP_FAILURE, "find_user_entry failed.\n");
++        }
+         return ret;
+     }
+ 
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 4c457b7e5..f5b0be6c2 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -299,7 +299,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
+ 
+     subdom_conf_path = subdomain_create_conf_path(id_ctx, subdom);
+     if (subdom_conf_path == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "subdom_conf_path failed\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "subdomain_create_conf_path failed\n");
+         return ENOMEM;
+     }
+ 
+diff --git a/src/providers/be_dyndns.c b/src/providers/be_dyndns.c
+index 2de3b11bb..1a304db37 100644
+--- a/src/providers/be_dyndns.c
++++ b/src/providers/be_dyndns.c
+@@ -1111,7 +1111,8 @@ be_nsupdate_args(TALLOC_CTX *mem_ctx,
+         argc++;
+         break;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown nsupdate auth type\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unknown nsupdate auth type %d\n", auth_type);
+         goto fail;
+     }
+ 
+diff --git a/src/providers/be_ptask.c b/src/providers/be_ptask.c
+index fb80909a0..fab9e21b8 100644
+--- a/src/providers/be_ptask.c
++++ b/src/providers/be_ptask.c
+@@ -251,7 +251,7 @@ static void be_ptask_schedule(struct be_ptask *task,
+     task->timer = tevent_add_timer(task->ev, task, tv, be_ptask_execute, task);
+     if (task->timer == NULL) {
+         /* nothing we can do about it */
+-        DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Unable to schedule task [%s]\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to schedule task [%s]\n",
+                                     task->name);
+         be_ptask_disable(task);
+     }
+diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
+index 01cbf03e2..fdddf8bca 100644
+--- a/src/providers/be_refresh.c
++++ b/src/providers/be_refresh.c
+@@ -125,7 +125,8 @@ static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx,
+         base_dn = sysdb_netgroup_base_dn(mem_ctx, domain);
+         break;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Uknown or unsupported refresh type\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Uknown or unsupported refresh type %d\n", type);
+         return ERR_INTERNAL;
+         break;
+     }
+diff --git a/src/providers/data_provider/dp.c b/src/providers/data_provider/dp.c
+index 0858c43d2..90324d74d 100644
+--- a/src/providers/data_provider/dp.c
++++ b/src/providers/data_provider/dp.c
+@@ -109,7 +109,7 @@ dp_init_interface(struct data_provider *provider)
+ 
+     ret = sbus_connection_add_path_map(provider->sbus_conn, paths);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+               ret, sss_strerror(ret));
+     }
+ 
+@@ -196,7 +196,7 @@ dp_init_send(TALLOC_CTX *mem_ctx,
+         (sbus_server_on_connection_cb)dp_client_init,
+         (sbus_server_on_connection_data)state->provider);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create subrequest!\n");
+         ret = ENOMEM;
+         goto done;
+     }
+diff --git a/src/providers/data_provider/dp_target_sudo.c b/src/providers/data_provider/dp_target_sudo.c
+index db14039c4..59e2358cc 100644
+--- a/src/providers/data_provider/dp_target_sudo.c
++++ b/src/providers/data_provider/dp_target_sudo.c
+@@ -42,13 +42,13 @@ static errno_t dp_sudo_parse_message(TALLOC_CTX *mem_ctx,
+ 
+     ret = sbus_iterator_read_u(read_iter, &dp_flags);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse the message (flags)!\n");
+         return ret;
+     }
+ 
+     ret = sbus_iterator_read_u(read_iter, &sudo_type);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse the message (type)!\n");
+         return ret;
+     }
+ 
+@@ -66,13 +66,15 @@ static errno_t dp_sudo_parse_message(TALLOC_CTX *mem_ctx,
+         /* read rules_num */
+         ret = sbus_iterator_read_u(read_iter, &num_rules);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n");
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to parse the message (num rules)!\n");
+             return ret;
+         }
+ 
+         ret = sbus_iterator_read_as(mem_ctx, read_iter, &rules);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Failed, to parse the message!\n");
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to parse the message (rules)!\n");
+             return ret;
+         }
+         break;
+diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
+index 10421c6b4..f059a3f96 100644
+--- a/src/providers/data_provider_be.c
++++ b/src/providers/data_provider_be.c
+@@ -407,7 +407,7 @@ static void check_if_online(struct be_ctx *be_ctx, int delay)
+                                   check_if_online_delayed, be_ctx);
+ 
+     if (time_event == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE,
++        DEBUG(SSSDBG_CRIT_FAILURE,
+               "Scheduling check_if_online_delayed failed.\n");
+         goto failed;
+     }
+@@ -420,7 +420,6 @@ static void check_if_online(struct be_ctx *be_ctx, int delay)
+ 
+ failed:
+     be_ctx->check_online_ref_count--;
+-    DEBUG(SSSDBG_CRIT_FAILURE, "Failed to run a check_online test.\n");
+ 
+     if (be_ctx->check_online_ref_count == 0) {
+         reset_fo(be_ctx);
+@@ -629,7 +628,7 @@ static void dp_initialized(struct tevent_req *req)
+ 
+     ret = be_register_monitor_iface(be_ctx->mon_conn, be_ctx);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register monitor interface "
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register monitor interface "
+               "[%d]: %s\n", ret, sss_strerror(ret));
+         goto done;
+     }
+diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c
+index 8dc09f5b2..0dfbb04b0 100644
+--- a/src/providers/data_provider_fo.c
++++ b/src/providers/data_provider_fo.c
+@@ -651,7 +651,7 @@ errno_t be_resolve_server_process(struct tevent_req *subreq,
+         srvaddr = fo_get_server_hostent(state->srv);
+         if (!srvaddr) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "FATAL: No hostent available for server (%s)\n",
++                  "No hostent available for server (%s)\n",
+                   fo_get_server_str_name(state->srv));
+             return EFAULT;
+         }
+diff --git a/src/providers/data_provider_opts.c b/src/providers/data_provider_opts.c
+index 9db43fc40..bb543ae4f 100644
+--- a/src/providers/data_provider_opts.c
++++ b/src/providers/data_provider_opts.c
+@@ -233,7 +233,7 @@ static int dp_copy_options_ex(TALLOC_CTX *memctx,
+             }
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Failed to retrieve value for option (%s)\n",
++                      "Failed to copy value for option (%s)\n",
+                        opts[i].opt_name);
+                 goto done;
+             }
+@@ -249,7 +249,7 @@ static int dp_copy_options_ex(TALLOC_CTX *memctx,
+             }
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Failed to retrieve value for option (%s)\n",
++                      "Failed to copy value for option (%s)\n",
+                       opts[i].opt_name);
+                 goto done;
+             }
+@@ -265,7 +265,7 @@ static int dp_copy_options_ex(TALLOC_CTX *memctx,
+             }
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Failed to retrieve value for option (%s)\n",
++                      "Failed to copy value for option (%s)\n",
+                        opts[i].opt_name);
+                 goto done;
+             }
+diff --git a/src/providers/data_provider_req.h b/src/providers/data_provider_req.h
+index f2e05797f..75f7f9713 100644
+--- a/src/providers/data_provider_req.h
++++ b/src/providers/data_provider_req.h
+@@ -39,6 +39,7 @@
+ #define BE_REQ_USER_AND_GROUP 0x0012
+ #define BE_REQ_BY_UUID        0x0013
+ #define BE_REQ_BY_CERT        0x0014
++#define BE_REQ__LAST          BE_REQ_BY_CERT /* must be equal to max REQ number */
+ #define BE_REQ_TYPE_MASK      0x00FF
+ 
+ /**
+diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c
+index 59fc20692..54d2b4164 100644
+--- a/src/providers/files/files_ops.c
++++ b/src/providers/files/files_ops.c
+@@ -395,7 +395,7 @@ static errno_t refresh_override_attrs(struct files_id_ctx *id_ctx,
+                              override_attrs, &count, &msgs);
+     if (ret != EOK) {
+         if (ret == ENOENT) {
+-            DEBUG(SSSDBG_OP_FAILURE, "No overrides, nothing to do.\n");
++            DEBUG(SSSDBG_TRACE_FUNC, "No overrides, nothing to do.\n");
+             ret = EOK;
+         } else {
+             DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
+diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
+index 375b6f885..4a6727c97 100644
+--- a/src/providers/ipa/ipa_access.c
++++ b/src/providers/ipa/ipa_access.c
+@@ -671,7 +671,7 @@ static void ipa_pam_access_handler_done(struct tevent_req *subreq)
+     talloc_free(subreq);
+ 
+     if (ret == ENOENT) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC rules find, denying access\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC rules found, denying access\n");
+         state->pd->pam_status = PAM_PERM_DENIED;
+         goto done;
+     } else if (ret != EOK) {
+diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
+index 1211ba4c9..8cadb9249 100644
+--- a/src/providers/ipa/ipa_common.c
++++ b/src/providers/ipa/ipa_common.c
+@@ -781,8 +781,7 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts,
+                                     dp_opt_get_string(ipa_opts->auth,
+                                                       KRB5_REALM));
+         if (value == NULL) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set %s!\n",
+-                     ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name);
++            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+             ret = ENOMEM;
+             goto done;
+         }
+@@ -851,7 +850,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
+     srvaddr = fo_get_server_hostent(server);
+     if (!srvaddr) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "FATAL: No hostent available for server (%s)\n",
++              "No hostent available for server (%s)\n",
+                   fo_get_server_str_name(server));
+         talloc_free(tmp_ctx);
+         return;
+diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
+index 31e53d24d..1fee41a36 100644
+--- a/src/providers/ipa/ipa_hbac_common.c
++++ b/src/providers/ipa/ipa_hbac_common.c
+@@ -423,7 +423,7 @@ hbac_eval_user_element(TALLOC_CTX *mem_ctx,
+     ret = sysdb_initgroups(tmp_ctx, domain, username, &res);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "sysdb_asq_search failed [%d]: %s\n", ret, sss_strerror(ret));
++              "sysdb_initgroups() failed [%d]: %s\n", ret, sss_strerror(ret));
+         goto done;
+     }
+ 
+diff --git a/src/providers/ipa/ipa_hbac_services.c b/src/providers/ipa/ipa_hbac_services.c
+index 79088ff66..387e915cd 100644
+--- a/src/providers/ipa/ipa_hbac_services.c
++++ b/src/providers/ipa/ipa_hbac_services.c
+@@ -487,7 +487,7 @@ hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx,
+             /* Original DN matched a single service. Get the service name */
+             name = ldb_msg_find_attr_as_string(msgs[0], IPA_CN, NULL);
+             if (name == NULL) {
+-                DEBUG(SSSDBG_CRIT_FAILURE, "Attribute is missing!\n");
++                DEBUG(SSSDBG_CRIT_FAILURE, "Attribute IPA_CN is missing!\n");
+                 ret = EFAULT;
+                 goto done;
+             }
+@@ -523,7 +523,7 @@ hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx,
+                 /* Original DN matched a single group. Get the groupname */
+                 name = ldb_msg_find_attr_as_string(msgs[0], IPA_CN, NULL);
+                 if (name == NULL) {
+-                    DEBUG(SSSDBG_CRIT_FAILURE, "Attribute is missing!\n");
++                    DEBUG(SSSDBG_CRIT_FAILURE, "Attribute IPA_CN is missing!\n");
+                     ret = EFAULT;
+                     goto done;
+                 }
+diff --git a/src/providers/ipa/ipa_hbac_users.c b/src/providers/ipa/ipa_hbac_users.c
+index 2801a3162..25850eac0 100644
+--- a/src/providers/ipa/ipa_hbac_users.c
++++ b/src/providers/ipa/ipa_hbac_users.c
+@@ -124,7 +124,7 @@ get_ipa_groupname(TALLOC_CTX *mem_ctx,
+     if (strcasecmp("cn", account_comp_name) != 0) {
+         /* The third component name is not "cn" */
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Expected cn in second component, got %s\n", account_comp_name);
++              "Expected cn in third component, got %s\n", account_comp_name);
+         ret = ERR_UNEXPECTED_ENTRY_TYPE;
+         goto done;
+     }
+@@ -135,7 +135,7 @@ get_ipa_groupname(TALLOC_CTX *mem_ctx,
+                     account_comp_val->length) != 0) {
+         /* The third component value is not "accounts" */
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Expected cn accounts second component, got %s\n",
++              "Expected accounts third component, got %s\n",
+               (const char *) account_comp_val->data);
+         ret = ERR_UNEXPECTED_ENTRY_TYPE;
+         goto done;
+diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
+index 9253514a3..2cbe0c9c7 100644
+--- a/src/providers/ipa/ipa_id.c
++++ b/src/providers/ipa/ipa_id.c
+@@ -266,7 +266,7 @@ ipa_initgr_get_overrides_send(TALLOC_CTX *memctx,
+     }
+     state->groups_id_attr = talloc_strdup(state, groups_id_attr);
+     if (state->groups_id_attr == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+         ret = ENOMEM;
+         goto done;
+     }
+diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
+index a4d58e3bd..afdd6fdd0 100644
+--- a/src/providers/ipa/ipa_init.c
++++ b/src/providers/ipa/ipa_init.c
+@@ -317,10 +317,10 @@ static errno_t ipa_init_client_mode(struct be_ctx *be_ctx,
+     ret = sysdb_get_view_name(ipa_id_ctx, be_ctx->domain->sysdb,
+                               &ipa_id_ctx->view_name);
+     if (ret == ENOENT) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find view name in the cache. "
++        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find view name in the cache. "
+               "Will do online lookup later.\n");
+     } else if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name() failed [%d]: %s\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_get_view_name() failed [%d]: %s\n",
+               ret, sss_strerror(ret));
+         return ret;
+     }
+diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
+index c3e1acb48..fb93c6233 100644
+--- a/src/providers/ipa/ipa_s2n_exop.c
++++ b/src/providers/ipa/ipa_s2n_exop.c
+@@ -2224,7 +2224,8 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
+ 
+         break;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected request type.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unexpected request type %d.\n", state->request_type);
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
+index 5cb02de86..760349134 100644
+--- a/src/providers/ipa/ipa_selinux.c
++++ b/src/providers/ipa/ipa_selinux.c
+@@ -681,7 +681,7 @@ static errno_t selinux_fork_child(struct selinux_child_state *state)
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", errno, sss_strerror(errno));
++              "pipe (from) failed [%d][%s].\n", errno, sss_strerror(errno));
+         return ret;
+     }
+ 
+@@ -689,7 +689,7 @@ static errno_t selinux_fork_child(struct selinux_child_state *state)
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", errno, sss_strerror(errno));
++              "pipe (to) failed [%d][%s].\n", errno, sss_strerror(errno));
+         return ret;
+     }
+ 
+diff --git a/src/providers/ipa/ipa_session.c b/src/providers/ipa/ipa_session.c
+index 6672cb349..935393ccd 100644
+--- a/src/providers/ipa/ipa_session.c
++++ b/src/providers/ipa/ipa_session.c
+@@ -570,7 +570,7 @@ ipa_pam_session_handler_done(struct tevent_req *subreq)
+     talloc_free(subreq);
+ 
+     if (ret == ENOENT) {
+-        DEBUG(SSSDBG_IMPORTANT_INFO, "No Desktop Profile rules found\n");
++        DEBUG(SSSDBG_FUNC_DATA, "No Desktop Profile rules found\n");
+         if (!state->session_ctx->no_rules_found) {
+             state->session_ctx->no_rules_found = true;
+             state->session_ctx->last_request = time(NULL);
+@@ -668,7 +668,7 @@ ipa_pam_session_handler_get_deskprofile_user_info(TALLOC_CTX *mem_ctx,
+ 
+     if (res->count != 1) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "sysdb_getpwnam() got more users than expected. "
++              "sysdb_getpwnam() returned unexpected amount of users. "
+               "Expected [%d], got [%d]\n", 1, res->count);
+         ret = EINVAL;
+         goto done;
+diff --git a/src/providers/ipa/ipa_subdomains_ext_groups.c b/src/providers/ipa/ipa_subdomains_ext_groups.c
+index c730c3317..790ae9d16 100644
+--- a/src/providers/ipa/ipa_subdomains_ext_groups.c
++++ b/src/providers/ipa/ipa_subdomains_ext_groups.c
+@@ -840,7 +840,8 @@ static void ipa_add_ad_memberships_get_next(struct tevent_req *req)
+         }
+ 
+         if (missing_groups) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "There are unresolved external group "
++            /* this might be HBAC or sudo rule */
++            DEBUG(SSSDBG_FUNC_DATA, "There are unresolved external group "
+                                        "memberships even after all groups "
+                                        "have been looked up on the LDAP "
+                                        "server.\n");
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 36f32fae8..46d496258 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -506,7 +506,13 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx,
+             break;
+         default:
+             ret = EINVAL;
+-            DEBUG(SSSDBG_OP_FAILURE, "Invalid sub-domain request type.\n");
++            if (state->entry_type > BE_REQ__LAST) {
++                DEBUG(SSSDBG_OP_FAILURE, "Invalid sub-domain request type %d.\n",
++                      state->entry_type);
++            } else {
++                DEBUG(SSSDBG_TRACE_FUNC, "Unhandled sub-domain request type %d.\n",
++                      state->entry_type);
++            }
+     }
+     if (ret != EOK) goto fail;
+ 
+@@ -1027,6 +1033,9 @@ apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+     const char *homedir = NULL;
+     struct ldb_message_element *msg_el = NULL;
+     size_t c;
++    const char *category = NULL;
++    size_t length = 0;
++    bool user_class = true;
+ 
+     msg_el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+     if (msg_el == NULL) {
+@@ -1039,12 +1048,15 @@ apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+      * case of a MPG group lookup if SYSDB_OBJECTCATEGORY is SYSDB_GROUP_CLASS.
+      */
+     for (c = 0; c < msg_el->num_values; c++) {
+-        if (strncmp(SYSDB_USER_CLASS, (const char *)msg_el->values[c].data,
+-                    msg_el->values[c].length) == 0
+-                || (sss_domain_is_mpg(dom)
+-                    && strncmp(SYSDB_GROUP_CLASS,
+-                               (const char *)msg_el->values[c].data,
+-                               msg_el->values[c].length) == 0)) {
++        category = (const char *)msg_el->values[c].data;
++        length = msg_el->values[c].length;
++        if (strncmp(SYSDB_USER_CLASS, category, length) == 0) {
++            user_class = true;
++            break;
++        }
++        if (sss_domain_is_mpg(dom)
++               && strncmp(SYSDB_GROUP_CLASS, category, length) == 0) {
++            user_class = false;
+             break;
+         }
+     }
+@@ -1064,8 +1076,12 @@ apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ 
+     uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
+     if (uid == 0) {
+-        DEBUG(SSSDBG_OP_FAILURE, "UID for user [%s] is not known.\n",
+-                                  fqname);
++        if (user_class) {
++            DEBUG(SSSDBG_OP_FAILURE, "UID for user [%s] is unknown\n", fqname);
++        } else {
++            DEBUG(SSSDBG_TRACE_INTERNAL,
++                  "No UID for object [%s], perhaps mpg\n", fqname);
++        }
+         ret = ENOENT;
+         goto done;
+     }
+@@ -1309,7 +1325,7 @@ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
+ 
+         state->object_sid = talloc_strdup(state, sid);
+         if (state->object_sid == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+             ret = ENOMEM;
+             goto fail;
+         }
+@@ -1521,7 +1537,7 @@ static errno_t ipa_get_ad_apply_override_step(struct tevent_req *req)
+ 
+         state->ar->filter_value = talloc_strdup(state->ar, obj_name);
+         if (state->ar->filter_value == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+             return ENOMEM;
+         }
+         state->ar->filter_type = BE_FILTER_NAME;
+diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
+index fcdd05322..deb2c2cee 100644
+--- a/src/providers/ipa/ipa_subdomains_server.c
++++ b/src/providers/ipa/ipa_subdomains_server.c
+@@ -513,7 +513,7 @@ static void ipa_getkeytab_exec(const char *ccache,
+ 
+     gkt_env[0] = talloc_asprintf(NULL, "KRB5CCNAME=%s", ccache);
+     if (gkt_env[0] == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to format KRB5CCNAME\n");
++        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to format KRB5CCNAME\n");
+         exit(1);
+     }
+ 
+@@ -522,7 +522,7 @@ static void ipa_getkeytab_exec(const char *ccache,
+     ret = unlink(keytab_path);
+     if (ret == -1) {
+         ret = errno;
+-        DEBUG(SSSDBG_CRIT_FAILURE,
++        DEBUG(SSSDBG_FATAL_FAILURE,
+               "Failed to unlink the temporary ccname [%d][%s]\n",
+               ret, sss_strerror(ret));
+         exit(1);
+@@ -533,12 +533,12 @@ static void ipa_getkeytab_exec(const char *ccache,
+                  "-r", "-s", server, "-p", principal, "-k", keytab_path, NULL,
+                  gkt_env);
+ 
+-    DEBUG(SSSDBG_CRIT_FAILURE,
++    DEBUG(SSSDBG_FATAL_FAILURE,
+           "execle returned %d, this shouldn't happen!\n", ret);
+ 
+     /* The child should never end up here */
+     ret = errno;
+-    DEBUG(SSSDBG_CRIT_FAILURE,
++    DEBUG(SSSDBG_FATAL_FAILURE,
+           "execle failed [%d][%s].\n", ret, sss_strerror(ret));
+     exit(1);
+ }
+@@ -748,7 +748,8 @@ static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req)
+ 
+     state->new_keytab = talloc_asprintf(state, "%sXXXXXX", state->keytab);
+     if (state->new_keytab == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Cannot set up ipa_get_keytab. talloc_asprintf() failed\n");
+         return ENOMEM;
+     }
+ 
+diff --git a/src/providers/ipa/ipa_sudo.c b/src/providers/ipa/ipa_sudo.c
+index 931770922..1b881d085 100644
+--- a/src/providers/ipa/ipa_sudo.c
++++ b/src/providers/ipa/ipa_sudo.c
+@@ -223,7 +223,7 @@ ipa_sudo_init_ipa_schema(TALLOC_CTX *mem_ctx,
+                        ipa_sudorule_map, IPA_OPTS_SUDORULE,
+                        &sudo_ctx->sudorule_map);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse attribute map "
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse attribute map (rule) "
+               "[%d]: %s\n", ret, sss_strerror(ret));
+         goto done;
+     }
+@@ -232,7 +232,7 @@ ipa_sudo_init_ipa_schema(TALLOC_CTX *mem_ctx,
+                        ipa_sudocmdgroup_map, IPA_OPTS_SUDOCMDGROUP,
+                        &sudo_ctx->sudocmdgroup_map);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse attribute map "
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse attribute map (cmdgroup) "
+               "[%d]: %s\n", ret, sss_strerror(ret));
+         goto done;
+     }
+@@ -241,7 +241,7 @@ ipa_sudo_init_ipa_schema(TALLOC_CTX *mem_ctx,
+                        ipa_sudocmd_map, IPA_OPTS_SUDOCMD,
+                        &sudo_ctx->sudocmd_map);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse attribute map "
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse attribute map (cmd) "
+               "[%d]: %s\n", ret, sss_strerror(ret));
+         goto done;
+     }
+@@ -250,16 +250,16 @@ ipa_sudo_init_ipa_schema(TALLOC_CTX *mem_ctx,
+                          CONFDB_SUDO_THRESHOLD, CONFDB_DEFAULT_SUDO_THRESHOLD,
+                          &sudo_ctx->sudocmd_threshold);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Could not parse sudo search base\n");
+-        return ret;
++        DEBUG(SSSDBG_CRIT_FAILURE, "Could not get sudo threshold\n");
++        goto done;
+     }
+ 
+     ret = sdap_parse_search_base(sudo_ctx, sudo_ctx->sdap_opts->basic,
+                                  SDAP_SUDO_SEARCH_BASE,
+                                  &sudo_ctx->sudo_sb);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Could not parse sudo search base\n");
+-        return ret;
++        DEBUG(SSSDBG_CRIT_FAILURE, "Could not parse sudo search base\n");
++        goto done;
+     }
+ 
+     ret = ipa_sudo_ptask_setup(be_ctx, sudo_ctx);
+diff --git a/src/providers/ipa/ipa_sudo_async.c b/src/providers/ipa/ipa_sudo_async.c
+index 1d7a69814..c531ecbf9 100644
+--- a/src/providers/ipa/ipa_sudo_async.c
++++ b/src/providers/ipa/ipa_sudo_async.c
+@@ -520,7 +520,7 @@ ipa_sudo_fetch_addtl_cmdgroups_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
+-    DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu additional command groups\n",
++    DEBUG(SSSDBG_FUNC_DATA, "Received %zu additional command groups\n",
+           num_attrs);
+ 
+     ret = ipa_sudo_filter_rules_bycmdgroups(state, state->domain, attrs,
+@@ -609,7 +609,7 @@ ipa_sudo_fetch_rules_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
+-    DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo rules\n", num_attrs);
++    DEBUG(SSSDBG_FUNC_DATA, "Received %zu sudo rules\n", num_attrs);
+ 
+     ret = ipa_sudo_conv_rules(state->conv, attrs, num_attrs);
+     if (ret != EOK) {
+@@ -689,7 +689,7 @@ ipa_sudo_fetch_cmdgroups_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
+-    DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo command groups\n",
++    DEBUG(SSSDBG_FUNC_DATA, "Received %zu sudo command groups\n",
+           num_attrs);
+ 
+     ret = ipa_sudo_conv_cmdgroups(state->conv, attrs, num_attrs);
+@@ -769,7 +769,7 @@ ipa_sudo_fetch_cmds_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
+-    DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo commands\n", num_attrs);
++    DEBUG(SSSDBG_FUNC_DATA, "Received %zu sudo commands\n", num_attrs);
+ 
+     ret = ipa_sudo_conv_cmds(state->conv, attrs, num_attrs);
+     if (ret != EOK) {
+@@ -1109,7 +1109,7 @@ done:
+     if (in_transaction) {
+         sret = sysdb_transaction_cancel(state->sysdb);
+         if (sret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+         }
+     }
+ 
+diff --git a/src/providers/ipa/ipa_sudo_conversion.c b/src/providers/ipa/ipa_sudo_conversion.c
+index b5fc49379..bd1ec72b3 100644
+--- a/src/providers/ipa/ipa_sudo_conversion.c
++++ b/src/providers/ipa/ipa_sudo_conversion.c
+@@ -801,7 +801,7 @@ convert_host(TALLOC_CTX *mem_ctx,
+         *skip_entry = true;
+         return NULL;
+     } else if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n",
+               value, ret, sss_strerror(ret));
+         return NULL;
+     }
+@@ -841,7 +841,7 @@ convert_user(TALLOC_CTX *mem_ctx,
+         *skip_entry = true;
+         return NULL;
+     } else if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n",
+               value, ret, sss_strerror(ret));
+         return NULL;
+     }
+@@ -904,7 +904,7 @@ convert_group(TALLOC_CTX *mem_ctx,
+         *skip_entry = true;
+         return NULL;
+     } else if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n",
+               value, ret, sss_strerror(ret));
+         return NULL;
+     }
+diff --git a/src/providers/ipa/ipa_views.c b/src/providers/ipa/ipa_views.c
+index 2a918bdc8..e1090d03b 100644
+--- a/src/providers/ipa/ipa_views.c
++++ b/src/providers/ipa/ipa_views.c
+@@ -232,7 +232,7 @@ static errno_t get_dp_id_data_for_xyz(TALLOC_CTX *mem_ctx, const char *val,
+     ar->filter_value = talloc_strdup(ar, val);
+     ar->domain = talloc_strdup(ar, domain_name);
+     if (ar->filter_value == NULL || ar->domain == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+         talloc_free(ar);
+         return ENOMEM;
+     }
+@@ -471,7 +471,7 @@ static void ipa_get_ad_override_done(struct tevent_req *subreq)
+ 
+     ret = ipa_get_ad_override_qualify_name(state);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Cannot qualify object name\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot qualify object name\n");
+         goto fail;
+     }
+ 
+diff --git a/src/providers/krb5/krb5_access.c b/src/providers/krb5/krb5_access.c
+index be9068c0f..2ae5abe14 100644
+--- a/src/providers/krb5/krb5_access.c
++++ b/src/providers/krb5/krb5_access.c
+@@ -78,7 +78,8 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
+     }
+ 
+     if (pd->cmd != SSS_PAM_ACCT_MGMT) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected pam task.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unexpected pam task %d.\n", pd->cmd);
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
+index a1c0b3640..699c2467b 100644
+--- a/src/providers/krb5/krb5_auth.c
++++ b/src/providers/krb5/krb5_auth.c
+@@ -499,7 +499,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
+                 /* handle empty password gracefully */
+                 if (authtok_type == SSS_AUTHTOK_TYPE_EMPTY) {
+                     DEBUG(SSSDBG_CRIT_FAILURE,
+-                          "Illegal zero-length authtok for user [%s]\n",
++                          "Illegal empty authtok for user [%s]\n",
+                            pd->user);
+                     state->pam_status = PAM_AUTH_ERR;
+                     state->dp_err = DP_ERR_OK;
+@@ -854,7 +854,7 @@ static void krb5_auth_done(struct tevent_req *subreq)
+             ret = EOK;
+             goto done;
+         default:
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM task\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM task %d\n", pd->cmd);
+             ret = EINVAL;
+             goto done;
+         }
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index cab7b27a2..06fdf7156 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -258,7 +258,7 @@ static void sss_krb5_expire_callback_func(krb5_context context, void *data,
+ 
+     blob = talloc_array(kr->pd, uint32_t, 2);
+     if (blob == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n");
+         return;
+     }
+ 
+@@ -525,7 +525,8 @@ static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx,
+                                      out_token, out_pin);
+         break;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unsupported authtok type %d\n", sss_authtok_get_type(auth_tok));
+     }
+ 
+     return EINVAL;
+@@ -1087,7 +1088,7 @@ static errno_t pack_response_packet(TALLOC_CTX *mem_ctx, errno_t error,
+ 
+     buf = talloc_array(mem_ctx, uint8_t, size);
+     if (!buf) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Insufficient memory to create message.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed\n");
+         return ENOMEM;
+     }
+ 
+@@ -1958,13 +1959,12 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+                                           &msg_len, &msg);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "pack_user_info_chpass_error failed.\n");
++                  "pack_user_info_chpass_error failed [%d]\n", ret);
+         } else {
+             ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, msg_len,
+                                    msg);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "pam_add_response failed.\n");
++                DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+             }
+         }
+         return kerr;
+@@ -2036,13 +2036,12 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+                                               &user_resp_len, &user_resp);
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "pack_user_info_chpass_error failed.\n");
++                      "pack_user_info_chpass_error failed [%d]\n", ret);
+             } else {
+                 ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, user_resp_len,
+                                        user_resp);
+                 if (ret != EOK) {
+-                    DEBUG(SSSDBG_CRIT_FAILURE,
+-                          "pam_add_response failed.\n");
++                    DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+                 }
+             }
+         }
+@@ -2448,7 +2447,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
+ 
+     pd = create_pam_data(kr);
+     if (pd == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "create_pam_data failed.\n");
+         return ENOMEM;
+     }
+     kr->pd = pd;
+@@ -3110,7 +3109,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+ 
+     kr->creds = calloc(1, sizeof(krb5_creds));
+     if (kr->creds == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "calloc failed.\n");
+         return ENOMEM;
+     }
+ 
+@@ -3345,7 +3344,7 @@ int main(int argc, const char *argv[])
+ 
+     kr = talloc_zero(NULL, struct krb5_req);
+     if (kr == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -3403,7 +3402,7 @@ int main(int argc, const char *argv[])
+ 
+     ret = k5c_setup(kr, offline);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "krb5_child_setup failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "k5c_setup failed.\n");
+         goto done;
+     }
+ 
+diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
+index 37f4304e8..01777e22b 100644
+--- a/src/providers/krb5/krb5_child_handler.c
++++ b/src/providers/krb5/krb5_child_handler.c
+@@ -449,14 +449,14 @@ static errno_t fork_child(struct tevent_req *req)
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", errno, strerror(errno));
++              "pipe (from) failed [%d][%s].\n", errno, strerror(errno));
+         goto fail;
+     }
+     ret = pipe(pipefd_to_child);
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", errno, strerror(errno));
++              "pipe (to) failed [%d][%s].\n", errno, strerror(errno));
+         goto fail;
+     }
+ 
+diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
+index 5c11c347b..316603946 100644
+--- a/src/providers/krb5/krb5_common.c
++++ b/src/providers/krb5/krb5_common.c
+@@ -793,7 +793,7 @@ static void krb5_resolve_callback(void *private_data, struct fo_server *server)
+ 
+     krb5_service = talloc_get_type(private_data, struct krb5_service);
+     if (!krb5_service) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Bad private_data\n");
+         return;
+     }
+ 
+@@ -1110,7 +1110,7 @@ void remove_krb5_info_files_callback(void *pvt)
+                                               ctx->kdc_service_name);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "be_fo_run_callbacks_at_next_request failed, "
++              "be_fo_run_callbacks_at_next_request(kdc_service_name) failed, "
+                   "krb5 info files will not be removed, because "
+                   "it is unclear if they will be recreated properly.\n");
+         return;
+@@ -1120,7 +1120,7 @@ void remove_krb5_info_files_callback(void *pvt)
+                                             ctx->kpasswd_service_name);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "be_fo_run_callbacks_at_next_request failed, "
++                  "be_fo_run_callbacks_at_next_request(kpasswd_service_name) failed, "
+                       "krb5 info files will not be removed, because "
+                       "it is unclear if they will be recreated properly.\n");
+             return;
+diff --git a/src/providers/krb5/krb5_delayed_online_authentication.c b/src/providers/krb5/krb5_delayed_online_authentication.c
+index 8572d1249..07d375b9d 100644
+--- a/src/providers/krb5/krb5_delayed_online_authentication.c
++++ b/src/providers/krb5/krb5_delayed_online_authentication.c
+@@ -173,7 +173,7 @@ static errno_t authenticate_stored_users(
+         ret = hash_lookup(uid_table, &key, &value);
+ 
+         if (ret == HASH_SUCCESS) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "User [%s] is still logged in, "
++            DEBUG(SSSDBG_FUNC_DATA, "User [%s] is still logged in, "
+                       "trying online authentication.\n", pd->user);
+ 
+             auth_data = talloc_zero(deferred_auth_ctx->be_ctx,
+@@ -193,7 +193,7 @@ static errno_t authenticate_stored_users(
+                 }
+             }
+         } else {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "User [%s] is not logged in anymore, "
++            DEBUG(SSSDBG_FUNC_DATA, "User [%s] is not logged in anymore, "
+                       "discarding online authentication.\n", pd->user);
+             talloc_free(pd);
+         }
+diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
+index 8b2159e92..d79e7c367 100644
+--- a/src/providers/krb5/krb5_renew_tgt.c
++++ b/src/providers/krb5/krb5_renew_tgt.c
+@@ -405,7 +405,7 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
+ 
+     base_dn = sysdb_user_base_dn(tmp_ctx, renew_tgt_ctx->be_ctx->domain);
+     if (base_dn == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_base_dn failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_base_dn failed.\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -440,7 +440,7 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
+ 
+         ret = sss_parse_internal_fqname(tmp_ctx, user_name, NULL, &user_dom);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE,
++            DEBUG(SSSDBG_CRIT_FAILURE,
+                   "Cannot parse internal fqname [%d]: %s\n",
+                   ret, sss_strerror(ret));
+             goto done;
+diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
+index e3f8f2140..43056ba28 100644
+--- a/src/providers/krb5/krb5_utils.c
++++ b/src/providers/krb5/krb5_utils.c
+@@ -287,7 +287,7 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
+                 name = sss_output_name(tmp_ctx, kr->pd->user, case_sensitive, 0);
+                 if (name == NULL) {
+                     DEBUG(SSSDBG_CRIT_FAILURE,
+-                          "sss_get_cased_name failed\n");
++                          "sss_output_name failed\n");
+                     goto done;
+                 }
+ 
+diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
+index 89ff4ece0..42ef962b4 100644
+--- a/src/providers/ldap/ldap_auth.c
++++ b/src/providers/ldap/ldap_auth.c
+@@ -64,7 +64,7 @@ static errno_t add_expired_warning(struct pam_data *pd, long exp_time)
+ 
+     data = talloc_array(pd, uint32_t, 2);
+     if (data == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n");
+         return ENOMEM;
+     }
+ 
+@@ -249,7 +249,8 @@ errno_t check_pwexpire_policy(enum pwexpire pw_expire_type,
+         ret = EOK;
+         break;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown password expiration type.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unknown password expiration type %d.\n", pw_expire_type);
+         ret = EINVAL;
+     }
+ 
+@@ -1355,9 +1356,10 @@ static void sdap_pam_chpass_handler_auth_done(struct tevent_req *subreq)
+         case PWEXPIRE_NONE:
+             break;
+         default:
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Unknown password expiration type.\n");
+-                state->pd->pam_status = PAM_SYSTEM_ERR;
+-                goto done;
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Unknown password expiration type %d.\n", pw_expire_type);
++            state->pd->pam_status = PAM_SYSTEM_ERR;
++            goto done;
+         }
+     }
+ 
+diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
+index 84941c6e4..8580e2785 100644
+--- a/src/providers/ldap/ldap_child.c
++++ b/src/providers/ldap/ldap_child.c
+@@ -223,7 +223,7 @@ static int lc_verify_keytab_ex(const char *principal,
+             /* This should never happen. The API docs for this function
+              * specify only success for this function
+              */
+-            DEBUG(SSSDBG_CRIT_FAILURE,"Could not free keytab entry contents\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Could not free keytab entry contents\n");
+             /* This is non-fatal, so we'll continue here */
+         }
+ 
+diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
+index cd589a7c0..2ad8680a1 100644
+--- a/src/providers/ldap/ldap_init.c
++++ b/src/providers/ldap/ldap_init.c
+@@ -43,8 +43,8 @@ struct ldap_init_ctx {
+ };
+ 
+ /* Please use this only for short lists */
+-errno_t check_order_list_for_duplicates(char **list,
+-                                        bool case_sensitive)
++static errno_t check_order_list_for_duplicates(char **list,
++                                               bool case_sensitive)
+ {
+     size_t c;
+     size_t d;
+diff --git a/src/providers/ldap/ldap_options.c b/src/providers/ldap/ldap_options.c
+index d06d3980e..bb51785fb 100644
+--- a/src/providers/ldap/ldap_options.c
++++ b/src/providers/ldap/ldap_options.c
+@@ -408,14 +408,15 @@ int ldap_get_options(TALLOC_CTX *memctx,
+         sss_erase_talloc_mem_securely(cleartext);
+         talloc_free(cleartext);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_blob(authtok) failed.\n");
+             goto done;
+         }
+ 
+         ret = dp_opt_set_string(opts->basic, SDAP_DEFAULT_AUTHTOK_TYPE,
+                                 "password");
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "dp_opt_set_string(authtok_type) failed.\n");
+             goto done;
+         }
+     }
+@@ -629,7 +630,8 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
+             default_entry_map = rfc2307bis_autofs_entry_map;
+             break;
+         default:
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Unknown LDAP schema!\n");
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Unknown LDAP schema %d!\n", opts->schema_type);
+             return EINVAL;
+     }
+ 
+diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
+index 7cb00480d..32c0144b9 100644
+--- a/src/providers/ldap/sdap.c
++++ b/src/providers/ldap/sdap.c
+@@ -371,7 +371,7 @@ int sdap_get_map(TALLOC_CTX *memctx,
+ 
+         if (map[i].def_name && !map[i].name) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "Failed to retrieve value for %s\n", map[i].opt_name);
++                  "Failed to process value for %s\n", map[i].opt_name);
+             talloc_zfree(map);
+             return EINVAL;
+         }
+@@ -532,7 +532,8 @@ int sdap_parse_entry(TALLOC_CTX *memctx,
+             if (!vals) {
+                 ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+                 if (lerrno != LDAP_SUCCESS) {
+-                    DEBUG(SSSDBG_CRIT_FAILURE, "LDAP Library error: %d(%s)\n",
++                    DEBUG(SSSDBG_CRIT_FAILURE,
++                          "ldap_get_values_len() failed: %d(%s)\n",
+                           lerrno, sss_ldap_err2string(lerrno));
+                     ret = EIO;
+                     goto done;
+@@ -613,7 +614,7 @@ int sdap_parse_entry(TALLOC_CTX *memctx,
+ 
+     ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+     if (lerrno) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "LDAP Library error: %d(%s)\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "ldap_get_option() failed: %d(%s)\n",
+               lerrno, sss_ldap_err2string(lerrno));
+         ret = EIO;
+         goto done;
+@@ -884,7 +885,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+             ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_HARD;
+         }
+         else {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Unknown value for tls_reqcert.\n");
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Unknown value for tls_reqcert '%s'.\n", tls_opt);
+             return EINVAL;
+         }
+         /* LDAP_OPT_X_TLS_REQUIRE_CERT has to be set as a global option,
+@@ -893,7 +895,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+                               &ldap_opt_x_tls_require_cert);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_set_option failed: %s\n", sss_ldap_err2string(ret));
++                  "ldap_set_option(req_cert) failed: %s\n",
++                  sss_ldap_err2string(ret));
+             return EIO;
+         }
+     }
+@@ -903,7 +906,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+         ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, tls_opt);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_set_option failed: %s\n", sss_ldap_err2string(ret));
++                  "ldap_set_option(cacertfile) failed: %s\n",
++                  sss_ldap_err2string(ret));
+             return EIO;
+         }
+     }
+@@ -913,7 +917,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+         ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, tls_opt);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_set_option failed: %s\n", sss_ldap_err2string(ret));
++                  "ldap_set_option(cacertdir) failed: %s\n",
++                  sss_ldap_err2string(ret));
+             return EIO;
+         }
+     }
+@@ -923,7 +928,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+         ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, tls_opt);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_set_option failed: %s\n", sss_ldap_err2string(ret));
++                  "ldap_set_option(certfile) failed: %s\n",
++                  sss_ldap_err2string(ret));
+             return EIO;
+         }
+     }
+@@ -933,7 +939,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+         ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, tls_opt);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_set_option failed: %s\n", sss_ldap_err2string(ret));
++                  "ldap_set_option(keyfile) failed: %s\n",
++                  sss_ldap_err2string(ret));
+             return EIO;
+         }
+     }
+@@ -943,7 +950,8 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
+         ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, tls_opt);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_set_option failed: %s\n", sss_ldap_err2string(ret));
++                  "ldap_set_option(cipher) failed: %s\n",
++                  sss_ldap_err2string(ret));
+             return EIO;
+         }
+     }
+diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
+index dd04ec512..8add97ba8 100644
+--- a/src/providers/ldap/sdap_access.c
++++ b/src/providers/ldap/sdap_access.c
+@@ -317,7 +317,8 @@ static errno_t sdap_access_check_next_rule(struct sdap_access_req_ctx *state,
+ 
+         default:
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "Unexpected access rule type. Access denied.\n");
++                  "Unexpected access rule type %d. Access denied.\n",
++                  state->access_ctx->access_rule[state->current_rule]);
+             ret = ERR_ACCESS_DENIED;
+         }
+ 
+@@ -1220,13 +1221,13 @@ static errno_t sdap_save_user_cache_bool(struct sss_domain_info *domain,
+     attrs = sysdb_new_attrs(NULL);
+     if (attrs == NULL) {
+         ret = ENOMEM;
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Could not create attrs\n");
+         goto done;
+     }
+ 
+     ret = sysdb_attrs_add_bool(attrs, attr_name, value);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attrs\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up attr value\n");
+         goto done;
+     }
+ 
+@@ -1787,7 +1788,7 @@ errno_t sdap_access_ppolicy_step(struct tevent_req *req)
+                                    false);
+ 
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_access_ppolicy_send failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_get_generic_send failed.\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -1913,7 +1914,7 @@ static void sdap_access_ppolicy_step_done(struct tevent_req *subreq)
+             ret = sdap_access_decide_offline(state->cached_access);
+         } else {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "sdap_get_generic_send() returned error [%d][%s]\n",
++                  "sdap_id_op_done() returned error [%d][%s]\n",
+                   ret, sss_strerror(ret));
+         }
+ 
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 68d5d44f8..cc77fb249 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -749,7 +749,7 @@ sdap_modify_send(TALLOC_CTX *mem_ctx,
+ 
+     ret = ldap_modify_ext(state->sh->ldap, dn, mods, NULL, NULL, &msgid);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to send operation!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "ldap_modify_ext() failed [%d]\n", ret);
+         goto done;
+     }
+ 
+@@ -2120,7 +2120,7 @@ static int sdap_x_deref_create_control(struct sdap_handle *sh,
+ 
+     ret = ldap_create_deref_control_value(sh->ldap, ds, &derefval);
+     if (ret != LDAP_SUCCESS) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldap_control_create failed: %s\n",
++        DEBUG(SSSDBG_CRIT_FAILURE, "ldap_create_deref_control_value failed: %s\n",
+                   ldap_err2string(ret));
+         return ret;
+     }
+@@ -2129,7 +2129,7 @@ static int sdap_x_deref_create_control(struct sdap_handle *sh,
+                               1, &derefval, 1, ctrl);
+     ldap_memfree(derefval.bv_val);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldap_control_create failed\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_control_create failed %d\n", ret);
+         return ret;
+     }
+ 
+@@ -2875,7 +2875,8 @@ static void sdap_deref_search_done(struct tevent_req *subreq)
+                 &state->reply_count, &state->reply);
+         break;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown deref method\n");
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unknown deref method %d\n", state->deref_type);
+         tevent_req_error(req, EINVAL);
+         return;
+     }
+diff --git a/src/providers/ldap/sdap_async_autofs.c b/src/providers/ldap/sdap_async_autofs.c
+index eaca0324e..ae2fa33e1 100644
+--- a/src/providers/ldap/sdap_async_autofs.c
++++ b/src/providers/ldap/sdap_async_autofs.c
+@@ -720,7 +720,7 @@ sdap_autofs_setautomntent_send(TALLOC_CTX *memctx,
+                                       dp_opt_get_int(state->opts->basic,
+                                                      SDAP_SEARCH_TIMEOUT));
+     if (!subreq) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_get_automntmap_send failed\n");
+         ret = ENOMEM;
+         goto fail;
+     }
+diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
+index 5f69cedcc..eead3f119 100644
+--- a/src/providers/ldap/sdap_async_connection.c
++++ b/src/providers/ldap/sdap_async_connection.c
+@@ -694,10 +694,10 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
+                               LDAP_OPT_RESULT_CODE, &ldap_err);
+         if (ret != LDAP_OPT_SUCCESS) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "ldap_bind failed (couldn't get ldap error)\n");
++                  "ldap_sasl_bind failed (couldn't get ldap error)\n");
+             ret = LDAP_LOCAL_ERROR;
+         } else {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "ldap_bind failed (%d)[%s]\n",
++            DEBUG(SSSDBG_CRIT_FAILURE, "ldap_sasl_bind failed (%d)[%s]\n",
+                       ldap_err, sss_ldap_err2string(ldap_err));
+             ret = ldap_err;
+         }
+@@ -988,7 +988,7 @@ static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx,
+                                        (*sdap_sasl_interact), state);
+     if (ret != LDAP_SUCCESS) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "ldap_sasl_bind failed (%d)[%s]\n",
++              "ldap_sasl_interactive_bind_s failed (%d)[%s]\n",
+                ret, sss_ldap_err2string(ret));
+ 
+         optret = sss_ldap_get_diagnostic_msg(state, state->sh->ldap,
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 5dbfd73c4..16c4a5f37 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -883,10 +883,7 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
+     const char *check_name;
+ 
+     if (dom->ignore_group_members) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Group members are ignored, nothing to do. If you see this " \
+-              "message it might indicate an error in the group processing " \
+-              "logic.\n");
++        DEBUG(SSSDBG_TRACE_FUNC, "Group members are ignored, nothing to do.\n");
+         return EOK;
+     }
+ 
+@@ -978,7 +975,12 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
+         ret = sysdb_remove_attrs(group_dom, group_name, SYSDB_MEMBER_GROUP,
+                                  discard_const(remove_attrs));
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, "sysdb_remove_attrs failed.\n");
++            if (ret != ENOENT) {
++                DEBUG(SSSDBG_OP_FAILURE, "sysdb_remove_attrs failed.\n");
++            } else {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      "sysdb_remove_attrs failed for missing entry\n");
++            }
+             goto fail;
+         }
+     } else {
+@@ -1014,7 +1016,7 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
+     return EOK;
+ 
+ fail:
+-    DEBUG(SSSDBG_OP_FAILURE,
++    DEBUG(SSSDBG_MINOR_FAILURE,
+            "Failed to save members of group %s\n", group_name);
+     return ret;
+ }
+@@ -1130,8 +1132,13 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
+             /* Do not fail completely on errors.
+              * Just report the failure to save and go on */
+             if (ret) {
+-                DEBUG(SSSDBG_OP_FAILURE,
+-                      "Failed to store group %d members.\n", i);
++                if (ret != ENOENT) {
++                    DEBUG(SSSDBG_OP_FAILURE,
++                          "Failed to store group %d members: %d\n", i, ret);
++                } else {
++                    DEBUG(SSSDBG_FUNC_DATA,
++                          "Can't save members of missing group %d\n", i);
++                }
+             } else {
+                 DEBUG(SSSDBG_TRACE_ALL, "Group %d members processed!\n", i);
+             }
+@@ -1270,7 +1277,7 @@ sdap_process_group_send(TALLOC_CTX *memctx,
+ 
+     /* Group without members */
+     if (el->num_values == 0) {
+-        DEBUG(SSSDBG_OP_FAILURE, "No Members. Done!\n");
++        DEBUG(SSSDBG_FUNC_DATA, "No Members. Done!\n");
+         ret = EOK;
+         goto done;
+     }
+@@ -2249,7 +2256,7 @@ static void sdap_nested_done(struct tevent_req *subreq)
+ 
+     if (hash_count(state->missing_external) == 0) {
+         /* No external members. Processing complete */
+-        DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
++        DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done\n");
+         tevent_req_done(req);
+         return;
+     }
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index 4b5b36403..bf8f9482b 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -345,7 +345,7 @@ int sdap_initgr_common_store(struct sysdb_ctx *sysdb,
+                                          add_groups, ldap_groups,
+                                          ldap_groups_count);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Adding incomplete users failed\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Adding incomplete groups failed\n");
+             goto done;
+         }
+     }
+@@ -1043,6 +1043,10 @@ static void sdap_initgr_nested_search(struct tevent_req *subreq)
+         state->groups[state->groups_cur] = talloc_steal(state->groups,
+                                                         groups[0]);
+         state->groups_cur++;
++    } else if (count == 0) {
++        /* this might be HBAC or sudo rule */
++        DEBUG(SSSDBG_FUNC_DATA, "Object %s not found. Skipping\n",
++              state->group_dns[state->cur]);
+     } else {
+         DEBUG(SSSDBG_OP_FAILURE,
+               "Search for group %s, returned %zu results. Skipping\n",
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index eb3e779ed..80ac4c1f4 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -378,7 +378,7 @@ static void sdap_ad_resolve_sids_done(struct tevent_req *subreq)
+         /* Group was not found, we will ignore the error and continue with
+          * next group. This may happen for example if the group is built-in,
+          * but a custom search base is provided. */
+-        DEBUG(SSSDBG_CRIT_FAILURE,
++        DEBUG(SSSDBG_MINOR_FAILURE,
+               "Unable to resolve SID %s - will try next sid.\n",
+               state->current_sid);
+     } else if (ret != EOK || sdap_error != EOK || dp_error != DP_ERR_OK) {
+diff --git a/src/providers/ldap/sdap_async_sudo.c b/src/providers/ldap/sdap_async_sudo.c
+index 5473e1df8..28b65b639 100644
+--- a/src/providers/ldap/sdap_async_sudo.c
++++ b/src/providers/ldap/sdap_async_sudo.c
+@@ -111,7 +111,7 @@ static void sdap_sudo_load_sudoers_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo rules\n",
++    DEBUG(SSSDBG_FUNC_DATA, "Received %zu sudo rules\n",
+           state->num_rules);
+ 
+     tevent_req_done(req);
+@@ -665,7 +665,7 @@ done:
+     if (in_transaction) {
+         sret = sysdb_transaction_cancel(state->sysdb);
+         if (sret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+         }
+     }
+ 
+diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
+index 9d25aea8b..480efc41b 100644
+--- a/src/providers/ldap/sdap_child_helpers.c
++++ b/src/providers/ldap/sdap_child_helpers.c
+@@ -95,14 +95,14 @@ static errno_t sdap_fork_child(struct tevent_context *ev,
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", ret, strerror(ret));
++              "pipe(from) failed [%d][%s].\n", ret, strerror(ret));
+         goto fail;
+     }
+     ret = pipe(pipefd_to_child);
+     if (ret == -1) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pipe failed [%d][%s].\n", ret, strerror(ret));
++              "pipe(to) failed [%d][%s].\n", ret, strerror(ret));
+         goto fail;
+     }
+ 
+@@ -332,7 +332,7 @@ struct tevent_req *sdap_get_tgt_send(TALLOC_CTX *mem_ctx,
+ 
+     ret = set_tgt_child_timeout(req, ev, timeout);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "activate_child_timeout_handler failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "set_tgt_child_timeout failed.\n");
+         goto fail;
+     }
+ 
+diff --git a/src/providers/ldap/sdap_hostid.c b/src/providers/ldap/sdap_hostid.c
+index d90a83854..ae8caaddb 100644
+--- a/src/providers/ldap/sdap_hostid.c
++++ b/src/providers/ldap/sdap_hostid.c
+@@ -166,7 +166,7 @@ hosts_get_done(struct tevent_req *subreq)
+     }
+ 
+     if (state->count == 0) {
+-        DEBUG(SSSDBG_OP_FAILURE,
++        DEBUG(SSSDBG_FUNC_DATA,
+               "No host with name [%s] found.\n", state->name);
+ 
+         ret = sysdb_delete_ssh_host(state->domain, state->name);
+diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c
+index 6c803f31d..b8d76f8a5 100644
+--- a/src/providers/ldap/sdap_id_op.c
++++ b/src/providers/ldap/sdap_id_op.c
+@@ -563,7 +563,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
+                    "is enabled.\n");
+         } else {
+             /* be is going offline as there is no more servers to try */
+-            DEBUG(SSSDBG_CRIT_FAILURE,
++            DEBUG(SSSDBG_OP_FAILURE,
+                   "Failed to connect, going offline (%d [%s])\n",
+                    ret, strerror(ret));
+             is_offline = true;
+diff --git a/src/providers/proxy/proxy_auth.c b/src/providers/proxy/proxy_auth.c
+index 926ce98f4..0e6fc8ea8 100644
+--- a/src/providers/proxy/proxy_auth.c
++++ b/src/providers/proxy/proxy_auth.c
+@@ -68,7 +68,7 @@ static struct tevent_req *proxy_child_send(TALLOC_CTX *mem_ctx,
+ 
+     req = tevent_req_create(mem_ctx, &state, struct proxy_child_ctx);
+     if (req == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not send PAM request to child\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+         return NULL;
+     }
+ 
+@@ -391,7 +391,7 @@ static void proxy_child_init_done(struct tevent_req *subreq) {
+      */
+     sig_ctx = talloc_zero(child_ctx->auth_ctx, struct proxy_child_sig_ctx);
+     if(sig_ctx == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+         tevent_req_error(req, ENOMEM);
+         return;
+     }
+@@ -753,7 +753,7 @@ proxy_pam_handler_send(TALLOC_CTX *mem_ctx,
+         pd->pam_status = PAM_SUCCESS;
+         goto immediately;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported PAM task.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported PAM task %d\n", pd->cmd);
+         pd->pam_status = PAM_MODULE_UNKNOWN;
+         goto immediately;
+     }
+diff --git a/src/providers/proxy/proxy_child.c b/src/providers/proxy/proxy_child.c
+index dc06f4669..bb96ec0f4 100644
+--- a/src/providers/proxy/proxy_child.c
++++ b/src/providers/proxy/proxy_child.c
+@@ -270,7 +270,7 @@ static errno_t call_pam_stack(const char *pam_target, struct pam_data *pd)
+                 }
+                 break;
+             default:
+-                DEBUG(SSSDBG_CRIT_FAILURE, "unknown PAM call\n");
++                DEBUG(SSSDBG_CRIT_FAILURE, "unknown PAM call %d\n", pd->cmd);
+                 pam_status=PAM_ABORT;
+         }
+ 
+@@ -383,13 +383,13 @@ proxy_cli_init(struct pc_ctx *ctx)
+     ret = sss_iface_connect_address(ctx, ctx->ev, sbus_cliname, sbus_address,
+                                     NULL, &ctx->conn);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to %s\n", sbus_address);
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to connect to %s\n", sbus_address);
+         goto done;
+     }
+ 
+     ret = sbus_connection_add_path_map(ctx->conn, paths);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+               ret, sss_strerror(ret));
+         goto done;
+     }
+@@ -580,7 +580,7 @@ int main(int argc, const char *argv[])
+         return 3;
+     }
+ 
+-    DEBUG(SSSDBG_CRIT_FAILURE,
++    DEBUG(SSSDBG_IMPORTANT_INFO,
+           "Proxy child for domain [%s] started!\n", domain);
+ 
+     /* loop on main */
+diff --git a/src/providers/proxy/proxy_client.c b/src/providers/proxy/proxy_client.c
+index 09ebf3bda..5a4fbcde1 100644
+--- a/src/providers/proxy/proxy_client.c
++++ b/src/providers/proxy/proxy_client.c
+@@ -116,7 +116,7 @@ proxy_client_init(struct sbus_connection *conn,
+ 
+     ret = sbus_connection_add_path_map(conn, paths);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+               ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c
+index 82394862c..f36386089 100644
+--- a/src/providers/proxy/proxy_id.c
++++ b/src/providers/proxy/proxy_id.c
+@@ -170,7 +170,7 @@ handle_getpw_result(enum nss_status status, struct passwd *pwd,
+     switch (status) {
+     case NSS_STATUS_NOTFOUND:
+ 
+-        DEBUG(SSSDBG_MINOR_FAILURE, "User not found.\n");
++        DEBUG(SSSDBG_TRACE_FUNC, "User not found.\n");
+         *del_user = true;
+         break;
+ 
+@@ -979,9 +979,7 @@ static int get_gr_name(struct proxy_id_ctx *ctx,
+     grp = talloc(tmpctx, struct group);
+     if (!grp) {
+         ret = ENOMEM;
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              "proxy -> getgrnam_r failed for '%s': [%d] %s\n",
+-              i_name, ret, strerror(ret));
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc() failed\n");
+         goto done;
+     }
+ 
+diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
+index 07f05ff17..294a4b882 100644
+--- a/src/resolv/async_resolv.c
++++ b/src/resolv/async_resolv.c
+@@ -177,7 +177,7 @@ add_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx)
+     ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts,
+                                             ctx);
+     if (ctx->timeout_watcher == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer() failed\n");
+     }
+ }
+ 
+diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c
+index a802ed5d0..27de1b44a 100644
+--- a/src/responder/autofs/autofssrv.c
++++ b/src/responder/autofs/autofssrv.c
+@@ -85,7 +85,7 @@ autofs_register_service_iface(struct autofs_ctx *autofs_ctx,
+ 
+     ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register service interface"
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+               "[%d]: %s\n", ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c
+index 6d51e75ac..7c8090993 100644
+--- a/src/responder/autofs/autofssrv_cmd.c
++++ b/src/responder/autofs/autofssrv_cmd.c
+@@ -477,7 +477,7 @@ sss_autofs_cmd_setautomntent(struct cli_ctx *cli_ctx)
+                                             autofs_ctx->rctx->ncache, 0, NULL,
+                                             cmd_ctx->mapname);
+     if (req == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_map_by_name_send failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -685,7 +685,7 @@ sss_autofs_cmd_getautomntent(struct cli_ctx *cli_ctx)
+ 
+     req = autofs_setent_send(cli_ctx, cli_ctx->ev, autofs_ctx, cmd_ctx->mapname);
+     if (req == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "autofs_setent_send failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -886,7 +886,7 @@ sss_autofs_cmd_getautomntbyname(struct cli_ctx *cli_ctx)
+                                               cmd_ctx->mapname,
+                                               cmd_ctx->keyname);
+     if (req == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_entry_by_name_send failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
+index 0c8538414..c6902f842 100644
+--- a/src/responder/common/cache_req/cache_req.c
++++ b/src/responder/common/cache_req/cache_req.c
+@@ -1187,7 +1187,7 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
+     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");
++        DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_inp_send() failed\n");
+         return ENOMEM;
+     }
+ 
+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 a740fbb8d..83d00f775 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
+@@ -47,8 +47,8 @@ cache_req_object_by_name_well_known(TALLOC_CTX *mem_ctx,
+     }
+ 
+     if (domname == NULL || name == NULL) {
+-        CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, "Unable to split [%s] in "
+-                        "name and odmain part. Skipping detection of "
++        CACHE_REQ_DEBUG(SSSDBG_FUNC_DATA, cr, "Unable to split [%s] in "
++                        "name and domain part. Skipping detection of "
+                         "well-known name.\n", data->name.input);
+         return ENOENT;
+     }
+diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
+index e8d298546..7061d018a 100644
+--- a/src/responder/common/responder_common.c
++++ b/src/responder/common/responder_common.c
+@@ -116,7 +116,7 @@ static errno_t get_client_cred(struct cli_ctx *cctx)
+     if (ret != EOK) {
+         ret = errno;
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "getsock failed [%d][%s].\n", ret, strerror(ret));
++              "getsockopt failed [%d][%s].\n", ret, strerror(ret));
+         return ret;
+     }
+     if (client_cred_len != sizeof(struct ucred)) {
+@@ -805,7 +805,7 @@ sss_dp_on_reconnect(struct sbus_connection *conn,
+                                             SSS_BUS_PATH,
+                                             be_conn->cli_name);
+     if (req == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "sbus_call_dp_client_Register_send() failed\n");
+         return;
+     }
+ 
+diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
+index 10939600d..e551b0fff 100644
+--- a/src/responder/common/responder_get_domains.c
++++ b/src/responder/common/responder_get_domains.c
+@@ -630,7 +630,7 @@ static void sss_parse_inp_done(struct tevent_req *subreq)
+                                      state->rawinp,
+                                      &state->domname, &state->name);
+     if (ret == EAGAIN && state->domname != NULL && state->name == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE,
++        DEBUG(SSSDBG_FUNC_DATA,
+               "Unknown domain in [%s]\n", state->rawinp);
+         state->error = ERR_DOMAIN_NOT_FOUND;
+     } else if (ret != EOK) {
+diff --git a/src/responder/common/responder_iface.c b/src/responder/common/responder_iface.c
+index 911cd6cc0..aaa765950 100644
+--- a/src/responder/common/responder_iface.c
++++ b/src/responder/common/responder_iface.c
+@@ -127,7 +127,7 @@ sss_resp_register_sbus_iface(struct sbus_connection *conn,
+ 
+     ret = sbus_connection_add_path_map(conn, paths);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+               ret, sss_strerror(ret));
+     }
+ 
+@@ -151,7 +151,7 @@ sss_resp_register_service_iface(struct resp_ctx *rctx)
+ 
+     ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register service interface"
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+               "[%d]: %s\n", ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/responder/ifp/ifp_iface/ifp_iface.c b/src/responder/ifp/ifp_iface/ifp_iface.c
+index a3385091b..833cf6843 100644
+--- a/src/responder/ifp/ifp_iface/ifp_iface.c
++++ b/src/responder/ifp/ifp_iface/ifp_iface.c
+@@ -264,7 +264,7 @@ ifp_register_sbus_interface(struct sbus_connection *conn,
+ 
+     ret = sbus_connection_add_path_map(conn, paths);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+               ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/responder/ifp/ifpsrv.c b/src/responder/ifp/ifpsrv.c
+index 17d7692d3..7407ee07b 100644
+--- a/src/responder/ifp/ifpsrv.c
++++ b/src/responder/ifp/ifpsrv.c
+@@ -67,7 +67,7 @@ sysbus_init(TALLOC_CTX *mem_ctx,
+     sysbus = sbus_connect_system(mem_ctx, ev, dbus_name,
+                                  &ifp_ctx->rctx->last_request_time);
+     if (sysbus == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to system bus!\n");
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to connect to system bus!\n");
+         return ERR_NO_SYSBUS;
+     }
+ 
+@@ -75,13 +75,13 @@ sysbus_init(TALLOC_CTX *mem_ctx,
+ 
+     ret = ifp_register_sbus_interface(sysbus, ifp_ctx);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not register interfaces\n");
++        DEBUG(SSSDBG_FATAL_FAILURE, "Could not register interfaces\n");
+         goto done;
+     }
+ 
+     ret = ifp_register_nodes(ifp_ctx, sysbus);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not register nodes factories\n");
++        DEBUG(SSSDBG_FATAL_FAILURE, "Could not register nodes factories\n");
+         goto done;
+     }
+ 
+@@ -148,7 +148,7 @@ ifp_register_service_iface(struct ifp_ctx *ifp_ctx,
+ 
+     ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register service interface"
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+               "[%d]: %s\n", ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/responder/ifp/ifpsrv_util.c b/src/responder/ifp/ifpsrv_util.c
+index ebc4c2118..3b3df7bc0 100644
+--- a/src/responder/ifp/ifpsrv_util.c
++++ b/src/responder/ifp/ifpsrv_util.c
+@@ -341,7 +341,7 @@ immediately:
+     list_ctx->paths = talloc_realloc(list_ctx, list_ctx->paths, const char *,
+                                      list_ctx->paths_max + 1);
+     if (list_ctx->paths == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
+index eac955b4a..844776c5f 100644
+--- a/src/responder/nss/nss_cmd.c
++++ b/src/responder/nss/nss_cmd.c
+@@ -121,7 +121,7 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, memcache, rawname, 0);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_get_object_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -187,7 +187,7 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, memcache, NULL, id);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_get_object_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -240,7 +240,7 @@ static errno_t nss_getby_svc(struct cli_ctx *cli_ctx,
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, SSS_MC_NONE, NULL, 0);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_get_object_send() failed\n");
+         return ENOMEM;
+     }
+ 
+@@ -376,7 +376,7 @@ static errno_t nss_getby_cert(struct cli_ctx *cli_ctx,
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, SSS_MC_NONE, NULL, 0);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_get_object_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -433,7 +433,7 @@ static errno_t nss_getby_sid(struct cli_ctx *cli_ctx,
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, SSS_MC_NONE, NULL, 0);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_get_object_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -488,7 +488,7 @@ static errno_t nss_getby_addr(struct cli_ctx *cli_ctx,
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, memcache, NULL, 0);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_get_object_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -640,7 +640,7 @@ static errno_t nss_setent(struct cli_ctx *cli_ctx,
+ 
+     subreq = nss_setent_send(cli_ctx, cli_ctx->ev, cli_ctx, type, enum_ctx);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_setent_send() failed\n");
+         return ENOMEM;
+     }
+ 
+@@ -697,7 +697,7 @@ static errno_t nss_getent(struct cli_ctx *cli_ctx,
+ 
+     subreq = nss_setent_send(cli_ctx, cli_ctx->ev, cli_ctx, type, enum_ctx);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create setent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_setent_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -829,7 +829,7 @@ static errno_t sss_nss_setnetgrent(struct cli_ctx *cli_ctx,
+     subreq = nss_setnetgrent_send(cli_ctx, cli_ctx->ev, cli_ctx, type,
+                                   nss_ctx->netgrent, state_ctx->netgroup);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_setnetgrent_send() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -904,7 +904,7 @@ static errno_t nss_getnetgrent(struct cli_ctx *cli_ctx,
+                                   cmd_ctx->nss_ctx->netgrent,
+                                   cmd_ctx->state_ctx->netgroup);
+     if (subreq == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "nss_setnetgrent_send() failed\n");
+         return ENOMEM;
+     }
+ 
+diff --git a/src/responder/nss/nss_iface.c b/src/responder/nss/nss_iface.c
+index a47b35fca..ab2ba926d 100644
+--- a/src/responder/nss/nss_iface.c
++++ b/src/responder/nss/nss_iface.c
+@@ -67,7 +67,7 @@ nss_update_initgr_memcache(struct nss_ctx *nctx,
+     ret = sysdb_initgroups(tmp_ctx, dom, fq_name, &res);
+     if (ret != EOK && ret != ENOENT) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Failed to make request to our cache! [%d][%s]\n",
++              "sysdb_initgroups() failed [%d][%s]\n",
+               ret, strerror(ret));
+         goto done;
+     }
+@@ -234,7 +234,7 @@ nss_register_backend_iface(struct sbus_connection *conn,
+ 
+     ret = sbus_connection_add_path(conn, SSS_BUS_PATH, &iface);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register service interface"
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+               "[%d]: %s\n", ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/responder/nss/nss_protocol_netgr.c b/src/responder/nss/nss_protocol_netgr.c
+index 1e9959c72..274d43007 100644
+--- a/src/responder/nss/nss_protocol_netgr.c
++++ b/src/responder/nss/nss_protocol_netgr.c
+@@ -159,7 +159,7 @@ nss_protocol_fill_netgrent(struct nss_ctx *nss_ctx,
+             ret = nss_protocol_fill_netgr_member(packet, entry, &rp);
+             break;
+         default:
+-            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected value type!\n");
++            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected value type %d!\n", entry->type);
+             ret = ERR_INTERNAL;
+             break;
+         }
+diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
+index 31a2750b1..e80104e3d 100644
+--- a/src/responder/nss/nsssrv.c
++++ b/src/responder/nss/nsssrv.c
+@@ -347,7 +347,7 @@ nss_register_service_iface(struct nss_ctx *nss_ctx,
+ 
+     ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register service interface"
++        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+               "[%d]: %s\n", ret, sss_strerror(ret));
+     }
+ 
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index d3f092b2b..c526f665b 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -138,7 +138,7 @@ static void inform_user(struct pam_data* pd, const char *pam_message)
+     ret = pack_user_info_msg(pd, pam_message, &msg_len, &msg);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-              "pack_user_info_account_expired failed.\n");
++              "pack_user_info_msg failed.\n");
+     } else {
+         ret = pam_add_response(pd, SSS_PAM_USER_INFO, msg_len, msg);
+         if (ret != EOK) {
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index e1fd72e64..bf285c264 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -425,7 +425,7 @@ bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd)
+         }
+     }
+     if (pctx->smartcard_services[c] == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
++        DEBUG(SSSDBG_CONF_SETTINGS,
+               "Smartcard authentication for service [%s] not supported.\n",
+               pd->service);
+         return false;
+@@ -810,7 +810,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+     } else if (pd->cmd == SSS_PAM_PREAUTH) {
+         extra_args[arg_c++] = "--pre";
+     } else {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM command [%d}.\n", pd->cmd);
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM command [%d].\n", pd->cmd);
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/sbus/router/sbus_router_handler.c b/src/sbus/router/sbus_router_handler.c
+index 91a84c51b..a92cf524b 100644
+--- a/src/sbus/router/sbus_router_handler.c
++++ b/src/sbus/router/sbus_router_handler.c
+@@ -239,7 +239,8 @@ sbus_signal_handler(struct sbus_connection *conn,
+     list = sbus_router_listeners_lookup(router->listeners, meta->interface,
+                                         meta->member);
+     if (list == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "We do not listen to this signal!\n");
++        /* Most probably not fully initialized yet */
++        DEBUG(SSSDBG_FUNC_DATA, "We do not listen to this signal!\n");
+         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+     }
+ 
+diff --git a/src/sss_iface/sss_iface.c b/src/sss_iface/sss_iface.c
+index e20c14fea..ed70e30eb 100644
+--- a/src/sss_iface/sss_iface.c
++++ b/src/sss_iface/sss_iface.c
+@@ -116,8 +116,8 @@ sss_iface_connect_address(TALLOC_CTX *mem_ctx,
+ 
+     conn = sbus_connect_private(mem_ctx, ev, address,
+                                 conn_name, last_request_time);
+-    if (conn == NULL) {
+-        return ENOMEM;
++    if (conn == NULL) { /* most probably sbus_dbus_connect_address() failed */
++        return EFAULT;
+     }
+ 
+     *_conn = conn;
+diff --git a/src/util/child_common.c b/src/util/child_common.c
+index 5cac725ca..7e8c30552 100644
+--- a/src/util/child_common.c
++++ b/src/util/child_common.c
+@@ -768,7 +768,7 @@ void exec_child_ex(TALLOC_CTX *mem_ctx,
+                              binary, extra_argv, extra_args_only,
+                              &argv);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "prepare_child_argv.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "prepare_child_argv() failed.\n");
+         exit(EXIT_FAILURE);
+     }
+ 
+diff --git a/src/util/debug.h b/src/util/debug.h
+index 20db0f5e4..43d36720f 100644
+--- a/src/util/debug.h
++++ b/src/util/debug.h
+@@ -91,8 +91,8 @@ int get_fd_from_debug_file(void);
+ /* enables all debug levels;
+    0x0800 isn't used for historical reasons: 0x1FFF0 - 0x0800 = 0x1F7F0
+ */
+-#define SSSDBG_MASK_ALL       0x1F7F0
+-#define SSSDBG_DEFAULT        SSSDBG_FATAL_FAILURE
++#define SSSDBG_MASK_ALL  0x1F7F0
++#define SSSDBG_DEFAULT   (SSSDBG_FATAL_FAILURE|SSSDBG_CRIT_FAILURE|SSSDBG_OP_FAILURE)
+ 
+ #define SSSDBG_TIMESTAMP_UNRESOLVED   -1
+ #define SSSDBG_TIMESTAMP_DEFAULT       1
+diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
+index 4d4726daa..57157861e 100644
+--- a/src/util/domain_info_utils.c
++++ b/src/util/domain_info_utils.c
+@@ -207,7 +207,7 @@ find_domain_by_object_name_ex(struct sss_domain_info *domain,
+     ret = sss_parse_internal_fqname(tmp_ctx, object_name,
+                                     NULL, &domainname);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s\n",
++        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to parse name '%s' [%d]: %s\n",
+                                     object_name, ret, sss_strerror(ret));
+         goto done;
+     }
+diff --git a/src/util/server.c b/src/util/server.c
+index b27cbc155..869ed62a6 100644
+--- a/src/util/server.c
++++ b/src/util/server.c
+@@ -374,7 +374,7 @@ static void te_server_hup(struct tevent_context *ev,
+     struct logrotate_ctx *lctx =
+             talloc_get_type(private_data, struct logrotate_ctx);
+ 
+-    DEBUG(SSSDBG_CRIT_FAILURE, "Received SIGHUP. Rotating logfiles.\n");
++    DEBUG(SSSDBG_IMPORTANT_INFO, "Received SIGHUP. Rotating logfiles.\n");
+ 
+     ret = server_common_rotate_logs(lctx->confdb, lctx->confdb_path);
+     if (ret != EOK) {
+@@ -462,6 +462,7 @@ int server_setup(const char *name, int flags,
+     int watchdog_interval;
+     pid_t my_pid;
+     char *pidfile_name;
++    int cfg_debug_level = SSSDBG_INVALID;
+ 
+     my_pid = getpid();
+     ret = setpgid(my_pid, my_pid);
+@@ -588,20 +589,20 @@ int server_setup(const char *name, int flags,
+         /* set debug level if any in conf_entry */
+         ret = confdb_get_int(ctx->confdb_ctx, conf_entry,
+                              CONFDB_SERVICE_DEBUG_LEVEL,
+-                             SSSDBG_UNRESOLVED,
+-                             &debug_level);
++                             SSSDBG_INVALID,
++                             &cfg_debug_level);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) "
+                                          "[%s]\n", ret, strerror(ret));
+             return ret;
+         }
+ 
+-        if (debug_level == SSSDBG_UNRESOLVED) {
++        if (cfg_debug_level == SSSDBG_INVALID) {
+             /* Check for the `debug` alias */
+             ret = confdb_get_int(ctx->confdb_ctx, conf_entry,
+                     CONFDB_SERVICE_DEBUG_LEVEL_ALIAS,
+                     SSSDBG_DEFAULT,
+-                    &debug_level);
++                    &cfg_debug_level);
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) "
+                                             "[%s]\n", ret, strerror(ret));
+@@ -609,7 +610,7 @@ int server_setup(const char *name, int flags,
+             }
+         }
+ 
+-        debug_level = debug_convert_old_level(debug_level);
++        debug_level = debug_convert_old_level(cfg_debug_level);
+     }
+ 
+     /* same for debug timestamps */
+@@ -678,6 +679,8 @@ int server_setup(const char *name, int flags,
+             return ret;
+         }
+     }
++    DEBUG(SSSDBG_IMPORTANT_INFO,
++          "Starting with debug level = %#.4x\n", debug_level);
+ 
+     /* Setup the internal watchdog */
+     ret = confdb_get_int(ctx->confdb_ctx, conf_entry,
+diff --git a/src/util/sss_sockets.c b/src/util/sss_sockets.c
+index c6504ae13..8944e2c4e 100644
+--- a/src/util/sss_sockets.c
++++ b/src/util/sss_sockets.c
+@@ -322,7 +322,7 @@ struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx,
+ 
+     ret = set_fcntl_flags(state->sd, FD_CLOEXEC, O_NONBLOCK);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "settting fd flags failed.\n");
++        DEBUG(SSSDBG_CRIT_FAILURE, "setting fd flags failed.\n");
+         goto fail;
+     }
+ 
+diff --git a/src/util/string_utils.c b/src/util/string_utils.c
+index 1215ec96a..f54395a59 100644
+--- a/src/util/string_utils.c
++++ b/src/util/string_utils.c
+@@ -90,7 +90,7 @@ errno_t guid_blob_to_string_buf(const uint8_t *blob, char *str_buf,
+     int ret;
+ 
+     if (blob == NULL || str_buf == NULL || buf_size < GUID_STR_BUF_SIZE) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Buffer too small.\n");
++        DEBUG(SSSDBG_OP_FAILURE, "Buffer too small.\n");
+         return EINVAL;
+     }
+ 
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index 05a66d293..b5c7419a9 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -165,6 +165,7 @@ errno_t sss_ldb_error_to_errno(int ldberr)
+     case LDB_ERR_OPERATIONS_ERROR:
+         return EIO;
+     case LDB_ERR_NO_SUCH_OBJECT:
++    case LDB_ERR_NO_SUCH_ATTRIBUTE:
+         return ENOENT;
+     case LDB_ERR_BUSY:
+         return EBUSY;
+@@ -174,7 +175,7 @@ errno_t sss_ldb_error_to_errno(int ldberr)
+     case LDB_ERR_INVALID_ATTRIBUTE_SYNTAX:
+         return EINVAL;
+     default:
+-        DEBUG(SSSDBG_CRIT_FAILURE,
++        DEBUG(SSSDBG_MINOR_FAILURE,
+               "LDB returned unexpected error: [%i]\n",
+               ldberr);
+         return EFAULT;
+-- 
+2.21.3
+
diff --git a/SOURCES/0020-sss_format.h-include-config.h.patch b/SOURCES/0020-sss_format.h-include-config.h.patch
new file mode 100644
index 0000000..e237096
--- /dev/null
+++ b/SOURCES/0020-sss_format.h-include-config.h.patch
@@ -0,0 +1,31 @@
+From 45f2eb57dc9068cba13099cab90f1be3f3455442 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Fri, 2 Oct 2020 14:04:24 +0200
+Subject: [PATCH 20/27] sss_format.h: include config.h
+
+config.h is required for the definitions to work correctly. Compilation
+will fail if sss_format.h is included in a file that does not include
+directly or indirectly config.h
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/util/sss_format.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/util/sss_format.h b/src/util/sss_format.h
+index 5cf080842..9a3041704 100644
+--- a/src/util/sss_format.h
++++ b/src/util/sss_format.h
+@@ -27,6 +27,8 @@
+ #ifndef __SSS_FORMAT_H__
+ #define __SSS_FORMAT_H__
+ 
++#include "config.h"
++
+ #include <inttypes.h>
+ 
+ /* key_serial_t is defined in keyutils.h as typedef int32_t */
+-- 
+2.21.3
+
diff --git a/SOURCES/0021-packet-add-sss_packet_set_body.patch b/SOURCES/0021-packet-add-sss_packet_set_body.patch
new file mode 100644
index 0000000..5311316
--- /dev/null
+++ b/SOURCES/0021-packet-add-sss_packet_set_body.patch
@@ -0,0 +1,59 @@
+From 3b0e48c33c6b43688ff46fed576266cfe6362595 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 8 Oct 2020 13:25:17 +0200
+Subject: [PATCH 21/27] packet: add sss_packet_set_body
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/responder/common/responder_packet.c | 19 +++++++++++++++++++
+ src/responder/common/responder_packet.h |  5 +++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c
+index ab15b1dac..f56d92276 100644
+--- a/src/responder/common/responder_packet.c
++++ b/src/responder/common/responder_packet.c
+@@ -302,6 +302,25 @@ void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen
+     *blen = sss_packet_get_len(packet) - SSS_NSS_HEADER_SIZE;
+ }
+ 
++errno_t sss_packet_set_body(struct sss_packet *packet,
++                            uint8_t *body,
++                            size_t blen)
++{
++    uint8_t *pbody;
++    size_t plen;
++    errno_t ret;
++
++    ret = sss_packet_grow(packet, blen);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    sss_packet_get_body(packet, &pbody, &plen);
++    memcpy(pbody, body, blen);
++
++    return EOK;
++}
++
+ void sss_packet_set_error(struct sss_packet *packet, int error)
+ {
+     SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_ERR_OFFSET, error,
+diff --git a/src/responder/common/responder_packet.h b/src/responder/common/responder_packet.h
+index afceb4aae..509a22a9a 100644
+--- a/src/responder/common/responder_packet.h
++++ b/src/responder/common/responder_packet.h
+@@ -42,4 +42,9 @@ uint32_t sss_packet_get_status(struct sss_packet *packet);
+ void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen);
+ void sss_packet_set_error(struct sss_packet *packet, int error);
+ 
++/* Grow packet and set its body. */
++errno_t sss_packet_set_body(struct sss_packet *packet,
++                            uint8_t *body,
++                            size_t blen);
++
+ #endif /* __SSSSRV_PACKET_H__ */
+-- 
+2.21.3
+
diff --git a/SOURCES/0022-domain-store-hostname-and-keytab-path.patch b/SOURCES/0022-domain-store-hostname-and-keytab-path.patch
new file mode 100644
index 0000000..27628e6
--- /dev/null
+++ b/SOURCES/0022-domain-store-hostname-and-keytab-path.patch
@@ -0,0 +1,119 @@
+From 6715b31f2e12c7f76cfb477551cee46e697c7d51 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 8 Oct 2020 13:25:58 +0200
+Subject: [PATCH 22/27] domain: store hostname and keytab path
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/confdb/confdb.c       | 45 +++++++++++++++++++++++++++++++++++++++
+ src/confdb/confdb.h       |  6 ++++++
+ src/db/sysdb_subdomains.c | 12 +++++++++++
+ 3 files changed, 63 insertions(+)
+
+diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
+index d2fc018fd..f981ddf1e 100644
+--- a/src/confdb/confdb.c
++++ b/src/confdb/confdb.c
+@@ -871,6 +871,35 @@ done:
+     return ret;
+ }
+ 
++static char *confdb_get_domain_hostname(TALLOC_CTX *mem_ctx,
++                                        struct ldb_result *res,
++                                        const char *provider)
++{
++    char sys[HOST_NAME_MAX + 1] = {'\0'};
++    const char *opt = NULL;
++    int ret;
++
++    if (strcasecmp(provider, "ad") == 0) {
++        opt = ldb_msg_find_attr_as_string(res->msgs[0], "ad_hostname", NULL);
++    } else if (strcasecmp(provider, "ipa") == 0) {
++        opt = ldb_msg_find_attr_as_string(res->msgs[0], "ipa_hostname", NULL);
++    }
++
++    if (opt != NULL) {
++        return talloc_strdup(mem_ctx, opt);
++    }
++
++    ret = gethostname(sys, sizeof(sys));
++    if (ret != 0) {
++        ret = errno;
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get hostname [%d]: %s\n", ret,
++              sss_strerror(ret));
++        return NULL;
++    }
++
++    return talloc_strdup(mem_ctx, sys);
++}
++
+ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+                                       TALLOC_CTX *mem_ctx,
+                                       const char *name,
+@@ -1536,6 +1565,22 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+         goto done;
+     }
+ 
++    domain->hostname = confdb_get_domain_hostname(domain, res, domain->provider);
++    if (domain->hostname == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain hostname\n");
++        goto done;
++    }
++
++    domain->krb5_keytab = NULL;
++    tmp = ldb_msg_find_attr_as_string(res->msgs[0], "krb5_keytab", NULL);
++    if (tmp != NULL) {
++        domain->krb5_keytab = talloc_strdup(domain, tmp);
++        if (domain->krb5_keytab == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain keytab!\n");
++            goto done;
++        }
++    }
++
+     domain->has_views = false;
+     domain->view_name = NULL;
+ 
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index fd6d76cde..54e3f7380 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -425,6 +425,12 @@ struct sss_domain_info {
+     /* Do not use the _output_fqnames property directly in new code, but rather
+      * use sss_domain_info_{get,set}_output_fqnames(). */
+     bool output_fqnames;
++
++    /* Hostname associated with this domain. */
++    const char *hostname;
++
++    /* Keytab used by this domain. */
++    const char *krb5_keytab;
+ };
+ 
+ /**
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index d256817a6..5b42f9bdc 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -125,6 +125,18 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+         }
+     }
+ 
++    dom->hostname = talloc_strdup(dom, parent->hostname);
++    if (dom->hostname == NULL && parent->hostname != NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy hostname.\n");
++        goto fail;
++    }
++
++    dom->krb5_keytab = talloc_strdup(dom, parent->krb5_keytab);
++    if (dom->krb5_keytab == NULL && parent->krb5_keytab != NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy krb5_keytab.\n");
++        goto fail;
++    }
++
+     dom->enumerate = enumerate;
+     dom->fqnames = true;
+     dom->mpg_mode = mpg_mode;
+-- 
+2.21.3
+
diff --git a/SOURCES/0023-cache_req-add-helper-to-call-user-by-upn-search.patch b/SOURCES/0023-cache_req-add-helper-to-call-user-by-upn-search.patch
new file mode 100644
index 0000000..168f8b6
--- /dev/null
+++ b/SOURCES/0023-cache_req-add-helper-to-call-user-by-upn-search.patch
@@ -0,0 +1,70 @@
+From a3e2677f919c6b1b1649ad80cc3435b4bb2efc0d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 10 Dec 2020 19:28:58 +0100
+Subject: [PATCH 23/27] cache_req: add helper to call user by upn search
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/responder/common/cache_req/cache_req.h    | 13 +++++++++++
+ .../cache_req/plugins/cache_req_user_by_upn.c | 23 +++++++++++++++++++
+ 2 files changed, 36 insertions(+)
+
+diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
+index d36cb2d3b..d301a076e 100644
+--- a/src/responder/common/cache_req/cache_req.h
++++ b/src/responder/common/cache_req/cache_req.h
+@@ -277,6 +277,19 @@ cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx,
+ #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_upn_send(TALLOC_CTX *mem_ctx,
++                           struct tevent_context *ev,
++                           struct resp_ctx *rctx,
++                           struct sss_nc_ctx *ncache,
++                           int cache_refresh_percent,
++                           enum cache_req_dom_type req_dom_type,
++                           const char *domain,
++                           const char *upn);
++
++#define cache_req_user_by_upn_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,
+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 e08ab70ae..037994c8c 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
+@@ -133,3 +133,26 @@ const struct cache_req_plugin cache_req_user_by_upn = {
+     .dp_get_domain_send_fn = NULL,
+     .dp_get_domain_recv_fn = NULL,
+ };
++
++struct tevent_req *
++cache_req_user_by_upn_send(TALLOC_CTX *mem_ctx,
++                           struct tevent_context *ev,
++                           struct resp_ctx *rctx,
++                           struct sss_nc_ctx *ncache,
++                           int cache_refresh_percent,
++                           enum cache_req_dom_type req_dom_type,
++                           const char *domain,
++                           const char *upn)
++{
++    struct cache_req_data *data;
++
++    data = cache_req_data_name(mem_ctx, CACHE_REQ_USER_BY_UPN, upn);
++    if (data == NULL) {
++        return NULL;
++    }
++
++    return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
++                                         cache_refresh_percent,
++                                         req_dom_type, domain,
++                                         data);
++}
+-- 
+2.21.3
+
diff --git a/SOURCES/0024-pam-fix-typo-in-debug-message.patch b/SOURCES/0024-pam-fix-typo-in-debug-message.patch
new file mode 100644
index 0000000..25167e1
--- /dev/null
+++ b/SOURCES/0024-pam-fix-typo-in-debug-message.patch
@@ -0,0 +1,27 @@
+From dcc42015f7ada1c4e4daed17e2c8087e29cb7616 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 1 Oct 2020 14:02:44 +0200
+Subject: [PATCH 24/27] pam: fix typo in debug message
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/responder/pam/pamsrv_cmd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index 1d0251497..acbfc0c39 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -1941,7 +1941,7 @@ static void pam_check_user_search_next(struct tevent_req *req)
+     talloc_zfree(req);
+     if (ret != EOK && ret != ENOENT) {
+         DEBUG(SSSDBG_OP_FAILURE, "Cache lookup failed, trying to get fresh "
+-                                 "data from the backened.\n");
++                                 "data from the backend.\n");
+     }
+ 
+     DEBUG(SSSDBG_TRACE_ALL, "PAM initgroups scheme [%s].\n",
+-- 
+2.21.3
+
diff --git a/SOURCES/0025-pam-add-pam_gssapi_services-option.patch b/SOURCES/0025-pam-add-pam_gssapi_services-option.patch
new file mode 100644
index 0000000..7c90067
--- /dev/null
+++ b/SOURCES/0025-pam-add-pam_gssapi_services-option.patch
@@ -0,0 +1,280 @@
+From d63172f1277c5ed166a22f04d144bf85ded4757c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Fri, 9 Oct 2020 13:03:54 +0200
+Subject: [PATCH 25/27] pam: add pam_gssapi_services option
+
+:config: Added `pam_gssapi_services` to list PAM services
+  that can authenticate using GSSAPI
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/confdb/confdb.c                  | 12 +++++++++++
+ src/confdb/confdb.h                  |  4 ++++
+ src/config/SSSDConfig/sssdoptions.py |  1 +
+ src/config/SSSDConfigTest.py         |  6 ++++--
+ src/config/cfg_rules.ini             |  3 +++
+ src/config/etc/sssd.api.conf         |  2 ++
+ src/db/sysdb_subdomains.c            | 13 ++++++++++++
+ src/man/sssd.conf.5.xml              | 30 ++++++++++++++++++++++++++++
+ src/responder/pam/pamsrv.c           | 21 +++++++++++++++++++
+ src/responder/pam/pamsrv.h           |  3 +++
+ 10 files changed, 93 insertions(+), 2 deletions(-)
+
+diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
+index f981ddf1e..7f1956d6d 100644
+--- a/src/confdb/confdb.c
++++ b/src/confdb/confdb.c
+@@ -1581,6 +1581,18 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+         }
+     }
+ 
++    tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_PAM_GSSAPI_SERVICES,
++                                      "-");
++    if (tmp != NULL) {
++        ret = split_on_separator(domain, tmp, ',', true, true,
++                                 &domain->gssapi_services, NULL);
++        if (ret != 0) {
++            DEBUG(SSSDBG_FATAL_FAILURE,
++                  "Cannot parse %s\n", CONFDB_PAM_GSSAPI_SERVICES);
++            goto done;
++        }
++    }
++
+     domain->has_views = false;
+     domain->view_name = NULL;
+ 
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index 54e3f7380..7a3bc8bb5 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -144,6 +144,7 @@
+ #define CONFDB_PAM_P11_ALLOWED_SERVICES "pam_p11_allowed_services"
+ #define CONFDB_PAM_P11_URI "p11_uri"
+ #define CONFDB_PAM_INITGROUPS_SCHEME "pam_initgroups_scheme"
++#define CONFDB_PAM_GSSAPI_SERVICES "pam_gssapi_services"
+ 
+ /* SUDO */
+ #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
+@@ -431,6 +432,9 @@ struct sss_domain_info {
+ 
+     /* Keytab used by this domain. */
+     const char *krb5_keytab;
++
++    /* List of PAM services that are allowed to authenticate with GSSAPI. */
++    char **gssapi_services;
+ };
+ 
+ /**
+diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py
+index de96db6f4..f59fe8d9f 100644
+--- a/src/config/SSSDConfig/sssdoptions.py
++++ b/src/config/SSSDConfig/sssdoptions.py
+@@ -104,6 +104,7 @@ class SSSDOptions(object):
+         'p11_wait_for_card_timeout': _('Additional timeout to wait for a card if requested'),
+         'p11_uri': _('PKCS#11 URI to restrict the selection of devices for Smartcard authentication'),
+         'pam_initgroups_scheme' : _('When shall the PAM responder force an initgroups request'),
++        'pam_gssapi_services' : _('List of PAM services that are allowed to authenticate with GSSAPI.'),
+ 
+         # [sudo]
+         'sudo_timed': _('Whether to evaluate the time-based attributes in sudo rules'),
+diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
+index 323be5ed3..21fffe1b6 100755
+--- a/src/config/SSSDConfigTest.py
++++ b/src/config/SSSDConfigTest.py
+@@ -653,7 +653,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+             'full_name_format',
+             're_expression',
+             'cached_auth_timeout',
+-            'auto_private_groups']
++            'auto_private_groups',
++            'pam_gssapi_services']
+ 
+         self.assertTrue(type(options) == dict,
+                         "Options should be a dictionary")
+@@ -1030,7 +1031,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+             'full_name_format',
+             're_expression',
+             'cached_auth_timeout',
+-            'auto_private_groups']
++            'auto_private_groups',
++            'pam_gssapi_services']
+ 
+         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 773afd8bb..c6dfd5648 100644
+--- a/src/config/cfg_rules.ini
++++ b/src/config/cfg_rules.ini
+@@ -139,6 +139,7 @@ option = pam_p11_allowed_services
+ option = p11_wait_for_card_timeout
+ option = p11_uri
+ option = pam_initgroups_scheme
++option = pam_gssapi_services
+ 
+ [rule/allowed_sudo_options]
+ validator = ini_allowed_options
+@@ -437,6 +438,7 @@ option = wildcard_limit
+ option = full_name_format
+ option = re_expression
+ option = auto_private_groups
++option = pam_gssapi_services
+ 
+ #Entry cache timeouts
+ option = entry_cache_user_timeout
+@@ -831,6 +833,7 @@ option = ad_backup_server
+ option = ad_site
+ option = use_fully_qualified_names
+ option = auto_private_groups
++option = pam_gssapi_services
+ 
+ [rule/sssd_checks]
+ validator = sssd_checks
+diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
+index 623160ffd..f46f3c46d 100644
+--- a/src/config/etc/sssd.api.conf
++++ b/src/config/etc/sssd.api.conf
+@@ -80,6 +80,7 @@ pam_p11_allowed_services = str, None, false
+ p11_wait_for_card_timeout = int, None, false
+ p11_uri = str, None, false
+ pam_initgroups_scheme = str, None, false
++pam_gssapi_services = str, None, false
+ 
+ [sudo]
+ # sudo service
+@@ -199,6 +200,7 @@ cached_auth_timeout = int, None, false
+ full_name_format = str, None, false
+ re_expression = str, None, false
+ auto_private_groups = str, None, false
++pam_gssapi_services = str, None, false
+ 
+ #Entry cache timeouts
+ entry_cache_user_timeout = int, None, false
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index 5b42f9bdc..bfc6df0f5 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -184,6 +184,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+     dom->homedir_substr = parent->homedir_substr;
+     dom->override_gid = parent->override_gid;
+ 
++    dom->gssapi_services = parent->gssapi_services;
++
+     if (parent->sysdb == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n");
+         goto fail;
+@@ -241,6 +243,17 @@ check_subdom_config_file(struct confdb_ctx *confdb,
+           sd_conf_path, CONFDB_DOMAIN_FQ,
+           subdomain->fqnames ? "TRUE" : "FALSE");
+ 
++    /* allow to set pam_gssapi_services */
++    ret = confdb_get_string_as_list(confdb, subdomain, sd_conf_path,
++                                    CONFDB_PAM_GSSAPI_SERVICES,
++                                    &subdomain->gssapi_services);
++    if (ret != EOK && ret != ENOENT) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Failed to get %s option for the subdomain: %s\n",
++              CONFDB_PAM_GSSAPI_SERVICES, subdomain->name);
++        goto done;
++    }
++
+     ret = EOK;
+ done:
+     talloc_free(tmp_ctx);
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index d247400bf..db9dd4677 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -1706,6 +1706,35 @@ p11_uri = library-description=OpenSC%20smartcard%20framework;slot-id=2
+                         </para>
+                     </listitem>
+                 </varlistentry>
++                <varlistentry>
++                    <term>pam_gssapi_services</term>
++                    <listitem>
++                        <para>
++                            Comma separated list of PAM services that are
++                            allowed to try GSSAPI authentication using
++                            pam_sss_gss.so module.
++                        </para>
++                        <para>
++                            To disable GSSAPI authentication, set this option
++                            to <quote>-</quote> (dash).
++                        </para>
++                        <para>
++                            Note: This option can also be set per-domain which
++                            overwrites the value in [pam] section. It can also
++                            be set for trusted domain which overwrites the value
++                            in the domain section.
++                        </para>
++                        <para>
++                            Example:
++                            <programlisting>
++pam_gssapi_services = sudo, sudo-i
++                            </programlisting>
++                        </para>
++                        <para>
++                            Default: - (GSSAPI authentication is disabled)
++                        </para>
++                    </listitem>
++                </varlistentry>
+             </variablelist>
+         </refsect2>
+ 
+@@ -3780,6 +3809,7 @@ ldap_user_extra_attrs = phone:telephoneNumber
+             <para>ad_backup_server,</para>
+             <para>ad_site,</para>
+             <para>use_fully_qualified_names</para>
++            <para>pam_gssapi_services</para>
+         <para>
+             For more details about these options see their individual description
+             in the manual page.
+diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
+index 1f1ee608b..0492569c7 100644
+--- a/src/responder/pam/pamsrv.c
++++ b/src/responder/pam/pamsrv.c
+@@ -327,6 +327,27 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
+         }
+     }
+ 
++    ret = confdb_get_string(pctx->rctx->cdb, pctx, CONFDB_PAM_CONF_ENTRY,
++                            CONFDB_PAM_GSSAPI_SERVICES, "-", &tmpstr);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FATAL_FAILURE,
++              "Failed to determine gssapi services.\n");
++        goto done;
++    }
++    DEBUG(SSSDBG_TRACE_INTERNAL, "Found value [%s] for option [%s].\n", tmpstr,
++                                 CONFDB_PAM_GSSAPI_SERVICES);
++
++    if (tmpstr != NULL) {
++        ret = split_on_separator(pctx, tmpstr, ',', true, true,
++                                 &pctx->gssapi_services, NULL);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  "split_on_separator() failed [%d]: [%s].\n", ret,
++                  sss_strerror(ret));
++            goto done;
++        }
++    }
++
+     /* The responder is initialized. Now tell it to the monitor. */
+     ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_PAM,
+                                    SSS_PAM_SBUS_SERVICE_NAME,
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index 24d307a14..730dee288 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -62,6 +62,9 @@ struct pam_ctx {
+     int num_prompting_config_sections;
+ 
+     enum pam_initgroups_scheme initgroups_scheme;
++
++    /* List of PAM services that are allowed to authenticate with GSSAPI. */
++    char **gssapi_services;
+ };
+ 
+ struct pam_auth_req {
+-- 
+2.21.3
+
diff --git a/SOURCES/0026-pam-add-pam_gssapi_check_upn-option.patch b/SOURCES/0026-pam-add-pam_gssapi_check_upn-option.patch
new file mode 100644
index 0000000..6e59705
--- /dev/null
+++ b/SOURCES/0026-pam-add-pam_gssapi_check_upn-option.patch
@@ -0,0 +1,250 @@
+From fffe3169bb490c4b010b168c639aa6f9b2ec0c52 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 10 Dec 2020 22:05:30 +0100
+Subject: [PATCH 26/27] pam: add pam_gssapi_check_upn option
+
+:config: Added `pam_gssapi_check_upn` to enforce authentication
+  only with principal that can be associated with target user.
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/confdb/confdb.c                  | 10 ++++++++++
+ src/confdb/confdb.h                  |  2 ++
+ src/config/SSSDConfig/sssdoptions.py |  1 +
+ src/config/SSSDConfigTest.py         |  6 ++++--
+ src/config/cfg_rules.ini             |  3 +++
+ src/config/etc/sssd.api.conf         |  2 ++
+ src/db/sysdb_subdomains.c            | 12 ++++++++++++
+ src/man/sssd.conf.5.xml              | 26 ++++++++++++++++++++++++++
+ src/responder/pam/pamsrv.c           |  9 +++++++++
+ src/responder/pam/pamsrv.h           |  1 +
+ 10 files changed, 70 insertions(+), 2 deletions(-)
+
+diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
+index 7f1956d6d..2881ce5da 100644
+--- a/src/confdb/confdb.c
++++ b/src/confdb/confdb.c
+@@ -1593,6 +1593,16 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+         }
+     }
+ 
++    tmp = ldb_msg_find_attr_as_string(res->msgs[0], CONFDB_PAM_GSSAPI_CHECK_UPN,
++                                      NULL);
++    if (tmp != NULL) {
++        domain->gssapi_check_upn = talloc_strdup(domain, tmp);
++        if (domain->gssapi_check_upn == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++    }
++
+     domain->has_views = false;
+     domain->view_name = NULL;
+ 
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index 7a3bc8bb5..036f9ecad 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -145,6 +145,7 @@
+ #define CONFDB_PAM_P11_URI "p11_uri"
+ #define CONFDB_PAM_INITGROUPS_SCHEME "pam_initgroups_scheme"
+ #define CONFDB_PAM_GSSAPI_SERVICES "pam_gssapi_services"
++#define CONFDB_PAM_GSSAPI_CHECK_UPN "pam_gssapi_check_upn"
+ 
+ /* SUDO */
+ #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
+@@ -435,6 +436,7 @@ struct sss_domain_info {
+ 
+     /* List of PAM services that are allowed to authenticate with GSSAPI. */
+     char **gssapi_services;
++    char *gssapi_check_upn; /* true | false | NULL */
+ };
+ 
+ /**
+diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py
+index f59fe8d9f..5da52a937 100644
+--- a/src/config/SSSDConfig/sssdoptions.py
++++ b/src/config/SSSDConfig/sssdoptions.py
+@@ -105,6 +105,7 @@ class SSSDOptions(object):
+         'p11_uri': _('PKCS#11 URI to restrict the selection of devices for Smartcard authentication'),
+         'pam_initgroups_scheme' : _('When shall the PAM responder force an initgroups request'),
+         'pam_gssapi_services' : _('List of PAM services that are allowed to authenticate with GSSAPI.'),
++        'pam_gssapi_check_upn' : _('Whether to match authenticated UPN with target user'),
+ 
+         # [sudo]
+         'sudo_timed': _('Whether to evaluate the time-based attributes in sudo rules'),
+diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
+index 21fffe1b6..ea4e4f6c9 100755
+--- a/src/config/SSSDConfigTest.py
++++ b/src/config/SSSDConfigTest.py
+@@ -654,7 +654,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+             're_expression',
+             'cached_auth_timeout',
+             'auto_private_groups',
+-            'pam_gssapi_services']
++            'pam_gssapi_services',
++            'pam_gssapi_check_upn']
+ 
+         self.assertTrue(type(options) == dict,
+                         "Options should be a dictionary")
+@@ -1032,7 +1033,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+             're_expression',
+             'cached_auth_timeout',
+             'auto_private_groups',
+-            'pam_gssapi_services']
++            'pam_gssapi_services',
++            'pam_gssapi_check_upn']
+ 
+         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 c6dfd5648..6642c6321 100644
+--- a/src/config/cfg_rules.ini
++++ b/src/config/cfg_rules.ini
+@@ -140,6 +140,7 @@ option = p11_wait_for_card_timeout
+ option = p11_uri
+ option = pam_initgroups_scheme
+ option = pam_gssapi_services
++option = pam_gssapi_check_upn
+ 
+ [rule/allowed_sudo_options]
+ validator = ini_allowed_options
+@@ -439,6 +440,7 @@ option = full_name_format
+ option = re_expression
+ option = auto_private_groups
+ option = pam_gssapi_services
++option = pam_gssapi_check_upn
+ 
+ #Entry cache timeouts
+ option = entry_cache_user_timeout
+@@ -834,6 +836,7 @@ option = ad_site
+ option = use_fully_qualified_names
+ option = auto_private_groups
+ option = pam_gssapi_services
++option = pam_gssapi_check_upn
+ 
+ [rule/sssd_checks]
+ validator = sssd_checks
+diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
+index f46f3c46d..d3cad7380 100644
+--- a/src/config/etc/sssd.api.conf
++++ b/src/config/etc/sssd.api.conf
+@@ -81,6 +81,7 @@ p11_wait_for_card_timeout = int, None, false
+ p11_uri = str, None, false
+ pam_initgroups_scheme = str, None, false
+ pam_gssapi_services = str, None, false
++pam_gssapi_check_upn = bool, None, false
+ 
+ [sudo]
+ # sudo service
+@@ -201,6 +202,7 @@ full_name_format = str, None, false
+ re_expression = str, None, false
+ auto_private_groups = str, None, false
+ pam_gssapi_services = str, None, false
++pam_gssapi_check_upn = bool, None, false
+ 
+ #Entry cache timeouts
+ entry_cache_user_timeout = int, None, false
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index bfc6df0f5..03ba12164 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -254,6 +254,18 @@ check_subdom_config_file(struct confdb_ctx *confdb,
+         goto done;
+     }
+ 
++    /* allow to set pam_gssapi_check_upn */
++    ret = confdb_get_string(confdb, subdomain, sd_conf_path,
++                            CONFDB_PAM_GSSAPI_CHECK_UPN,
++                            subdomain->parent->gssapi_check_upn,
++                            &subdomain->gssapi_check_upn);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Failed to get %s option for the subdomain: %s\n",
++              CONFDB_PAM_GSSAPI_CHECK_UPN, subdomain->name);
++        goto done;
++    }
++
+     ret = EOK;
+ done:
+     talloc_free(tmp_ctx);
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index db9dd4677..d637e2eaa 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -1735,6 +1735,31 @@ pam_gssapi_services = sudo, sudo-i
+                         </para>
+                     </listitem>
+                 </varlistentry>
++                <varlistentry>
++                    <term>pam_gssapi_check_upn</term>
++                    <listitem>
++                        <para>
++                            If True, SSSD will require that the Kerberos user
++                            principal that successfully authenticated through
++                            GSSAPI can be associated with the user who is being
++                            authenticated. Authentication will fail if the check
++                            fails.
++                        </para>
++                        <para>
++                            If False, every user that is able to obtained
++                            required service ticket will be authenticated.
++                        </para>
++                        <para>
++                            Note: This option can also be set per-domain which
++                            overwrites the value in [pam] section. It can also
++                            be set for trusted domain which overwrites the value
++                            in the domain section.
++                        </para>
++                        <para>
++                            Default: True
++                        </para>
++                    </listitem>
++                </varlistentry>
+             </variablelist>
+         </refsect2>
+ 
+@@ -3810,6 +3835,7 @@ ldap_user_extra_attrs = phone:telephoneNumber
+             <para>ad_site,</para>
+             <para>use_fully_qualified_names</para>
+             <para>pam_gssapi_services</para>
++            <para>pam_gssapi_check_upn</para>
+         <para>
+             For more details about these options see their individual description
+             in the manual page.
+diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
+index 0492569c7..0db2824ff 100644
+--- a/src/responder/pam/pamsrv.c
++++ b/src/responder/pam/pamsrv.c
+@@ -348,6 +348,15 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
+         }
+     }
+ 
++    ret = confdb_get_bool(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
++                          CONFDB_PAM_GSSAPI_CHECK_UPN, true,
++                          &pctx->gssapi_check_upn);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to read %s [%d]: %s\n",
++              CONFDB_PAM_GSSAPI_CHECK_UPN, ret, sss_strerror(ret));
++        goto done;
++    }
++
+     /* The responder is initialized. Now tell it to the monitor. */
+     ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_PAM,
+                                    SSS_PAM_SBUS_SERVICE_NAME,
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index 730dee288..bf4dd75b0 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -65,6 +65,7 @@ struct pam_ctx {
+ 
+     /* List of PAM services that are allowed to authenticate with GSSAPI. */
+     char **gssapi_services;
++    bool gssapi_check_upn;
+ };
+ 
+ struct pam_auth_req {
+-- 
+2.21.3
+
diff --git a/SOURCES/0027-pam-add-pam_sss_gss-module-for-gssapi-authentication.patch b/SOURCES/0027-pam-add-pam_sss_gss-module-for-gssapi-authentication.patch
new file mode 100644
index 0000000..baa7927
--- /dev/null
+++ b/SOURCES/0027-pam-add-pam_sss_gss-module-for-gssapi-authentication.patch
@@ -0,0 +1,1866 @@
+From d09aa174b04a825979f31c61b05239de088a732f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Tue, 7 Jul 2020 11:05:37 +0200
+Subject: [PATCH 27/27] pam: add pam_sss_gss module for gssapi authentication
+
+:feature: New PAM module `pam_sss_gss` for authentication using GSSAPI
+:packaging: Added `pam_sss_gss.so` PAM module and `pam_sss_gss.8` manual page
+
+Reviewed-by: Robbie Harwood <rharwood@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ Makefile.am                        |  33 +-
+ configure.ac                       |   1 +
+ contrib/sssd.spec.in               |   2 +
+ src/external/libgssapi_krb5.m4     |   8 +
+ src/man/Makefile.am                |   4 +-
+ src/man/pam_sss_gss.8.xml          | 209 ++++++++
+ src/responder/pam/pamsrv.h         |   4 +
+ src/responder/pam/pamsrv_cmd.c     |   2 +
+ src/responder/pam/pamsrv_gssapi.c  | 792 +++++++++++++++++++++++++++++
+ src/sss_client/pam_sss_gss.c       | 588 +++++++++++++++++++++
+ src/sss_client/pam_sss_gss.exports |   4 +
+ src/sss_client/sss_cli.h           |   8 +
+ src/tests/dlopen-tests.c           |   1 +
+ 13 files changed, 1653 insertions(+), 3 deletions(-)
+ create mode 100644 src/external/libgssapi_krb5.m4
+ create mode 100644 src/man/pam_sss_gss.8.xml
+ create mode 100644 src/responder/pam/pamsrv_gssapi.c
+ create mode 100644 src/sss_client/pam_sss_gss.c
+ create mode 100644 src/sss_client/pam_sss_gss.exports
+
+diff --git a/Makefile.am b/Makefile.am
+index 430b4e842..1c82776ab 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1585,12 +1585,14 @@ sssd_pam_SOURCES = \
+     src/responder/pam/pamsrv_cmd.c \
+     src/responder/pam/pamsrv_p11.c \
+     src/responder/pam/pamsrv_dp.c \
++    src/responder/pam/pamsrv_gssapi.c \
+     src/responder/pam/pam_prompting_config.c \
+     src/sss_client/pam_sss_prompt_config.c \
+     src/responder/pam/pam_helpers.c \
+     $(SSSD_RESPONDER_OBJ)
+ sssd_pam_CFLAGS = \
+     $(AM_CFLAGS) \
++    $(GSSAPI_KRB5_CFLAGS) \
+     $(NULL)
+ sssd_pam_LDADD = \
+     $(LIBADD_DL) \
+@@ -1599,6 +1601,7 @@ sssd_pam_LDADD = \
+     $(SELINUX_LIBS) \
+     $(PAM_LIBS) \
+     $(SYSTEMD_DAEMON_LIBS) \
++    $(GSSAPI_KRB5_LIBS) \
+     libsss_certmap.la \
+     $(SSSD_INTERNAL_LTLIBS) \
+     libsss_iface.la \
+@@ -2710,6 +2713,7 @@ pam_srv_tests_SOURCES = \
+     src/sss_client/pam_message.c \
+     src/responder/pam/pamsrv_cmd.c \
+     src/responder/pam/pamsrv_p11.c \
++    src/responder/pam/pamsrv_gssapi.c \
+     src/responder/pam/pam_helpers.c \
+     src/responder/pam/pamsrv_dp.c \
+     src/responder/pam/pam_LOCAL_domain.c \
+@@ -2721,6 +2725,7 @@ pam_srv_tests_CFLAGS = \
+     -I$(abs_builddir)/src \
+     $(AM_CFLAGS) \
+     $(CMOCKA_CFLAGS) \
++    $(GSSAPI_KRB5_CFLAGS) \
+     $(NULL)
+ pam_srv_tests_LDFLAGS = \
+     -Wl,-wrap,sss_packet_get_body \
+@@ -2736,6 +2741,7 @@ pam_srv_tests_LDADD = \
+     $(SSSD_LIBS) \
+     $(SSSD_INTERNAL_LTLIBS) \
+     $(SYSTEMD_DAEMON_LIBS) \
++    $(GSSAPI_KRB5_LIBS) \
+     libsss_test_common.la \
+     libsss_idmap.la \
+     libsss_certmap.la \
+@@ -4149,6 +4155,28 @@ pam_sss_la_LDFLAGS = \
+     -avoid-version \
+     -Wl,--version-script,$(srcdir)/src/sss_client/sss_pam.exports
+ 
++pamlib_LTLIBRARIES += pam_sss_gss.la
++pam_sss_gss_la_SOURCES = \
++    src/sss_client/pam_sss_gss.c \
++    src/sss_client/common.c \
++    $(NULL)
++
++pam_sss_gss_la_CFLAGS = \
++    $(AM_CFLAGS) \
++    $(GSSAPI_KRB5_CFLAGS) \
++    $(NULL)
++
++pam_sss_gss_la_LIBADD = \
++    $(CLIENT_LIBS) \
++    $(PAM_LIBS) \
++    $(GSSAPI_KRB5_LIBS) \
++    $(NULL)
++
++pam_sss_gss_la_LDFLAGS = \
++    -module \
++    -avoid-version \
++    -Wl,--version-script,$(srcdir)/src/sss_client/pam_sss_gss.exports
++
+ if BUILD_SUDO
+ 
+ libsss_sudo_la_SOURCES = \
+@@ -4187,7 +4215,10 @@ endif
+ 
+ dist_noinst_DATA += \
+     src/sss_client/sss_nss.exports \
+-    src/sss_client/sss_pam.exports
++    src/sss_client/sss_pam.exports \
++    src/sss_client/pam_sss_gss.exports \
++    $(NULL)
++
+ if BUILD_SUDO
+ dist_noinst_DATA += src/sss_client/sss_sudo.exports
+ endif
+diff --git a/configure.ac b/configure.ac
+index 0d24c4b35..75dc81d53 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -182,6 +182,7 @@ m4_include([src/external/libldb.m4])
+ m4_include([src/external/libdhash.m4])
+ m4_include([src/external/libcollection.m4])
+ m4_include([src/external/libini_config.m4])
++m4_include([src/external/libgssapi_krb5.m4])
+ m4_include([src/external/pam.m4])
+ m4_include([src/external/ldap.m4])
+ m4_include([src/external/libpcre.m4])
+diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
+index ed81da535..f7e5ce133 100644
+--- a/contrib/sssd.spec.in
++++ b/contrib/sssd.spec.in
+@@ -1166,6 +1166,7 @@ done
+ %license src/sss_client/COPYING src/sss_client/COPYING.LESSER
+ /%{_lib}/libnss_sss.so.2
+ /%{_lib}/security/pam_sss.so
++/%{_lib}/security/pam_sss_gss.so
+ %{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
+ %{_libdir}/krb5/plugins/authdata/sssd_pac_plugin.so
+ %if (0%{?with_cifs_utils_plugin} == 1)
+@@ -1178,6 +1179,7 @@ done
+ %dir %{_libdir}/%{name}/modules
+ %{_libdir}/%{name}/modules/sssd_krb5_localauth_plugin.so
+ %{_mandir}/man8/pam_sss.8*
++%{_mandir}/man8/pam_sss_gss.8*
+ %{_mandir}/man8/sssd_krb5_locator_plugin.8*
+ 
+ %files -n libsss_sudo
+diff --git a/src/external/libgssapi_krb5.m4 b/src/external/libgssapi_krb5.m4
+new file mode 100644
+index 000000000..67f3c464d
+--- /dev/null
++++ b/src/external/libgssapi_krb5.m4
+@@ -0,0 +1,8 @@
++AC_SUBST(GSSAPI_KRB5_CFLAGS)
++AC_SUBST(GSSAPI_KRB5_LIBS)
++
++PKG_CHECK_MODULES(GSSAPI_KRB5,
++    krb5-gssapi,
++    ,
++    AC_MSG_ERROR("Please install krb5-devel")
++    )
+diff --git a/src/man/Makefile.am b/src/man/Makefile.am
+index 351ab8015..c6890a792 100644
+--- a/src/man/Makefile.am
++++ b/src/man/Makefile.am
+@@ -69,8 +69,8 @@ man_MANS = \
+     sssd.8 sssd.conf.5 sssd-ldap.5 sssd-ldap-attributes.5 \
+     sssd-krb5.5 sssd-simple.5 sss-certmap.5 \
+     sssd_krb5_locator_plugin.8 \
+-    pam_sss.8 sss_obfuscate.8 sss_cache.8 sss_debuglevel.8 sss_seed.8 \
+-    sss_override.8 idmap_sss.8 sssctl.8 sssd-session-recording.5 \
++    pam_sss.8 pam_sss_gss.8 sss_obfuscate.8 sss_cache.8 sss_debuglevel.8 \
++	sss_seed.8 sss_override.8 idmap_sss.8 sssctl.8 sssd-session-recording.5 \
+     $(NULL)
+ 
+ if BUILD_LOCAL_PROVIDER
+diff --git a/src/man/pam_sss_gss.8.xml b/src/man/pam_sss_gss.8.xml
+new file mode 100644
+index 000000000..ce5b11bff
+--- /dev/null
++++ b/src/man/pam_sss_gss.8.xml
+@@ -0,0 +1,209 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
++"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
++<reference>
++<title>SSSD Manual pages</title>
++<refentry>
++    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
++                href="include/upstream.xml" />
++
++    <refmeta>
++        <refentrytitle>pam_sss_gss</refentrytitle>
++        <manvolnum>8</manvolnum>
++    </refmeta>
++
++    <refnamediv id='name'>
++        <refname>pam_sss_gss</refname>
++        <refpurpose>PAM module for SSSD GSSAPI authentication</refpurpose>
++    </refnamediv>
++
++    <refsynopsisdiv id='synopsis'>
++        <cmdsynopsis>
++            <command>pam_sss_gss.so</command>
++            <arg choice='opt'>
++                <replaceable>debug</replaceable>
++            </arg>
++        </cmdsynopsis>
++    </refsynopsisdiv>
++
++    <refsect1 id='description'>
++        <title>DESCRIPTION</title>
++        <para>
++            <command>pam_sss_gss.so</command> authenticates user
++            over GSSAPI in cooperation with SSSD.
++        </para>
++        <para>
++            This module will try to authenticate the user using the GSSAPI
++            hostbased service name host@hostname which translates to
++            host/hostname@REALM Kerberos principal. The
++            <emphasis>REALM</emphasis> part of the Kerberos principal name is
++            derived by Kerberos internal mechanisms and it can be set explicitly
++            in configuration of [domain_realm] section in /etc/krb5.conf.
++        </para>
++        <para>
++            SSSD is used to provide desired service name and to validate the
++            user's credentials using GSSAPI calls. If the service ticket is
++            already present in the Kerberos credentials cache or if user's
++            ticket granting ticket can be used to get the correct service ticket
++            then the user will be authenticated.
++        </para>
++        <para>
++            If <option>pam_gssapi_check_upn</option> is True (default) then SSSD
++            requires that the credentials used to obtain the service tickets can
++            be associated with the user. This means that the principal that owns
++            the Kerberos credentials must match with the user principal name as
++            defined in LDAP.
++        </para>
++        <para>
++            To enable GSSAPI authentication in SSSD, set
++            <option>pam_gssapi_services</option> option in [pam] or domain
++            section of sssd.conf. The service credentials need to be stored
++            in SSSD's keytab (it is already present if you use ipa or ad
++            provider). The keytab location can be set with
++            <option>krb5_keytab</option> option. See
++            <citerefentry>
++                <refentrytitle>sssd.conf</refentrytitle>
++                <manvolnum>5</manvolnum>
++            </citerefentry> and
++            <citerefentry>
++                <refentrytitle>sssd-krb5</refentrytitle>
++                <manvolnum>5</manvolnum>
++            </citerefentry> for more details on these options.
++        </para>
++    </refsect1>
++
++    <refsect1 id='options'>
++        <title>OPTIONS</title>
++        <variablelist remap='IP'>
++            <varlistentry>
++                <term>
++                    <option>debug</option>
++                </term>
++                <listitem>
++                    <para>Print debugging information.</para>
++                </listitem>
++            </varlistentry>
++        </variablelist>
++    </refsect1>
++
++    <refsect1 id='module_types_provides'>
++        <title>MODULE TYPES PROVIDED</title>
++        <para>Only the <option>auth</option> module type is provided.</para>
++    </refsect1>
++
++    <refsect1 id="return_values">
++        <title>RETURN VALUES</title>
++        <variablelist>
++            <varlistentry>
++                <term>PAM_SUCCESS</term>
++                <listitem>
++                    <para>
++                        The PAM operation finished successfully.
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>PAM_USER_UNKNOWN</term>
++                <listitem>
++                    <para>
++                        The user is not known to the authentication service or
++                        the GSSAPI authentication is not supported.
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>PAM_AUTH_ERR</term>
++                <listitem>
++                    <para>
++                        Authentication failure.
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>PAM_AUTHINFO_UNAVAIL</term>
++                <listitem>
++                    <para>
++                        Unable to access the authentication information.
++                        This might be due to a network or hardware failure.
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>PAM_SYSTEM_ERR</term>
++                <listitem>
++                    <para>
++                        A system error occurred. The SSSD log files may contain
++                        additional information about the error.
++                    </para>
++                </listitem>
++            </varlistentry>
++        </variablelist>
++    </refsect1>
++
++    <refsect1 id='examples'>
++        <title>EXAMPLES</title>
++        <para>
++            The main use case is to provide password-less authentication in
++            sudo but without the need to disable authentication completely.
++            To achieve this, first enable GSSAPI authentication for sudo in
++            sssd.conf:
++        </para>
++        <programlisting>
++[domain/MYDOMAIN]
++pam_gssapi_services = sudo, sudo-i
++        </programlisting>
++        <para>
++            And then enable the module in desired PAM stack
++            (e.g. /etc/pam.d/sudo and /etc/pam.d/sudo-i).
++        </para>
++        <programlisting>
++...
++auth sufficient pam_sss_gss.so
++...
++        </programlisting>
++    </refsect1>
++
++    <refsect1 id='troubleshooting'>
++        <title>TROUBLESHOOTING</title>
++        <para>
++            SSSD logs, pam_sss_gss debug output and syslog may contain helpful
++            information about the error. Here are some common issues:
++        </para>
++        <para>
++            1. I have KRB5CCNAME environment variable set and the authentication
++            does not work: Depending on your sudo version, it is possible that
++            sudo does not pass this variable to the PAM environment. Try adding
++            KRB5CCNAME to <option>env_keep</option> in /etc/sudoers or in your
++            LDAP sudo rules default options.
++        </para>
++        <para>
++            2. Authentication does not work and syslog contains "Server not
++            found in Kerberos database": Kerberos is probably not able to
++            resolve correct realm for the service ticket based on the hostname.
++            Try adding the hostname directly to
++            <option>[domain_realm]</option> in /etc/krb5.conf like so:
++        </para>
++        <para>
++            3. Authentication does not work and syslog contains "No Kerberos
++            credentials available": You don't have any credentials that can be
++            used to obtain the required service ticket. Use kinit or autheticate
++            over SSSD to acquire those credentials.
++        </para>
++        <para>
++            4. Authentication does not work and SSSD sssd-pam log contains "User
++            with UPN [$UPN] was not found." or "UPN [$UPN] does not match target
++            user [$username].": You are using credentials that can not be mapped
++            to the user that is being authenticated. Try to use kswitch to
++            select different principal, make sure you authenticated with SSSD or
++            consider disabling <option>pam_gssapi_check_upn</option>.
++        </para>
++        <programlisting>
++[domain_realm]
++.myhostname = MYREALM
++        </programlisting>
++    </refsect1>
++
++    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/seealso.xml" />
++
++</refentry>
++</reference>
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index bf4dd75b0..355329691 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -145,4 +145,8 @@ errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd);
+ 
+ enum pam_initgroups_scheme pam_initgroups_string_to_enum(const char *str);
+ const char *pam_initgroup_enum_to_string(enum pam_initgroups_scheme scheme);
++
++int pam_cmd_gssapi_init(struct cli_ctx *cli_ctx);
++int pam_cmd_gssapi_sec_ctx(struct cli_ctx *cctx);
++
+ #endif /* __PAMSRV_H__ */
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index acbfc0c39..9ea488be4 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -2401,6 +2401,8 @@ struct sss_cmd_table *get_pam_cmds(void)
+         {SSS_PAM_CHAUTHTOK, pam_cmd_chauthtok},
+         {SSS_PAM_CHAUTHTOK_PRELIM, pam_cmd_chauthtok_prelim},
+         {SSS_PAM_PREAUTH, pam_cmd_preauth},
++        {SSS_GSSAPI_INIT, pam_cmd_gssapi_init},
++        {SSS_GSSAPI_SEC_CTX, pam_cmd_gssapi_sec_ctx},
+         {SSS_CLI_NULL, NULL}
+     };
+ 
+diff --git a/src/responder/pam/pamsrv_gssapi.c b/src/responder/pam/pamsrv_gssapi.c
+new file mode 100644
+index 000000000..099675e1c
+--- /dev/null
++++ b/src/responder/pam/pamsrv_gssapi.c
+@@ -0,0 +1,792 @@
++/*
++    Authors:
++        Pavel Březina <pbrezina@redhat.com>
++
++    Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
++*/
++
++#include <errno.h>
++#include <gssapi.h>
++#include <gssapi/gssapi_ext.h>
++#include <gssapi/gssapi_krb5.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <talloc.h>
++#include <ldb.h>
++
++#include "confdb/confdb.h"
++#include "db/sysdb.h"
++#include "responder/common/responder_packet.h"
++#include "responder/common/responder.h"
++#include "responder/common/cache_req/cache_req.h"
++#include "responder/pam/pamsrv.h"
++#include "sss_client/sss_cli.h"
++#include "util/util.h"
++#include "util/sss_utf8.h"
++
++static errno_t read_str(size_t body_len,
++                        uint8_t *body,
++                        size_t *pctr,
++                        const char **_str)
++{
++    size_t i;
++
++    for (i = *pctr; i < body_len && body[i] != 0; i++) {
++        /* counting */
++    }
++
++    if (i >= body_len) {
++        return EINVAL;
++    }
++
++    if (!sss_utf8_check(&body[*pctr], i - *pctr)) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Body is not UTF-8 string!\n");
++        return EINVAL;
++    }
++
++    *_str = (const char *)&body[*pctr];
++    *pctr = i + 1;
++
++    return EOK;
++}
++
++static bool pam_gssapi_should_check_upn(struct pam_ctx *pam_ctx,
++                                        struct sss_domain_info *domain)
++{
++    if (domain->gssapi_check_upn != NULL) {
++        if (strcasecmp(domain->gssapi_check_upn, "true") == 0) {
++            return true;
++        }
++
++        if (strcasecmp(domain->gssapi_check_upn, "false") == 0) {
++            return false;
++        }
++
++        DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: %s\n",
++              CONFDB_PAM_GSSAPI_CHECK_UPN, domain->gssapi_check_upn);
++        return false;
++    }
++
++    return pam_ctx->gssapi_check_upn;
++}
++
++static bool pam_gssapi_allowed(struct pam_ctx *pam_ctx,
++                               struct sss_domain_info *domain,
++                               const char *service)
++{
++    char **list = pam_ctx->gssapi_services;
++
++    if (domain->gssapi_services != NULL) {
++        list = domain->gssapi_services;
++    }
++
++    if (strcmp(service, "-") == 0) {
++        /* Dash is used as a "not set" value to allow to explicitly disable
++         * gssapi auth for specific domain. Disallow this service to be safe.
++         */
++        DEBUG(SSSDBG_TRACE_FUNC, "Dash - was used as a PAM service name. "
++              "GSSAPI authentication is not allowed.\n");
++        return false;
++    }
++
++    return string_in_list(service, list, true);
++}
++
++static char *pam_gssapi_target(TALLOC_CTX *mem_ctx,
++                               struct sss_domain_info *domain)
++{
++    return talloc_asprintf(mem_ctx, "host@%s", domain->hostname);
++}
++
++static const char *pam_gssapi_get_upn(struct cache_req_result *result)
++{
++    if (result->count == 0) {
++        return NULL;
++    }
++
++    /* Canonical UPN should be available if the user has kinited through SSSD.
++     * Use it as a hint for GSSAPI. Default to empty string so it may be
++     * more easily transffered over the wire. */
++    return ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_CANONICAL_UPN, "");
++}
++
++static const char *pam_gssapi_get_name(struct cache_req_result *result)
++{
++    if (result->count == 0) {
++        return NULL;
++    }
++
++    /* Return username known to SSSD to make sure we authenticated as the same
++     * user after GSSAPI handshake. */
++    return ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
++}
++
++static errno_t pam_gssapi_init_parse(struct cli_protocol *pctx,
++                                     const char **_service,
++                                     const char **_username)
++{
++    size_t body_len;
++    size_t pctr = 0;
++    uint8_t *body;
++    errno_t ret;
++
++    sss_packet_get_body(pctx->creq->in, &body, &body_len);
++    if (body == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input\n");
++        return EINVAL;
++    }
++
++    ret = read_str(body_len, body, &pctr, _service);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    ret = read_str(body_len, body, &pctr, _username);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    return EOK;
++}
++
++static errno_t pam_gssapi_init_reply(struct cli_protocol *pctx,
++                                     const char *domain,
++                                     const char *target,
++                                     const char *upn,
++                                     const char *username)
++{
++    size_t reply_len;
++    size_t body_len;
++    size_t pctr;
++    uint8_t *body;
++    errno_t ret;
++
++    ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
++                         &pctx->creq->out);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create a new packet [%d]; %s\n",
++              ret, sss_strerror(ret));
++        return ret;
++    }
++
++    reply_len =  strlen(username) + 1;
++    reply_len += strlen(domain) + 1;
++    reply_len += strlen(target) + 1;
++    reply_len += strlen(upn) + 1;
++
++    ret = sss_packet_grow(pctx->creq->out, reply_len);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create response: %s\n",
++              sss_strerror(ret));
++        return ret;
++    }
++
++    sss_packet_get_body(pctx->creq->out, &body, &body_len);
++
++    pctr = 0;
++    SAFEALIGN_SETMEM_STRING(&body[pctr], username, strlen(username) + 1, &pctr);
++    SAFEALIGN_SETMEM_STRING(&body[pctr], domain, strlen(domain) + 1, &pctr);
++    SAFEALIGN_SETMEM_STRING(&body[pctr], target, strlen(target) + 1, &pctr);
++    SAFEALIGN_SETMEM_STRING(&body[pctr], upn, strlen(upn) + 1, &pctr);
++
++    return EOK;
++}
++
++struct gssapi_init_state {
++    struct cli_ctx *cli_ctx;
++    const char *username;
++    const char *service;
++};
++
++static void pam_cmd_gssapi_init_done(struct tevent_req *req);
++
++int pam_cmd_gssapi_init(struct cli_ctx *cli_ctx)
++{
++    struct gssapi_init_state *state;
++    struct cli_protocol *pctx;
++    struct tevent_req *req;
++    const char *username;
++    const char *service;
++    const char *attrs[] = { SYSDB_NAME, SYSDB_CANONICAL_UPN, NULL };
++    errno_t ret;
++
++    state = talloc_zero(cli_ctx, struct gssapi_init_state);
++    if (state == NULL) {
++        return ENOMEM;
++    }
++
++    pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
++
++    ret = pam_gssapi_init_parse(pctx, &service, &username);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse input [%d]: %s\n",
++              ret, sss_strerror(ret));
++        goto done;
++    }
++
++    state->cli_ctx = cli_ctx;
++    state->service = service;
++    state->username = username;
++
++    DEBUG(SSSDBG_TRACE_ALL,
++          "Requesting GSSAPI authentication of [%s] in service [%s]\n",
++          username, service);
++
++    req = cache_req_user_by_name_attrs_send(cli_ctx, cli_ctx->ev, cli_ctx->rctx,
++                                            cli_ctx->rctx->ncache, 0,
++                                            NULL, username, attrs);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
++        ret = ENOMEM;
++        goto done;
++    }
++
++    tevent_req_set_callback(req, pam_cmd_gssapi_init_done, state);
++
++    ret = EOK;
++
++done:
++    if (ret != EOK) {
++        sss_cmd_send_error(cli_ctx, ret);
++        sss_cmd_done(cli_ctx, NULL);
++    }
++
++    return EOK;
++}
++
++static void pam_cmd_gssapi_init_done(struct tevent_req *req)
++{
++    struct gssapi_init_state *state;
++    struct cache_req_result *result;
++    struct cli_protocol *pctx;
++    struct pam_ctx *pam_ctx;
++    const char *username;
++    const char *upn;
++    char *target;
++    errno_t ret;
++
++    state = tevent_req_callback_data(req, struct gssapi_init_state);
++    pctx = talloc_get_type(state->cli_ctx->protocol_ctx, struct cli_protocol);
++    pam_ctx = talloc_get_type(state->cli_ctx->rctx->pvt_ctx, struct pam_ctx);
++
++    ret = cache_req_user_by_name_attrs_recv(state, req, &result);
++    talloc_zfree(req);
++    if (ret == ENOENT || ret == ERR_DOMAIN_NOT_FOUND) {
++        ret = ENOENT;
++        goto done;
++    } else if (ret != EOK) {
++        goto done;
++    }
++
++    if (!pam_gssapi_allowed(pam_ctx, result->domain, state->service)) {
++        ret = ENOTSUP;
++        goto done;
++    }
++
++    username = pam_gssapi_get_name(result);
++    if (username == NULL) {
++        /* User with no name? */
++        ret = ERR_INTERNAL;
++        goto done;
++    }
++
++    upn = pam_gssapi_get_upn(result);
++    if (upn == NULL) {
++        /* UPN hint may be an empty string, but not NULL. */
++        ret = ERR_INTERNAL;
++        goto done;
++    }
++
++    target = pam_gssapi_target(state, result->domain);
++    if (target == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    DEBUG(SSSDBG_TRACE_FUNC,
++          "Trying GSSAPI auth: User[%s], Domain[%s], UPN[%s], Target[%s]\n",
++          username, result->domain->name, upn, target);
++
++    ret = pam_gssapi_init_reply(pctx, result->domain->name, target, upn,
++                                username);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct reply [%d]: %s\n",
++              ret, sss_strerror(ret));
++        goto done;
++    }
++
++done:
++    DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
++
++    if (ret == EOK) {
++        sss_packet_set_error(pctx->creq->out, EOK);
++    } else {
++        sss_cmd_send_error(state->cli_ctx, ret);
++    }
++
++    sss_cmd_done(state->cli_ctx, state);
++}
++
++static void gssapi_log_status(int type, OM_uint32 status_code)
++{
++    OM_uint32 message_context = 0;
++    gss_buffer_desc buf;
++    OM_uint32 minor;
++
++    do {
++        gss_display_status(&minor, status_code, type, GSS_C_NO_OID,
++                           &message_context, &buf);
++        DEBUG(SSSDBG_OP_FAILURE, "GSSAPI: %.*s\n", (int)buf.length,
++              (char *)buf.value);
++        gss_release_buffer(&minor, &buf);
++    } while (message_context != 0);
++}
++
++static void gssapi_log_error(OM_uint32 major, OM_uint32 minor)
++{
++    gssapi_log_status(GSS_C_GSS_CODE, major);
++    gssapi_log_status(GSS_C_MECH_CODE, minor);
++}
++
++static char *gssapi_get_name(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
++{
++    gss_buffer_desc buf;
++    OM_uint32 major;
++    OM_uint32 minor;
++    char *exported;
++
++    major = gss_display_name(&minor, gss_name, &buf, NULL);
++    if (major != GSS_S_COMPLETE) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export name\n");
++        return NULL;
++    }
++
++    exported = talloc_strndup(mem_ctx, buf.value, buf.length);
++    gss_release_buffer(&minor, &buf);
++
++    if (exported == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
++        return NULL;
++    }
++
++    return exported;
++}
++
++struct gssapi_state {
++    struct cli_ctx *cli_ctx;
++    struct sss_domain_info *domain;
++    const char *username;
++
++    char *authenticated_upn;
++    bool established;
++    gss_ctx_id_t ctx;
++};
++
++int gssapi_state_destructor(struct gssapi_state *state)
++{
++    OM_uint32 minor;
++
++    gss_delete_sec_context(&minor, &state->ctx, NULL);
++
++    return 0;
++}
++
++static struct gssapi_state *gssapi_get_state(struct cli_ctx *cli_ctx,
++                                             const char *username,
++                                             struct sss_domain_info *domain)
++{
++    struct gssapi_state *state;
++
++    state = talloc_get_type(cli_ctx->state_ctx, struct gssapi_state);
++    if (state != NULL) {
++        return state;
++    }
++
++    state = talloc_zero(cli_ctx, struct gssapi_state);
++    if (state == NULL) {
++        return NULL;
++    }
++
++    state->username = talloc_strdup(state, username);
++    if (state == NULL) {
++        talloc_free(state);
++        return NULL;
++    }
++
++    state->domain = domain;
++    state->cli_ctx = cli_ctx;
++    state->ctx = GSS_C_NO_CONTEXT;
++    talloc_set_destructor(state, gssapi_state_destructor);
++
++    cli_ctx->state_ctx = state;
++
++    return state;
++}
++
++static errno_t gssapi_get_creds(const char *keytab,
++                                const char *target,
++                                gss_cred_id_t *_creds)
++{
++    gss_key_value_set_desc cstore = {0, NULL};
++    gss_key_value_element_desc el;
++    gss_buffer_desc name_buf;
++    gss_name_t name = GSS_C_NO_NAME;
++    OM_uint32 major;
++    OM_uint32 minor;
++    errno_t ret;
++
++    if (keytab != NULL) {
++        el.key = "keytab";
++        el.value = keytab;
++        cstore.count = 1;
++        cstore.elements = &el;
++    }
++
++    if (target != NULL) {
++        name_buf.value = discard_const(target);
++        name_buf.length = strlen(target);
++
++        major = gss_import_name(&minor, &name_buf, GSS_C_NT_HOSTBASED_SERVICE,
++                                &name);
++        if (GSS_ERROR(major)) {
++            DEBUG(SSSDBG_OP_FAILURE, "Could not import name [%s] "
++                 "[maj:0x%x, min:0x%x]\n", target, major, minor);
++
++            gssapi_log_error(major, minor);
++
++            ret = EIO;
++            goto done;
++        }
++    }
++
++    major = gss_acquire_cred_from(&minor, name, GSS_C_INDEFINITE,
++                                  GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cstore,
++                                  _creds, NULL, NULL);
++    if (GSS_ERROR(major)) {
++        DEBUG(SSSDBG_OP_FAILURE, "Unable to read credentials from [%s] "
++              "[maj:0x%x, min:0x%x]\n", keytab ? keytab : "default",
++              major, minor);
++
++        gssapi_log_error(major, minor);
++
++        ret = EIO;
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    gss_release_name(&minor, &name);
++
++    return ret;
++}
++
++static errno_t
++gssapi_handshake(struct gssapi_state *state,
++                 struct cli_protocol *pctx,
++                 const char *keytab,
++                 const char *target,
++                 uint8_t *gss_data,
++                 size_t gss_data_len)
++{
++    OM_uint32 flags = GSS_C_MUTUAL_FLAG;
++    gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc input;
++    gss_name_t client_name;
++    gss_cred_id_t creds;
++    OM_uint32 ret_flags;
++    gss_OID mech_type;
++    OM_uint32 major;
++    OM_uint32 minor;
++    errno_t ret;
++
++    input.value = gss_data;
++    input.length = gss_data_len;
++
++    ret = gssapi_get_creds(keytab, target, &creds);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    major = gss_accept_sec_context(&minor, &state->ctx, creds,
++                                   &input, NULL, &client_name, &mech_type,
++                                   &output, &ret_flags, NULL, NULL);
++    if (major == GSS_S_CONTINUE_NEEDED || output.length > 0) {
++        ret = sss_packet_set_body(pctx->creq->out, output.value, output.length);
++        if (ret != EOK) {
++            goto done;
++        }
++    }
++
++    if (GSS_ERROR(major)) {
++        DEBUG(SSSDBG_OP_FAILURE, "Unable to establish GSS context "
++              "[maj:0x%x, min:0x%x]\n", major, minor);
++
++        gssapi_log_error(major, minor);
++        ret = EIO;
++        goto done;
++    }
++
++    if (major == GSS_S_CONTINUE_NEEDED) {
++        ret = EOK;
++        goto done;
++    } else if (major != GSS_S_COMPLETE) {
++        DEBUG(SSSDBG_OP_FAILURE, "Unable to establish GSS context, unexpected "
++              "value: 0x%x\n", major);
++        ret = EIO;
++        goto done;
++    }
++
++    if ((ret_flags & flags) != flags) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++                "Negotiated context does not support requested flags\n");
++        state->established = false;
++        ret = EIO;
++        goto done;
++    }
++
++    state->authenticated_upn = gssapi_get_name(state, client_name);
++    if (state->authenticated_upn == NULL) {
++        state->established = false;
++        ret = ENOMEM;
++        goto done;
++    }
++
++    DEBUG(SSSDBG_TRACE_FUNC, "Security context established with [%s]\n",
++          state->authenticated_upn);
++
++    state->established = true;
++    ret = EOK;
++
++done:
++    gss_release_cred(&minor, &creds);
++    gss_release_buffer(&minor, &output);
++
++    return ret;
++}
++
++static errno_t pam_cmd_gssapi_sec_ctx_parse(struct cli_protocol *pctx,
++                                            const char **_pam_service,
++                                            const char **_username,
++                                            const char **_domain,
++                                            uint8_t **_gss_data,
++                                            size_t *_gss_data_len)
++{
++    size_t body_len;
++    uint8_t *body;
++    size_t pctr;
++    errno_t ret;
++
++    sss_packet_get_body(pctx->creq->in, &body, &body_len);
++    if (body == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input\n");
++        return EINVAL;
++    }
++
++    pctr = 0;
++    ret = read_str(body_len, body, &pctr, _pam_service);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    ret = read_str(body_len, body, &pctr, _username);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    ret = read_str(body_len, body, &pctr, _domain);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    *_gss_data = (pctr == body_len) ? NULL : body + pctr;
++    *_gss_data_len = body_len - pctr;
++
++    return EOK;
++}
++
++static void pam_cmd_gssapi_sec_ctx_done(struct tevent_req *req);
++
++int
++pam_cmd_gssapi_sec_ctx(struct cli_ctx *cli_ctx)
++{
++    struct sss_domain_info *domain;
++    struct gssapi_state *state;
++    struct cli_protocol *pctx;
++    struct pam_ctx *pam_ctx;
++    struct tevent_req *req;
++    const char *pam_service;
++    const char *domain_name;
++    const char *username;
++    char *target;
++    size_t gss_data_len;
++    uint8_t *gss_data;
++    errno_t ret;
++
++    pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
++    pam_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct pam_ctx);
++
++    ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
++                         &pctx->creq->out);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create a new packet [%d]; %s\n",
++              ret, sss_strerror(ret));
++        return ret;
++    }
++
++    ret = pam_cmd_gssapi_sec_ctx_parse(pctx, &pam_service, &username,
++                                       &domain_name, &gss_data, &gss_data_len);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "Unable to parse input data [%d]: %s\n",
++              ret, sss_strerror(ret));
++        goto done;
++    }
++
++    domain = find_domain_by_name(cli_ctx->rctx->domains, domain_name, false);
++    if (domain == NULL) {
++        ret = EINVAL;
++        goto done;
++    }
++
++    if (!pam_gssapi_allowed(pam_ctx, domain, pam_service)) {
++        ret = ENOTSUP;
++        goto done;
++    }
++
++    target = pam_gssapi_target(cli_ctx, domain);
++    if (target == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    state = gssapi_get_state(cli_ctx, username, domain);
++    if (state == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    if (strcmp(username, state->username) != 0 || state->domain != domain) {
++        /* This should not happen, but be paranoid. */
++        DEBUG(SSSDBG_CRIT_FAILURE, "Different input user then who initiated "
++              "the request!\n");
++        ret = EPERM;
++        goto done;
++    }
++
++    if (state->established) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Security context is already established\n");
++        ret = EPERM;
++        goto done;
++    }
++
++    ret = gssapi_handshake(state, pctx, domain->krb5_keytab, target, gss_data,
++                           gss_data_len);
++    if (ret != EOK || !state->established) {
++        goto done;
++    }
++
++    if (!pam_gssapi_should_check_upn(pam_ctx, domain)) {
++        /* We are done. */
++        goto done;
++    }
++
++    /* We have established the security context. Now check the the principal
++     * used for authorization can be associated with the user. We have
++     * already done initgroups before so we could just search the sysdb
++     * directly, but use cache req to avoid looking up a possible expired
++     * object if the handshake took longer. */
++
++    DEBUG(SSSDBG_TRACE_FUNC, "Checking that target user matches UPN\n");
++
++    req = cache_req_user_by_upn_send(cli_ctx, cli_ctx->ev, cli_ctx->rctx,
++                                     cli_ctx->rctx->ncache, 0, DOM_TYPE_POSIX,
++                                     domain->name, state->authenticated_upn);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
++        ret = ENOMEM;
++        goto done;
++    }
++
++    tevent_req_set_callback(req, pam_cmd_gssapi_sec_ctx_done, state);
++
++    return EOK;
++
++done:
++    DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
++
++    if (ret == EOK) {
++        sss_packet_set_error(pctx->creq->out, EOK);
++    } else {
++        sss_cmd_send_error(cli_ctx, ret);
++    }
++
++    sss_cmd_done(cli_ctx, NULL);
++    return EOK;
++}
++
++static void pam_cmd_gssapi_sec_ctx_done(struct tevent_req *req)
++{
++    struct gssapi_state *state;
++    struct cache_req_result *result;
++    struct cli_protocol *pctx;
++    const char *name;
++    errno_t ret;
++
++    state = tevent_req_callback_data(req, struct gssapi_state);
++    pctx = talloc_get_type(state->cli_ctx->protocol_ctx, struct cli_protocol);
++
++    ret = cache_req_user_by_upn_recv(state, req, &result);
++    talloc_zfree(req);
++    if (ret == ENOENT || ret == ERR_DOMAIN_NOT_FOUND) {
++        /* We have no match. Return failure. */
++        DEBUG(SSSDBG_TRACE_FUNC, "User with UPN [%s] was not found. "
++              "Authentication failed.\n", state->authenticated_upn);
++        ret = EACCES;
++        goto done;
++    } else if (ret != EOK) {
++        /* Generic error. Return failure. */
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user by UPN [%d]: %s\n",
++              ret, sss_strerror(ret));
++        goto done;
++    }
++
++    /* Check that username match. */
++    name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
++    if (name == NULL || strcmp(name, state->username) != 0) {
++        DEBUG(SSSDBG_TRACE_FUNC, "UPN [%s] does not match target user [%s]. "
++              "Authentication failed.\n", state->authenticated_upn,
++              state->username);
++        ret = EACCES;
++        goto done;
++    }
++
++    DEBUG(SSSDBG_TRACE_FUNC, "User [%s] match UPN [%s]. Authentication was "
++          "successful.\n", state->username, state->authenticated_upn);
++
++    ret = EOK;
++
++done:
++    DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
++
++    if (ret == EOK) {
++        sss_packet_set_error(pctx->creq->out, EOK);
++    } else {
++        sss_cmd_send_error(state->cli_ctx, ret);
++    }
++
++    sss_cmd_done(state->cli_ctx, state);
++}
+diff --git a/src/sss_client/pam_sss_gss.c b/src/sss_client/pam_sss_gss.c
+new file mode 100644
+index 000000000..cd38db7da
+--- /dev/null
++++ b/src/sss_client/pam_sss_gss.c
+@@ -0,0 +1,588 @@
++/*
++    Authors:
++        Pavel Březina <pbrezina@redhat.com>
++
++    Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
++*/
++
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdbool.h>
++#include <security/pam_modules.h>
++#include <security/pam_ext.h>
++#include <gssapi.h>
++#include <gssapi/gssapi_ext.h>
++#include <gssapi/gssapi_generic.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/syslog.h>
++#include <unistd.h>
++
++#include "util/sss_format.h"
++#include "sss_client/sss_cli.h"
++
++bool debug_enabled;
++
++#define TRACE(pamh, fmt, ...) do { \
++    if (debug_enabled) { \
++        pam_info(pamh, "pam_sss_gss: " fmt, ## __VA_ARGS__); \
++    } \
++} while (0)
++
++#define ERROR(pamh, fmt, ...) do { \
++    if (debug_enabled) { \
++        pam_error(pamh, "pam_sss_gss: " fmt, ## __VA_ARGS__); \
++        pam_syslog(pamh, LOG_ERR, fmt, ## __VA_ARGS__); \
++    } \
++} while (0)
++
++static bool switch_euid(pam_handle_t *pamh, uid_t current, uid_t desired)
++{
++    int ret;
++
++    TRACE(pamh, "Switching euid from %" SPRIuid " to %" SPRIuid, current,
++          desired);
++
++    if (current == desired) {
++        return true;
++    }
++
++    ret = seteuid(desired);
++    if (ret != 0) {
++        ERROR(pamh, "Unable to set euid to %" SPRIuid, desired);
++        return false;
++    }
++
++    return true;
++}
++
++static const char *get_item_as_string(pam_handle_t *pamh, int item)
++{
++    const char *str;
++    int ret;
++
++    ret = pam_get_item(pamh, item, (void *)&str);
++    if (ret != PAM_SUCCESS || str == NULL || str[0] == '\0') {
++        return NULL;
++    }
++
++    return str;
++}
++
++static errno_t string_to_gss_name(pam_handle_t *pamh,
++                                  const char *target,
++                                  gss_OID type,
++                                  gss_name_t *_name)
++{
++    gss_buffer_desc name_buf;
++    OM_uint32 major;
++    OM_uint32 minor;
++
++    name_buf.value = (void *)(uintptr_t)target;
++    name_buf.length = strlen(target);
++    major = gss_import_name(&minor, &name_buf, type, _name);
++    if (GSS_ERROR(major)) {
++        ERROR(pamh, "Could not convert target to GSS name");
++        return EIO;
++    }
++
++    return EOK;
++}
++
++static void gssapi_log_status(pam_handle_t *pamh,
++                              int type,
++                              OM_uint32 status_code)
++{
++    gss_buffer_desc buf;
++    OM_uint32 message_context;
++    OM_uint32 minor;
++
++    message_context = 0;
++    do {
++        gss_display_status(&minor, status_code, type, GSS_C_NO_OID,
++                           &message_context, &buf);
++        ERROR(pamh, "GSSAPI: %.*s", (int)buf.length, (char *)buf.value);
++        gss_release_buffer(&minor, &buf);
++    } while (message_context != 0);
++}
++
++static void gssapi_log_error(pam_handle_t *pamh,
++                             OM_uint32 major,
++                             OM_uint32 minor)
++{
++    gssapi_log_status(pamh, GSS_C_GSS_CODE, major);
++    gssapi_log_status(pamh, GSS_C_MECH_CODE, minor);
++}
++
++static errno_t gssapi_get_creds(pam_handle_t *pamh,
++                                const char *ccache,
++                                const char *target,
++                                const char *upn,
++                                gss_cred_id_t *_creds)
++{
++    gss_key_value_set_desc cstore = {0, NULL};
++    gss_key_value_element_desc el;
++    gss_name_t name = GSS_C_NO_NAME;
++    OM_uint32 major;
++    OM_uint32 minor;
++    errno_t ret;
++
++    if (upn != NULL && upn[0] != '\0') {
++        TRACE(pamh, "Acquiring credentials for principal [%s]", upn);
++        ret = string_to_gss_name(pamh, upn, GSS_C_NT_USER_NAME, &name);
++        if (ret != EOK) {
++            goto done;
++        }
++    } else {
++        TRACE(pamh, "Acquiring credentials, principal name will be derived");
++    }
++
++    if (ccache != NULL) {
++        el.key = "ccache";
++        el.value = ccache;
++        cstore.count = 1;
++        cstore.elements = &el;
++    }
++
++    major = gss_acquire_cred_from(&minor, name, GSS_C_INDEFINITE,
++                                  GSS_C_NO_OID_SET, GSS_C_INITIATE,
++                                  &cstore, _creds, NULL, NULL);
++    if (GSS_ERROR(major)) {
++        /* TODO: Do not hardcode the error code. */
++        if (minor == 2529639053 && name != GSS_C_NO_NAME) {
++            /* Hint principal was not found. Try again and let GSSAPI choose. */
++            TRACE(pamh, "Principal [%s] was not found in ccache", upn);
++            ret = gssapi_get_creds(pamh, ccache, target, NULL, _creds);
++            goto done;
++        } else {
++            ERROR(pamh, "Unable to read credentials from [%s] "
++                  "[maj:0x%x, min:0x%x]", ccache == NULL ? "default" : ccache,
++                  major, minor);
++
++            gssapi_log_error(pamh, major, minor);
++            ret = EIO;
++            goto done;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    gss_release_name(&minor, &name);
++
++    return ret;
++}
++
++static errno_t sssd_gssapi_init_send(pam_handle_t *pamh,
++                                     const char *pam_service,
++                                     const char *pam_user,
++                                     uint8_t **_reply,
++                                     size_t *_reply_len)
++{
++    struct sss_cli_req_data req_data;
++    size_t service_len;
++    size_t user_len;
++    uint8_t *data;
++    errno_t ret;
++    int ret_errno;
++
++    if (pam_service == NULL || pam_user == NULL) {
++        return EINVAL;
++    }
++
++    service_len = strlen(pam_service) + 1;
++    user_len = strlen(pam_user) + 1;
++
++    req_data.len = (service_len + user_len) * sizeof(char);
++    data = (uint8_t*)malloc(req_data.len);
++    if (data == NULL) {
++        return ENOMEM;
++    }
++
++    memcpy(data, pam_service, service_len);
++    memcpy(data + service_len, pam_user, user_len);
++
++    req_data.data = data;
++
++    ret = sss_pam_make_request(SSS_GSSAPI_INIT, &req_data, _reply, _reply_len,
++                               &ret_errno);
++    free(data);
++    if (ret != PAM_SUCCESS) {
++        if (ret_errno == ENOTSUP) {
++            TRACE(pamh, "GSSAPI authentication is not supported for user %s "
++                  "and service %s", pam_user, pam_service);
++            return ret_errno;
++        }
++
++        ERROR(pamh, "Communication error [%d, %d]: %s; %s", ret, ret_errno,
++              pam_strerror(pamh, ret), strerror(ret_errno));
++
++        return (ret_errno != EOK) ? ret_errno : EIO;
++    }
++
++    return ret_errno;
++}
++
++static errno_t sssd_gssapi_init_recv(uint8_t *reply,
++                                     size_t reply_len,
++                                     char **_username,
++                                     char **_domain,
++                                     char **_target,
++                                     char **_upn)
++{
++    char *username = NULL;
++    char *domain = NULL;
++    char *target = NULL;
++    char *upn = NULL;
++    const char *buf;
++    size_t pctr = 0;
++    size_t dlen;
++    errno_t ret;
++
++    username = malloc(reply_len * sizeof(char));
++    domain = malloc(reply_len * sizeof(char));
++    target = malloc(reply_len * sizeof(char));
++    upn = malloc(reply_len * sizeof(char));
++    if (username == NULL || domain == NULL || target == NULL || upn == NULL) {
++        return ENOMEM;
++    }
++
++    buf = (const char*)reply;
++
++    dlen = reply_len;
++    ret = sss_readrep_copy_string(buf, &pctr, &reply_len, &dlen, &username,
++                                  NULL);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    dlen = reply_len;
++    ret = sss_readrep_copy_string(buf, &pctr, &reply_len, &dlen, &domain, NULL);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    dlen = reply_len;
++    ret = sss_readrep_copy_string(buf, &pctr, &reply_len, &dlen, &target, NULL);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    dlen = reply_len;
++    ret = sss_readrep_copy_string(buf, &pctr, &reply_len, &dlen, &upn, NULL);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    *_username = username;
++    *_domain = domain;
++    *_target = target;
++    *_upn = upn;
++
++done:
++    if (ret != EOK) {
++        free(username);
++        free(domain);
++        free(target);
++        free(upn);
++    }
++
++    return ret;
++}
++
++static errno_t sssd_gssapi_init(pam_handle_t *pamh,
++                                const char *pam_service,
++                                const char *pam_user,
++                                char **_username,
++                                char **_domain,
++                                char **_target,
++                                char **_upn)
++{
++    size_t reply_len;
++    uint8_t *reply;
++    errno_t ret;
++
++    ret = sssd_gssapi_init_send(pamh, pam_service, pam_user, &reply,
++                                &reply_len);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    ret = sssd_gssapi_init_recv(reply, reply_len, _username, _domain, _target,
++                                _upn);
++    free(reply);
++
++    return ret;
++}
++
++static errno_t sssd_establish_sec_ctx_send(pam_handle_t *pamh,
++                                           const char *pam_service,
++                                           const char *username,
++                                           const char *domain,
++                                           const void *gss_data,
++                                           size_t gss_data_len,
++                                           void **_reply,
++                                           size_t *_reply_len)
++{
++    struct sss_cli_req_data req_data;
++    size_t username_len;
++    size_t service_len;
++    size_t domain_len;
++    uint8_t *data;
++    int ret_errno;
++    int ret;
++
++    service_len = strlen(pam_service) + 1;
++    username_len = strlen(username) + 1;
++    domain_len = strlen(domain) + 1;
++
++    req_data.len = (service_len + username_len + domain_len) * sizeof(char)
++                   + gss_data_len;
++    data = malloc(req_data.len);
++    if (data == NULL) {
++        return ENOMEM;
++    }
++
++    memcpy(data, pam_service, service_len);
++    memcpy(data + service_len, username, username_len);
++    memcpy(data + service_len + username_len, domain, domain_len);
++    memcpy(data + service_len + username_len + domain_len, gss_data,
++           gss_data_len);
++
++    req_data.data = data;
++    ret = sss_pam_make_request(SSS_GSSAPI_SEC_CTX, &req_data, (uint8_t**)_reply,
++                               _reply_len, &ret_errno);
++    free(data);
++    if (ret != PAM_SUCCESS) {
++        /* ENOTSUP should not happend here so let's keep it as generic error. */
++        ERROR(pamh, "Communication error [%d, %d]: %s; %s", ret, ret_errno,
++              pam_strerror(pamh, ret), strerror(ret_errno));
++
++        return (ret_errno != EOK) ? ret_errno : EIO;
++    }
++
++    return ret_errno;
++}
++
++static int sssd_establish_sec_ctx(pam_handle_t *pamh,
++                                  const char *ccache,
++                                  const char *pam_service,
++                                  const char *username,
++                                  const char *domain,
++                                  const char *target,
++                                  const char *upn)
++{
++    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
++    gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
++    OM_uint32 flags = GSS_C_MUTUAL_FLAG;
++    gss_name_t gss_name;
++    gss_cred_id_t creds;
++    OM_uint32 ret_flags;
++    OM_uint32 major;
++    OM_uint32 minor;
++    int ret;
++
++    ret = gssapi_get_creds(pamh, ccache, target, upn, &creds);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    ret = string_to_gss_name(pamh, target, GSS_C_NT_HOSTBASED_SERVICE, &gss_name);
++    if (ret != 0) {
++        return ret;
++    }
++
++    do {
++        major = gss_init_sec_context(&minor, creds, &ctx,
++                                     gss_name, GSS_C_NO_OID, flags, 0, NULL,
++                                     &input, NULL, &output,
++                                     &ret_flags, NULL);
++
++        free(input.value);
++        memset(&input, 0, sizeof(gss_buffer_desc));
++
++        if (GSS_ERROR(major)) {
++            ERROR(pamh, "Unable to establish GSS context [maj:0x%x, min:0x%x]",
++                  major, minor);
++            gssapi_log_error(pamh, major, minor);
++            ret = EIO;
++            goto done;
++        } else if (major == GSS_S_CONTINUE_NEEDED || output.length > 0) {
++            ret = sssd_establish_sec_ctx_send(pamh, pam_service,
++                                              username, domain,
++                                              output.value, output.length,
++                                              &input.value, &input.length);
++            gss_release_buffer(NULL, &output);
++            if (ret != EOK) {
++                goto done;
++            }
++        }
++    } while (major != GSS_S_COMPLETE);
++
++    if ((ret_flags & flags) != flags) {
++        ERROR(pamh, "Negotiated context does not support requested flags\n");
++        ret = EIO;
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    gss_delete_sec_context(&minor, &ctx, NULL);
++    gss_release_name(&minor, &gss_name);
++
++    return ret;
++}
++
++static int errno_to_pam(pam_handle_t *pamh, errno_t ret)
++{
++    switch (ret) {
++        case EOK:
++            TRACE(pamh, "Authentication successful");
++            return PAM_SUCCESS;
++        case ENOENT:
++            TRACE(pamh, "User not found");
++            return PAM_USER_UNKNOWN;
++        case ENOTSUP:
++            TRACE(pamh, "GSSAPI authentication is not enabled "
++                  "for given user and service");
++            return PAM_USER_UNKNOWN;
++        case ESSS_NO_SOCKET:
++            TRACE(pamh, "SSSD socket does not exist");
++            return PAM_AUTHINFO_UNAVAIL;
++        case EPERM:
++            TRACE(pamh, "Authentication failed");
++            return PAM_AUTH_ERR;
++        default:
++            TRACE(pamh, "System error [%d]: %s",
++                     ret, strerror(ret));
++            return PAM_SYSTEM_ERR;
++    }
++}
++
++int pam_sm_authenticate(pam_handle_t *pamh,
++                        int flags,
++                        int argc,
++                        const char **argv)
++{
++    const char *pam_service;
++    const char *pam_user;
++    const char *ccache;
++    char *username = NULL;
++    char *domain = NULL;
++    char *target = NULL;
++    char *upn = NULL;
++    uid_t uid;
++    uid_t euid;
++    errno_t ret;
++
++    debug_enabled = false;
++    for (int i = 0; i < argc; i++) {
++        if (strcmp(argv[i], "debug") == 0) {
++            debug_enabled = true;
++            break;
++        }
++    }
++
++
++    /* Get non-default ccache if specified, may be NULL. */
++    ccache = getenv("KRB5CCNAME");
++
++    uid = getuid();
++    euid = geteuid();
++
++    /* Read PAM data. */
++    pam_service = get_item_as_string(pamh, PAM_SERVICE);
++    pam_user = get_item_as_string(pamh, PAM_USER);
++    if (pam_service == NULL || pam_user == NULL) {
++        ERROR(pamh, "Unable to get PAM data!");
++        ret = EINVAL;
++        goto done;
++    }
++
++    /* Initialize GSSAPI authentication with SSSD. Get user domain
++     * and target GSS service name. */
++    TRACE(pamh, "Initializing GSSAPI authentication with SSSD");
++    ret = sssd_gssapi_init(pamh, pam_service, pam_user, &username, &domain,
++                           &target, &upn);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    /* PAM is often called from set-user-id applications (sudo, su). we want to
++     * make sure that we access credentials of the caller (real uid). */
++    if (!switch_euid(pamh, euid, uid)) {
++        ret = EFAULT;
++        goto done;
++    }
++
++    /* Authenticate the user by estabilishing security context. Authorization is
++     * expected to be done by other modules through pam_access. */
++    TRACE(pamh, "Trying to establish security context");
++    TRACE(pamh, "SSSD User name: %s", username);
++    TRACE(pamh, "User domain: %s", domain);
++    TRACE(pamh, "User principal: %s", upn);
++    TRACE(pamh, "Target name: %s", target);
++    TRACE(pamh, "Using ccache: %s", ccache == NULL ? "default" : ccache);
++    ret = sssd_establish_sec_ctx(pamh, ccache, pam_service,
++                                 username, domain, target, upn);
++
++    /* Restore original euid. */
++    if (!switch_euid(pamh, uid, euid)) {
++        ret = EFAULT;
++        goto done;
++    }
++
++done:
++    sss_pam_close_fd();
++    free(domain);
++    free(target);
++    free(upn);
++
++    return errno_to_pam(pamh, ret);
++}
++
++int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
++{
++    return PAM_IGNORE;
++}
++
++int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
++{
++    return PAM_IGNORE;
++}
++
++int pam_sm_open_session(pam_handle_t *pamh,
++                        int flags,
++                        int argc,
++                        const char **argv)
++{
++    return PAM_IGNORE;
++}
++
++int pam_sm_close_session(pam_handle_t *pamh,
++                         int flags,
++                         int argc,
++                         const char **argv)
++{
++    return PAM_IGNORE;
++}
++
++int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
++{
++    return PAM_IGNORE;
++}
+diff --git a/src/sss_client/pam_sss_gss.exports b/src/sss_client/pam_sss_gss.exports
+new file mode 100644
+index 000000000..9afa106be
+--- /dev/null
++++ b/src/sss_client/pam_sss_gss.exports
+@@ -0,0 +1,4 @@
++{
++    global:
++    *;
++};
+diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
+index d897f43b7..2c3c71bc4 100644
+--- a/src/sss_client/sss_cli.h
++++ b/src/sss_client/sss_cli.h
+@@ -233,6 +233,8 @@ enum sss_cli_command {
+                                         * an authentication request to find
+                                         * out which authentication methods
+                                         * are available for the given user. */
++    SSS_GSSAPI_INIT          = 0x00FA, /**< Initialize GSSAPI authentication. */
++    SSS_GSSAPI_SEC_CTX       = 0x00FB, /**< Establish GSSAPI security ctx. */
+ 
+ /* PAC responder calls */
+     SSS_PAC_ADD_PAC_USER     = 0x0101,
+@@ -721,4 +723,10 @@ errno_t sss_readrep_copy_string(const char *in,
+                                 char **out,
+                                 size_t *size);
+ 
++enum pam_gssapi_cmd {
++    PAM_GSSAPI_GET_NAME,
++    PAM_GSSAPI_INIT,
++    PAM_GSSAPI_SENTINEL
++};
++
+ #endif /* _SSSCLI_H */
+diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c
+index ccf52abe9..bffa02188 100644
+--- a/src/tests/dlopen-tests.c
++++ b/src/tests/dlopen-tests.c
+@@ -47,6 +47,7 @@ struct so {
+     { "libnss_sss.so", { LIBPFX"libnss_sss.so", NULL } },
+     { "libsss_certmap.so", { LIBPFX"libsss_certmap.so", NULL } },
+     { "pam_sss.so", { LIBPFX"pam_sss.so", NULL } },
++    { "pam_sss_gss.so", { LIBPFX"pam_sss_gss.so", NULL } },
+ #ifdef BUILD_WITH_LIBSECRET
+     { "libsss_secrets.so", { LIBPFX"libsss_secrets.so", NULL } },
+ #endif /* BUILD_WITH_LIBSECRET */
+-- 
+2.21.3
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index 26bb823..a05bfb9 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -26,7 +26,7 @@
 
 Name: sssd
 Version: 2.4.0
-Release: 3%{?dist}
+Release: 5%{?dist}
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -42,6 +42,25 @@ Patch0005: 0005-negcache-make-sure-domain-config-does-not-leak-into-.patch
 Patch0006: 0006-utils-add-SSS_GND_SUBDOMAINS-flag-for-get_next_domai.patch
 Patch0007: 0007-negcache-make-sure-short-names-are-added-to-sub-doma.patch
 Patch0008: 0008-negcache-do-not-use-default_domain_suffix.patch
+Patch0009: 0009-kcm-decode-base64-encoded-secret-on-upgrade-path.patch
+Patch0010: 0010-nss-check-if-groups-are-filtered-during-initgroups.patch
+Patch0011: 0011-ifp-fix-use-after-free.patch
+Patch0012: 0012-ifp-fix-original-fix-use-after-free.patch
+Patch0013: 0013-pam_sss-use-unique-id-for-gdm-choice-list.patch
+Patch0014: 0014-authtok-add-label-to-Smartcard-token.patch
+Patch0015: 0015-pam_sss-add-certificate-label-to-reply-to-pam_sss.patch
+Patch0016: 0016-add-tests-multiple-certs-same-id.patch
+Patch0017: 0017-data_provider_be-Add-random-offset-default.patch
+Patch0018: 0018-data_provider_be-MAN-page-update.patch
+Patch0019: 0019-logs-review.patch
+Patch0020: 0020-sss_format.h-include-config.h.patch
+Patch0021: 0021-packet-add-sss_packet_set_body.patch
+Patch0022: 0022-domain-store-hostname-and-keytab-path.patch
+Patch0023: 0023-cache_req-add-helper-to-call-user-by-upn-search.patch
+Patch0024: 0024-pam-fix-typo-in-debug-message.patch
+Patch0025: 0025-pam-add-pam_gssapi_services-option.patch
+Patch0026: 0026-pam-add-pam_gssapi_check_upn-option.patch
+Patch0027: 0027-pam-add-pam_sss_gss-module-for-gssapi-authentication.patch
 
 ### Downstream Patches ###
 
@@ -940,6 +959,7 @@ done
 %license src/sss_client/COPYING src/sss_client/COPYING.LESSER
 %{_libdir}/libnss_sss.so.2
 %{_libdir}/security/pam_sss.so
+%{_libdir}/security/pam_sss_gss.so
 %{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
 %{_libdir}/krb5/plugins/authdata/sssd_pac_plugin.so
 %dir %{_libdir}/cifs-utils
@@ -950,6 +970,7 @@ done
 %dir %{_libdir}/%{name}/modules
 %{_libdir}/%{name}/modules/sssd_krb5_localauth_plugin.so
 %{_mandir}/man8/pam_sss.8*
+%{_mandir}/man8/pam_sss_gss.8*
 %{_mandir}/man8/sssd_krb5_locator_plugin.8*
 
 %files -n libsss_sudo
@@ -1215,6 +1236,17 @@ fi
                                 %{_libdir}/%{name}/modules/libwbclient.so
 
 %changelog
+* Mon Dec 28 2020 Alexey Tikhonov <atikhono@redhat.com> - 2.4.0-5
+- Resolves: rhbz#1631410 - Can't login with smartcard with multiple certs having same ID value
+- Resolves: rhbz#1884213 - [RFE] add offline_timeout_max config option to control offline interval backoff (additional patches)
+- Resolves: rhbz#1893159 - Default debug level should report all errors / failures
+- Resolves: rhbz#1893698 - [RFE] sudo kerberos authentication
+
+* Mon Dec 21 2020 Alexey Tikhonov <atikhono@redhat.com> - 2.4.0-4
+- Resolves: rhbz#1876514 - High CPU utilization by the sssd_kcm process
+- Resolves: rhbz#1876658 - filter_groups option partially filters the group from 'id' output of the user because gidNumber still appears in 'id' output [RHEL 8]
+- Resolves: rhbz#1895001 - User lookups over the InfoPipe responder fail intermittently
+
 * Mon Dec 07 2020 Alexey Tikhonov <atikhono@redhat.com> - 2.4.0-3
 - Resolves: rhbz#1900733 - sssd_be segfaults at be_refresh_get_values_ex() due to NULL ptrs in results of sysdb_search_with_ts_attr()
 - Resolves: rhbz#1876514 - High CPU utilization by the sssd_kcm process