From ec16b2b854278690d13a9b8a029161c3fe2066e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 11:45:54 +0200
Subject: [PATCH 01/20] IPA_ACCESS: Remove not used attribute
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

struct time_rules_ctx * is not used anywhere in in the access handler,
thus there's no need to store it.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_access.c | 2 --
 src/providers/ipa/ipa_access.h | 1 -
 2 files changed, 3 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 9682613e9..67838cd92 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -90,7 +90,6 @@ struct ipa_fetch_hbac_state {
     struct ipa_access_ctx *access_ctx;
     struct sdap_id_op *sdap_op;
     struct dp_option *ipa_options;
-    struct time_rules_ctx *tr_ctx;
 
     struct sdap_search_base **search_bases;
 
@@ -146,7 +145,6 @@ ipa_fetch_hbac_send(TALLOC_CTX *mem_ctx,
     state->access_ctx = access_ctx;
     state->sdap_ctx = access_ctx->sdap_ctx;
     state->ipa_options = access_ctx->ipa_options;
-    state->tr_ctx = access_ctx->tr_ctx;
     state->search_bases = access_ctx->hbac_search_bases;
 
     if (state->search_bases == NULL) {
diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
index 1e30a89a0..eb19fc438 100644
--- a/src/providers/ipa/ipa_access.h
+++ b/src/providers/ipa/ipa_access.h
@@ -35,7 +35,6 @@ enum ipa_access_mode {
 struct ipa_access_ctx {
     struct sdap_id_ctx *sdap_ctx;
     struct dp_option *ipa_options;
-    struct time_rules_ctx *tr_ctx;
     time_t last_update;
     struct sdap_access_ctx *sdap_access_ctx;
 

From b9114ff5c390b05cb9f5fe28bf10e0bc178c1076 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 12:40:59 +0200
Subject: [PATCH 02/20] IPA: Make ipa_hbac_sysdb_save() more generic
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Although there's no change in the ipa_hbac_sysdb_save() itself, its name
has been changed to ipa_common_entries_and_groups_sysdb_save() and its
been split out from HBAC related files and moved to the newly created
ipa_rules_common.[ch] files, which will also be used in the future for
new backend modules.

ipa_rules_common.[ch] is not exactly the best name for those files, IMO,
but I really cannot come up with something better.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 Makefile.am                          |   2 +
 src/providers/ipa/ipa_access.c       |  37 +++++---
 src/providers/ipa/ipa_hbac_common.c  | 134 ----------------------------
 src/providers/ipa/ipa_hbac_private.h |   7 --
 src/providers/ipa/ipa_rules_common.c | 163 +++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_rules_common.h |  40 +++++++++
 6 files changed, 230 insertions(+), 153 deletions(-)
 create mode 100644 src/providers/ipa/ipa_rules_common.c
 create mode 100644 src/providers/ipa/ipa_rules_common.h

diff --git a/Makefile.am b/Makefile.am
index faa2fbaba..161db1980 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3890,6 +3890,8 @@ libsss_ipa_la_SOURCES = \
     src/providers/ipa/ipa_hbac_services.c \
     src/providers/ipa/ipa_hbac_users.c \
     src/providers/ipa/ipa_hbac_common.c \
+    src/providers/ipa/ipa_rules_common.c \
+    src/providers/ipa/ipa_rules_common.h \
     src/providers/ipa/ipa_srv.c \
     src/providers/ipa/ipa_idmap.c \
     src/providers/ipa/ipa_dn.c \
diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 67838cd92..262f146da 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -32,6 +32,7 @@
 #include "providers/ipa/ipa_hosts.h"
 #include "providers/ipa/ipa_hbac_private.h"
 #include "providers/ipa/ipa_hbac_rules.h"
+#include "providers/ipa/ipa_rules_common.h"
 
 /* External logging function for HBAC. */
 void hbac_debug_messages(const char *file, int line,
@@ -515,10 +516,15 @@ static errno_t ipa_save_hbac(struct sss_domain_info *domain,
     in_transaction = true;
 
     /* Save the hosts */
-    ret = ipa_hbac_sysdb_save(domain, HBAC_HOSTS_SUBDIR, SYSDB_FQDN,
-                              state->host_count, state->hosts,
-                              HBAC_HOSTGROUPS_SUBDIR, SYSDB_NAME,
-                              state->hostgroup_count, state->hostgroups);
+    ret = ipa_common_entries_and_groups_sysdb_save(domain,
+                                                   HBAC_HOSTS_SUBDIR,
+                                                   SYSDB_FQDN,
+                                                   state->host_count,
+                                                   state->hosts,
+                                                   HBAC_HOSTGROUPS_SUBDIR,
+                                                   SYSDB_NAME,
+                                                   state->hostgroup_count,
+                                                   state->hostgroups);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts [%d]: %s\n",
               ret, sss_strerror(ret));
@@ -526,20 +532,27 @@ static errno_t ipa_save_hbac(struct sss_domain_info *domain,
     }
 
     /* Save the services */
-    ret = ipa_hbac_sysdb_save(domain, HBAC_SERVICES_SUBDIR, IPA_CN,
-                              state->service_count, state->services,
-                              HBAC_SERVICEGROUPS_SUBDIR, IPA_CN,
-                              state->servicegroup_count,
-                              state->servicegroups);
+    ret = ipa_common_entries_and_groups_sysdb_save(domain,
+                                                   HBAC_SERVICES_SUBDIR,
+                                                   IPA_CN,
+                                                   state->service_count,
+                                                   state->services,
+                                                   HBAC_SERVICEGROUPS_SUBDIR,
+                                                   IPA_CN,
+                                                   state->servicegroup_count,
+                                                   state->servicegroups);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services [%d]: %s\n",
               ret, sss_strerror(ret));
         goto done;
     }
     /* Save the rules */
-    ret = ipa_hbac_sysdb_save(domain, HBAC_RULES_SUBDIR, IPA_UNIQUE_ID,
-                              state->rule_count, state->rules,
-                              NULL, NULL, 0, NULL);
+    ret = ipa_common_entries_and_groups_sysdb_save(domain,
+                                                   HBAC_RULES_SUBDIR,
+                                                   IPA_UNIQUE_ID,
+                                                   state->rule_count,
+                                                   state->rules,
+                                                   NULL, NULL, 0, NULL);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules [%d]: %s\n",
               ret, sss_strerror(ret));
diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
index ba677965a..3866ee238 100644
--- a/src/providers/ipa/ipa_hbac_common.c
+++ b/src/providers/ipa/ipa_hbac_common.c
@@ -23,140 +23,6 @@
 #include "providers/ipa/ipa_hbac_private.h"
 #include "providers/ipa/ipa_common.h"
 
-static errno_t
-ipa_hbac_save_list(struct sss_domain_info *domain,
-                   bool delete_subdir, const char *subdir,
-                   const char *naming_attribute, size_t count,
-                   struct sysdb_attrs **list)
-{
-    int ret;
-    size_t c;
-    struct ldb_dn *base_dn;
-    const char *object_name;
-    struct ldb_message_element *el;
-    TALLOC_CTX *tmp_ctx;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    if (delete_subdir) {
-        base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, subdir);
-        if (base_dn == NULL) {
-            ret = ENOMEM;
-            goto done;
-        }
-
-        ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
-            goto done;
-        }
-    }
-
-    for (c = 0; c < count; c++) {
-        ret = sysdb_attrs_get_el(list[c], naming_attribute, &el);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_el failed.\n");
-            goto done;
-        }
-        if (el->num_values == 0) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "[%s] not found.\n", naming_attribute);
-            ret = EINVAL;
-            goto done;
-        }
-        object_name = talloc_strndup(tmp_ctx, (const char *)el->values[0].data,
-                                     el->values[0].length);
-        if (object_name == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
-            ret = ENOMEM;
-            goto done;
-        }
-        DEBUG(SSSDBG_TRACE_ALL, "Object name: [%s].\n", object_name);
-
-        ret = sysdb_store_custom(domain, object_name, subdir, list[c]);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_store_custom failed.\n");
-            goto done;
-        }
-    }
-
-    ret = EOK;
-
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-errno_t
-ipa_hbac_sysdb_save(struct sss_domain_info *domain,
-                    const char *primary_subdir, const char *attr_name,
-                    size_t primary_count, struct sysdb_attrs **primary,
-                    const char *group_subdir, const char *groupattr_name,
-                    size_t group_count, struct sysdb_attrs **groups)
-{
-    errno_t ret, sret;
-    bool in_transaction = false;
-
-    if ((primary_count == 0 || primary == NULL)
-        || (group_count > 0 && groups == NULL)) {
-        /* There always has to be at least one
-         * primary entry.
-         */
-        return EINVAL;
-    }
-
-    /* Save the entries and groups to the cache */
-    ret = sysdb_transaction_start(domain->sysdb);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
-        goto done;
-    };
-    in_transaction = true;
-
-    /* First, save the specific entries */
-    ret = ipa_hbac_save_list(domain, true, primary_subdir,
-                             attr_name, primary_count, primary);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not save %s. [%d][%s]\n",
-                  primary_subdir, ret, strerror(ret));
-        goto done;
-    }
-
-    /* Second, save the groups */
-    if (group_count > 0) {
-        ret = ipa_hbac_save_list(domain, true, group_subdir,
-                                 groupattr_name, group_count, groups);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "Could not save %s. [%d][%s]\n",
-                      group_subdir, ret, strerror(ret));
-            goto done;
-        }
-    }
-
-    ret = sysdb_transaction_commit(domain->sysdb);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
-        goto done;
-    }
-    in_transaction = false;
-
-done:
-    if (in_transaction) {
-        sret = sysdb_transaction_cancel(domain->sysdb);
-        if (sret != EOK) {
-            DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel sysdb transaction\n");
-        }
-    }
-
-    if (ret != EOK) {
-        DEBUG(SSSDBG_MINOR_FAILURE, "Error [%d][%s]\n", ret, strerror(ret));
-    }
-    return ret;
-}
-
 errno_t
 replace_attribute_name(const char *old_name,
                        const char *new_name, const size_t count,
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
index 8fc5dc6d0..ca0bd4710 100644
--- a/src/providers/ipa/ipa_hbac_private.h
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -66,13 +66,6 @@
 
 /* From ipa_hbac_common.c */
 errno_t
-ipa_hbac_sysdb_save(struct sss_domain_info *domain,
-                    const char *primary_subdir, const char *attr_name,
-                    size_t primary_count, struct sysdb_attrs **primary,
-                    const char *group_subdir, const char *groupattr_name,
-                    size_t group_count, struct sysdb_attrs **groups);
-
-errno_t
 replace_attribute_name(const char *old_name,
                        const char *new_name, const size_t count,
                        struct sysdb_attrs **list);
diff --git a/src/providers/ipa/ipa_rules_common.c b/src/providers/ipa/ipa_rules_common.c
new file mode 100644
index 000000000..056d04dd1
--- /dev/null
+++ b/src/providers/ipa/ipa_rules_common.c
@@ -0,0 +1,163 @@
+/*
+    SSSD
+
+    Authors:
+        Stephen Gallagher <sgallagh@redhat.com>
+
+    Copyright (C) 2011 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 "providers/ipa/ipa_rules_common.h"
+
+static errno_t
+ipa_common_save_list(struct sss_domain_info *domain,
+                     bool delete_subdir,
+                     const char *subdir,
+                     const char *naming_attribute,
+                     size_t count,
+                     struct sysdb_attrs **list)
+{
+    int ret;
+    size_t c;
+    struct ldb_dn *base_dn;
+    const char *object_name;
+    struct ldb_message_element *el;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+        return ENOMEM;
+    }
+
+    if (delete_subdir) {
+        base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, subdir);
+        if (base_dn == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
+            goto done;
+        }
+    }
+
+    for (c = 0; c < count; c++) {
+        ret = sysdb_attrs_get_el(list[c], naming_attribute, &el);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_el failed.\n");
+            goto done;
+        }
+        if (el->num_values == 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "[%s] not found.\n", naming_attribute);
+            ret = EINVAL;
+            goto done;
+        }
+        object_name = talloc_strndup(tmp_ctx, (const char *)el->values[0].data,
+                                     el->values[0].length);
+        if (object_name == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
+            ret = ENOMEM;
+            goto done;
+        }
+        DEBUG(SSSDBG_TRACE_ALL, "Object name: [%s].\n", object_name);
+
+        ret = sysdb_store_custom(domain, object_name, subdir, list[c]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_store_custom failed.\n");
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+errno_t
+ipa_common_entries_and_groups_sysdb_save(struct sss_domain_info *domain,
+                                         const char *primary_subdir,
+                                         const char *attr_name,
+                                         size_t primary_count,
+                                         struct sysdb_attrs **primary,
+                                         const char *group_subdir,
+                                         const char *groupattr_name,
+                                         size_t group_count,
+                                         struct sysdb_attrs **groups)
+{
+    errno_t ret, sret;
+    bool in_transaction = false;
+
+    if ((primary_count == 0 || primary == NULL)
+        || (group_count > 0 && groups == NULL)) {
+        /* There always has to be at least one
+         * primary entry.
+         */
+        return EINVAL;
+    }
+
+    /* Save the entries and groups to the cache */
+    ret = sysdb_transaction_start(domain->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+        goto done;
+    };
+    in_transaction = true;
+
+    /* First, save the specific entries */
+    ret = ipa_common_save_list(domain, true, primary_subdir,
+                               attr_name, primary_count, primary);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not save %s. [%d][%s]\n",
+                  primary_subdir, ret, strerror(ret));
+        goto done;
+    }
+
+    /* Second, save the groups */
+    if (group_count > 0) {
+        ret = ipa_common_save_list(domain, true, group_subdir,
+                                   groupattr_name, group_count, groups);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not save %s. [%d][%s]\n",
+                      group_subdir, ret, strerror(ret));
+            goto done;
+        }
+    }
+
+    ret = sysdb_transaction_commit(domain->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+        goto done;
+    }
+    in_transaction = false;
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(domain->sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel sysdb transaction\n");
+        }
+    }
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Error [%d][%s]\n", ret, strerror(ret));
+    }
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_rules_common.h b/src/providers/ipa/ipa_rules_common.h
new file mode 100644
index 000000000..38a6ba3a5
--- /dev/null
+++ b/src/providers/ipa/ipa_rules_common.h
@@ -0,0 +1,40 @@
+/*
+    SSSD
+
+    Authors:
+        Stephen Gallagher <sgallagh@redhat.com>
+
+    Copyright (C) 2011 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/>.
+*/
+
+#ifndef IPA_RULES_COMMON_H_
+#define IPA_RULES_COMMON_H_
+
+#include "providers/backend.h"
+
+/* From ipa_rules_common.c */
+errno_t
+ipa_common_entries_and_groups_sysdb_save(struct sss_domain_info *domain,
+                                         const char *primary_subdir,
+                                         const char *attr_name,
+                                         size_t primary_count,
+                                         struct sysdb_attrs **primary,
+                                         const char *group_subdir,
+                                         const char *groupattr_name,
+                                         size_t group_count,
+                                         struct sysdb_attrs **groups);
+
+#endif /* IPA_RULES_COMMON_H_ */

From 404544a29ef2f88bdcb96cd555fe4f0c6ae58841 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 13:00:25 +0200
Subject: [PATCH 03/20] IPA: Leave only HBAC specific defines in
 ipa_hbac_private.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The defines that were moved can and will be used by another backend
module that will be introduced in the near future.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_common.c   |  1 +
 src/providers/ipa/ipa_hbac_hosts.c    |  1 +
 src/providers/ipa/ipa_hbac_private.h  | 11 -----------
 src/providers/ipa/ipa_hbac_rules.c    |  1 +
 src/providers/ipa/ipa_hbac_services.c |  1 +
 src/providers/ipa/ipa_hbac_users.c    |  1 +
 src/providers/ipa/ipa_rules_common.h  | 12 ++++++++++++
 src/providers/ipa/ipa_selinux.c       |  1 +
 8 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
index 3866ee238..2dba31496 100644
--- a/src/providers/ipa/ipa_hbac_common.c
+++ b/src/providers/ipa/ipa_hbac_common.c
@@ -22,6 +22,7 @@
 
 #include "providers/ipa/ipa_hbac_private.h"
 #include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_rules_common.h"
 
 errno_t
 replace_attribute_name(const char *old_name,
diff --git a/src/providers/ipa/ipa_hbac_hosts.c b/src/providers/ipa/ipa_hbac_hosts.c
index d331cdfab..74d91e513 100644
--- a/src/providers/ipa/ipa_hbac_hosts.c
+++ b/src/providers/ipa/ipa_hbac_hosts.c
@@ -23,6 +23,7 @@
 #include "util/util.h"
 #include "db/sysdb.h"
 #include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ipa/ipa_rules_common.h"
 #include "providers/ldap/sdap_async.h"
 
 /*
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
index ca0bd4710..7d8b1ed2f 100644
--- a/src/providers/ipa/ipa_hbac_private.h
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -31,29 +31,18 @@
 #define IPA_HBAC_SERVICE "ipaHBACService"
 #define IPA_HBAC_SERVICE_GROUP "ipaHBACServiceGroup"
 
-#define IPA_UNIQUE_ID "ipauniqueid"
-
 #define IPA_MEMBER "member"
 #define HBAC_HOSTS_SUBDIR "hbac_hosts"
 #define HBAC_HOSTGROUPS_SUBDIR "hbac_hostgroups"
 
-#define OBJECTCLASS "objectclass"
 #define IPA_MEMBEROF "memberOf"
 #define IPA_ACCESS_RULE_TYPE "accessRuleType"
 #define IPA_HBAC_ALLOW "allow"
-#define IPA_MEMBER_USER "memberUser"
-#define IPA_USER_CATEGORY "userCategory"
 #define IPA_SERVICE_NAME "serviceName"
 #define IPA_SOURCE_HOST "sourceHost"
 #define IPA_SOURCE_HOST_CATEGORY "sourceHostCategory"
-#define IPA_EXTERNAL_HOST "externalHost"
-#define IPA_ENABLED_FLAG "ipaenabledflag"
-#define IPA_MEMBER_HOST "memberHost"
-#define IPA_HOST_CATEGORY "hostCategory"
-#define IPA_CN "cn"
 #define IPA_MEMBER_SERVICE "memberService"
 #define IPA_SERVICE_CATEGORY "serviceCategory"
-#define IPA_TRUE_VALUE "TRUE"
 
 #define IPA_HBAC_BASE_TMPL "cn=hbac,%s"
 #define IPA_SERVICES_BASE_TMPL "cn=hbacservices,cn=accounts,%s"
diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 7912dbec9..c860905cc 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -21,6 +21,7 @@
 */
 
 #include "util/util.h"
+#include "providers/ipa/ipa_rules_common.h"
 #include "providers/ipa/ipa_hbac_private.h"
 #include "providers/ipa/ipa_hbac_rules.h"
 #include "providers/ldap/sdap_async.h"
diff --git a/src/providers/ipa/ipa_hbac_services.c b/src/providers/ipa/ipa_hbac_services.c
index cf8ce84bf..79088ff66 100644
--- a/src/providers/ipa/ipa_hbac_services.c
+++ b/src/providers/ipa/ipa_hbac_services.c
@@ -21,6 +21,7 @@
 */
 
 #include "util/util.h"
+#include "providers/ipa/ipa_rules_common.h"
 #include "providers/ipa/ipa_hbac_private.h"
 #include "providers/ldap/sdap_async.h"
 
diff --git a/src/providers/ipa/ipa_hbac_users.c b/src/providers/ipa/ipa_hbac_users.c
index 0881647c2..af53fa035 100644
--- a/src/providers/ipa/ipa_hbac_users.c
+++ b/src/providers/ipa/ipa_hbac_users.c
@@ -21,6 +21,7 @@
 */
 
 #include "util/util.h"
+#include "providers/ipa/ipa_rules_common.h"
 #include "providers/ipa/ipa_hbac_private.h"
 #include "providers/ldap/sdap_async.h"
 
diff --git a/src/providers/ipa/ipa_rules_common.h b/src/providers/ipa/ipa_rules_common.h
index 38a6ba3a5..b5e05b039 100644
--- a/src/providers/ipa/ipa_rules_common.h
+++ b/src/providers/ipa/ipa_rules_common.h
@@ -25,6 +25,18 @@
 
 #include "providers/backend.h"
 
+#define IPA_UNIQUE_ID "ipauniqueid"
+
+#define OBJECTCLASS "objectclass"
+#define IPA_MEMBER_USER "memberUser"
+#define IPA_USER_CATEGORY "userCategory"
+#define IPA_EXTERNAL_HOST "externalHost"
+#define IPA_ENABLED_FLAG "ipaenabledflag"
+#define IPA_MEMBER_HOST "memberHost"
+#define IPA_HOST_CATEGORY "hostCategory"
+#define IPA_CN "cn"
+#define IPA_TRUE_VALUE "TRUE"
+
 /* From ipa_rules_common.c */
 errno_t
 ipa_common_entries_and_groups_sysdb_save(struct sss_domain_info *domain,
diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index 6d0778d78..e9cc33953 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -36,6 +36,7 @@
 #include "providers/ipa/ipa_access.h"
 #include "providers/ipa/ipa_selinux_maps.h"
 #include "providers/ipa/ipa_subdomains.h"
+#include "providers/ipa/ipa_rules_common.h"
 
 #ifndef SELINUX_CHILD_DIR
 #ifndef SSSD_LIBEXEC_PATH

From 7019f3e5e4a63feb7224101c2ab1160ed9a9e86f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 13:49:48 +0200
Subject: [PATCH 04/20] IPA_ACCESS: Make hbac_get_cache_rules() more generic
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This method can also be reused in the future for new backend modules.

In order to make it more generic, let's just move it to
ipa_rules_common.[ch], rename it to ipa_common_get_cached_rules() and
make the rule, subtree name and the attributes to be searched new
parameters of this method.

In order to not be declaring the enourmous list of attributes HBAC uses
when calling this method, a new hbac_get_attrs_to_get_cached_rules()
method has been introduced.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_access.c       | 77 ++++++------------------------------
 src/providers/ipa/ipa_access.h       |  5 ---
 src/providers/ipa/ipa_hbac_common.c  | 30 ++++++++++++++
 src/providers/ipa/ipa_hbac_private.h |  3 ++
 src/providers/ipa/ipa_rules_common.c | 61 ++++++++++++++++++++++++++++
 src/providers/ipa/ipa_rules_common.h |  9 +++++
 src/providers/ipa/ipa_selinux.c      | 33 ++++++++++++----
 7 files changed, 141 insertions(+), 77 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 262f146da..58c4992e0 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -591,6 +591,7 @@ errno_t ipa_hbac_evaluate_rules(struct be_ctx *be_ctx,
     struct hbac_eval_req *eval_req;
     enum hbac_eval_result result;
     struct hbac_info *info = NULL;
+    const char **attrs_get_cached_rules;
     errno_t ret;
 
     tmp_ctx = talloc_new(NULL);
@@ -603,8 +604,17 @@ errno_t ipa_hbac_evaluate_rules(struct be_ctx *be_ctx,
     hbac_ctx.pd = pd;
 
     /* Get HBAC rules from the sysdb */
-    ret = hbac_get_cached_rules(tmp_ctx, be_ctx->domain,
-                                &hbac_ctx.rule_count, &hbac_ctx.rules);
+    attrs_get_cached_rules = hbac_get_attrs_to_get_cached_rules(tmp_ctx);
+    if (attrs_get_cached_rules == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "hbac_get_attrs_to_get_cached_rules() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+    ret = ipa_common_get_cached_rules(tmp_ctx, be_ctx->domain,
+                                      IPA_HBAC_RULE, HBAC_RULES_SUBDIR,
+                                      attrs_get_cached_rules,
+                                      &hbac_ctx.rule_count, &hbac_ctx.rules);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not retrieve rules from the cache\n");
         goto done;
@@ -649,69 +659,6 @@ errno_t ipa_hbac_evaluate_rules(struct be_ctx *be_ctx,
     return ret;
 }
 
-errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx,
-                              struct sss_domain_info *domain,
-                              size_t *_rule_count,
-                              struct sysdb_attrs ***_rules)
-{
-    errno_t ret;
-    struct ldb_message **msgs;
-    struct sysdb_attrs **rules;
-    size_t rule_count;
-    TALLOC_CTX *tmp_ctx;
-    char *filter;
-    const char *attrs[] = { OBJECTCLASS,
-                            IPA_CN,
-                            SYSDB_ORIG_DN,
-                            IPA_UNIQUE_ID,
-                            IPA_ENABLED_FLAG,
-                            IPA_ACCESS_RULE_TYPE,
-                            IPA_MEMBER_USER,
-                            IPA_USER_CATEGORY,
-                            IPA_MEMBER_SERVICE,
-                            IPA_SERVICE_CATEGORY,
-                            IPA_SOURCE_HOST,
-                            IPA_SOURCE_HOST_CATEGORY,
-                            IPA_EXTERNAL_HOST,
-                            IPA_MEMBER_HOST,
-                            IPA_HOST_CATEGORY,
-                            NULL };
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) return ENOMEM;
-
-    filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
-    if (filter == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    ret = sysdb_search_custom(tmp_ctx, domain, filter,
-                              HBAC_RULES_SUBDIR, attrs,
-                              &rule_count, &msgs);
-    if (ret != EOK && ret != ENOENT) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
-        goto done;
-    } if (ret == ENOENT) {
-       rule_count = 0;
-    }
-
-    ret = sysdb_msg2attrs(tmp_ctx, rule_count, msgs, &rules);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Could not convert ldb message to sysdb_attrs\n");
-        goto done;
-    }
-
-    if (_rules) *_rules = talloc_steal(mem_ctx, rules);
-    if (_rule_count) *_rule_count = rule_count;
-
-    ret = EOK;
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
 struct ipa_pam_access_handler_state {
     struct tevent_context *ev;
     struct be_ctx *be_ctx;
diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
index eb19fc438..de6903502 100644
--- a/src/providers/ipa/ipa_access.h
+++ b/src/providers/ipa/ipa_access.h
@@ -63,9 +63,4 @@ ipa_pam_access_handler_recv(TALLOC_CTX *mem_ctx,
                              struct tevent_req *req,
                              struct pam_data **_data);
 
-errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx,
-                              struct sss_domain_info *domain,
-                              size_t *_rule_count,
-                              struct sysdb_attrs ***_rules);
-
 #endif /* _IPA_ACCESS_H_ */
diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
index 2dba31496..941441912 100644
--- a/src/providers/ipa/ipa_hbac_common.c
+++ b/src/providers/ipa/ipa_hbac_common.c
@@ -716,3 +716,33 @@ hbac_eval_host_element(TALLOC_CTX *mem_ctx,
     talloc_free(tmp_ctx);
     return ret;
 }
+
+const char **
+hbac_get_attrs_to_get_cached_rules(TALLOC_CTX *mem_ctx)
+{
+    const char **attrs = talloc_zero_array(mem_ctx, const char *, 16);
+    if (attrs == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array() failed\n");
+        goto done;
+    }
+
+    attrs[0] = OBJECTCLASS;
+    attrs[1] = IPA_CN;
+    attrs[2] = SYSDB_ORIG_DN;
+    attrs[3] = IPA_UNIQUE_ID;
+    attrs[4] = IPA_ENABLED_FLAG;
+    attrs[5] = IPA_ACCESS_RULE_TYPE;
+    attrs[6] = IPA_MEMBER_USER;
+    attrs[7] = IPA_USER_CATEGORY;
+    attrs[8] = IPA_MEMBER_SERVICE;
+    attrs[9] = IPA_SERVICE_CATEGORY;
+    attrs[10] = IPA_SOURCE_HOST;
+    attrs[11] = IPA_SOURCE_HOST_CATEGORY;
+    attrs[12] = IPA_EXTERNAL_HOST;
+    attrs[13] = IPA_MEMBER_HOST;
+    attrs[14] = IPA_HOST_CATEGORY;
+    attrs[15] = NULL;
+
+done:
+    return attrs;
+}
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
index 7d8b1ed2f..b11814b83 100644
--- a/src/providers/ipa/ipa_hbac_private.h
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -89,6 +89,9 @@ get_ipa_hostgroupname(TALLOC_CTX *mem_ctx,
                       const char *host_dn,
                       char **hostgroupname);
 
+const char **
+hbac_get_attrs_to_get_cached_rules(TALLOC_CTX *mem_ctx);
+
 /* From ipa_hbac_services.c */
 struct tevent_req *
 ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ipa/ipa_rules_common.c b/src/providers/ipa/ipa_rules_common.c
index 056d04dd1..6964e93fb 100644
--- a/src/providers/ipa/ipa_rules_common.c
+++ b/src/providers/ipa/ipa_rules_common.c
@@ -161,3 +161,64 @@ ipa_common_entries_and_groups_sysdb_save(struct sss_domain_info *domain,
     }
     return ret;
 }
+
+errno_t
+ipa_common_get_cached_rules(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domain,
+                            const char *rule,
+                            const char *subtree_name,
+                            const char **attrs,
+                            size_t *_rule_count,
+                            struct sysdb_attrs ***_rules)
+{
+    errno_t ret;
+    struct ldb_message **msgs;
+    struct sysdb_attrs **rules;
+    size_t rule_count;
+    TALLOC_CTX *tmp_ctx;
+    char *filter;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", rule);
+    if (filter == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_custom(tmp_ctx, domain, filter,
+                              subtree_name, attrs,
+                              &rule_count, &msgs);
+    if (ret != EOK && ret != ENOENT) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
+        goto done;
+    }
+
+    if (ret == ENOENT) {
+       rule_count = 0;
+    }
+
+    ret = sysdb_msg2attrs(tmp_ctx, rule_count, msgs, &rules);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not convert ldb message to sysdb_attrs\n");
+        goto done;
+    }
+
+    if (_rules) {
+        *_rules = talloc_steal(mem_ctx, rules);
+    }
+
+    if (_rule_count) {
+        *_rule_count = rule_count;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_rules_common.h b/src/providers/ipa/ipa_rules_common.h
index b5e05b039..9ccff7f71 100644
--- a/src/providers/ipa/ipa_rules_common.h
+++ b/src/providers/ipa/ipa_rules_common.h
@@ -49,4 +49,13 @@ ipa_common_entries_and_groups_sysdb_save(struct sss_domain_info *domain,
                                          size_t group_count,
                                          struct sysdb_attrs **groups);
 
+errno_t
+ipa_common_get_cached_rules(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domain,
+                            const char *rule,
+                            const char *subtree_name,
+                            const char **attrs,
+                            size_t *_rule_count,
+                            struct sysdb_attrs ***_rules);
+
 #endif /* IPA_RULES_COMMON_H_ */
diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index e9cc33953..2c61bac37 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -1009,6 +1009,7 @@ ipa_get_selinux_maps_offline(struct tevent_req *req)
                             SYSDB_SELINUX_SEEALSO,
                             SYSDB_SELINUX_USER,
                             NULL };
+    const char **attrs_get_cached_rules;
     const char *default_user;
     const char *order;
 
@@ -1066,10 +1067,20 @@ ipa_get_selinux_maps_offline(struct tevent_req *req)
     state->nmaps = nmaps;
 
     /* read all the HBAC rules */
-    ret = hbac_get_cached_rules(state, state->be_ctx->domain,
-                                &state->hbac_rule_count, &state->hbac_rules);
+    attrs_get_cached_rules = hbac_get_attrs_to_get_cached_rules(state);
+    if (attrs_get_cached_rules == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "hbac_get_attrs_to_get_cached_rules() failed\n");
+        return ENOMEM;
+    }
+
+    ret = ipa_common_get_cached_rules(state, state->be_ctx->domain,
+                                      IPA_HBAC_RULE, HBAC_RULES_SUBDIR,
+                                      attrs_get_cached_rules,
+                                      &state->hbac_rule_count,
+                                      &state->hbac_rules);
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "hbac_get_cached_rules failed [%d]: %s\n",
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_common_get_cached_rules failed [%d]: %s\n",
                                   ret, strerror(ret));
         return ret;
     }
@@ -1168,7 +1179,7 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
     struct ipa_id_ctx *id_ctx;
     struct dp_module *access_mod;
     struct dp_module *selinux_mod;
-
+    const char **attrs_get_cached_rules;
     const char *tmp_str;
     bool check_hbac;
     errno_t ret;
@@ -1208,9 +1219,17 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
         access_mod = dp_target_module(state->be_ctx->provider, DPT_ACCESS);
         selinux_mod = dp_target_module(state->be_ctx->provider, DPT_SELINUX);
         if (access_mod == selinux_mod) {
-            ret = hbac_get_cached_rules(state, state->be_ctx->domain,
-                                        &state->hbac_rule_count,
-                                        &state->hbac_rules);
+            attrs_get_cached_rules = hbac_get_attrs_to_get_cached_rules(state);
+            if (attrs_get_cached_rules == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = ipa_common_get_cached_rules(state, state->be_ctx->domain,
+                                              IPA_HBAC_RULE, HBAC_RULES_SUBDIR,
+                                              attrs_get_cached_rules,
+                                              &state->hbac_rule_count,
+                                              &state->hbac_rules);
             /* Terminates the request */
             goto done;
         }

From 21cf7fe929f9305801e034ad74a0f5291d8f1b6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 14:36:34 +0200
Subject: [PATCH 05/20] IPA_ACCESS: Make ipa_purge_hbac() more generic
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This mothod can also be reused in the future for new backend modules.

In order to make it more generic, let's just move it to
ipa_rules_common.[ch], rename it to ipa_common_purge_rules() and make
the subtreename to be purged a new paramether of this method.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_access.c       | 35 ++---------------------------------
 src/providers/ipa/ipa_rules_common.c | 32 ++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_rules_common.h |  4 ++++
 3 files changed, 38 insertions(+), 33 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 58c4992e0..28d46fecf 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -118,7 +118,6 @@ static errno_t ipa_fetch_hbac_hostinfo(struct tevent_req *req);
 static void ipa_fetch_hbac_hostinfo_done(struct tevent_req *subreq);
 static void ipa_fetch_hbac_services_done(struct tevent_req *subreq);
 static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq);
-static errno_t ipa_purge_hbac(struct sss_domain_info *domain);
 static errno_t ipa_save_hbac(struct sss_domain_info *domain,
                              struct ipa_fetch_hbac_state *state);
 
@@ -436,7 +435,8 @@ static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq)
 
     if (found == false) {
         /* No rules were found that apply to this host. */
-        ret = ipa_purge_hbac(state->be_ctx->domain);
+        ret = ipa_common_purge_rules(state->be_ctx->domain,
+                                     HBAC_RULES_SUBDIR);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove HBAC rules\n");
             goto done;
@@ -470,37 +470,6 @@ static errno_t ipa_fetch_hbac_recv(struct tevent_req *req)
     return EOK;
 }
 
-static errno_t ipa_purge_hbac(struct sss_domain_info *domain)
-{
-    TALLOC_CTX *tmp_ctx;
-    struct ldb_dn *base_dn;
-    errno_t ret;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        return ENOMEM;
-    }
-
-    /* Delete any rules in the sysdb so offline logins are also denied. */
-    base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, HBAC_RULES_SUBDIR);
-    if (base_dn == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
-        goto done;
-    }
-
-    ret = EOK;
-
-done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
 static errno_t ipa_save_hbac(struct sss_domain_info *domain,
                              struct ipa_fetch_hbac_state *state)
 {
diff --git a/src/providers/ipa/ipa_rules_common.c b/src/providers/ipa/ipa_rules_common.c
index 6964e93fb..971870c48 100644
--- a/src/providers/ipa/ipa_rules_common.c
+++ b/src/providers/ipa/ipa_rules_common.c
@@ -222,3 +222,35 @@ ipa_common_get_cached_rules(TALLOC_CTX *mem_ctx,
     talloc_free(tmp_ctx);
     return ret;
 }
+
+errno_t
+ipa_common_purge_rules(struct sss_domain_info *domain,
+                       const char *subtree_name)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_dn *base_dn;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    base_dn = sysdb_custom_subtree_dn(tmp_ctx, domain, subtree_name);
+    if (base_dn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_delete_recursive(domain->sysdb, base_dn, true);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_delete_recursive failed.\n");
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_rules_common.h b/src/providers/ipa/ipa_rules_common.h
index 9ccff7f71..7c62f4538 100644
--- a/src/providers/ipa/ipa_rules_common.h
+++ b/src/providers/ipa/ipa_rules_common.h
@@ -58,4 +58,8 @@ ipa_common_get_cached_rules(TALLOC_CTX *mem_ctx,
                             size_t *_rule_count,
                             struct sysdb_attrs ***_rules);
 
+errno_t
+ipa_common_purge_rules(struct sss_domain_info *domain,
+                       const char *subtree_name);
+
 #endif /* IPA_RULES_COMMON_H_ */

From f36c9a0c9bcd55bb29e1257fee8d63efe689678f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 15:29:35 +0200
Subject: [PATCH 06/20] IPA_RULES_COMMON: Introduce ipa_common_save_rules()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This method is kind of a replacement for ipa_save_hbac() one.

While ipa_save_hbac() wasn't removed, its porpuse has been totally
changed. Now it just prepare the ground and calls
ipa_common_save_rules() which is a more generic function that can be
reused for new backend modules.

In order to make the code cleaner a new structure has also been
introduced: struct ipa_common_entries; which contains the values that
will be used to save the entry and the entrygroup to sysdb.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_access.c       | 101 ++++++++++++++---------------------
 src/providers/ipa/ipa_rules_common.c |  92 +++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_rules_common.h |  18 +++++++
 3 files changed, 151 insertions(+), 60 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 28d46fecf..fe475a25f 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -118,7 +118,8 @@ static errno_t ipa_fetch_hbac_hostinfo(struct tevent_req *req);
 static void ipa_fetch_hbac_hostinfo_done(struct tevent_req *subreq);
 static void ipa_fetch_hbac_services_done(struct tevent_req *subreq);
 static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq);
-static errno_t ipa_save_hbac(struct sss_domain_info *domain,
+static errno_t ipa_save_hbac(TALLOC_CTX *mem_ctx,
+                             struct sss_domain_info *domain,
                              struct ipa_fetch_hbac_state *state);
 
 static struct tevent_req *
@@ -446,7 +447,7 @@ static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq)
         goto done;
     }
 
-    ret = ipa_save_hbac(state->be_ctx->domain, state);
+    ret = ipa_save_hbac(state, state->be_ctx->domain, state);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save HBAC rules\n");
         goto done;
@@ -470,83 +471,63 @@ static errno_t ipa_fetch_hbac_recv(struct tevent_req *req)
     return EOK;
 }
 
-static errno_t ipa_save_hbac(struct sss_domain_info *domain,
+static errno_t ipa_save_hbac(TALLOC_CTX *mem_ctx,
+                             struct sss_domain_info *domain,
                              struct ipa_fetch_hbac_state *state)
 {
-    bool in_transaction = false;
+    struct ipa_common_entries *hosts;
+    struct ipa_common_entries *services;
+    struct ipa_common_entries *rules;
     errno_t ret;
-    errno_t sret;
 
-    ret = sysdb_transaction_start(domain->sysdb);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Could not start transaction\n");
-        goto done;
-    }
-    in_transaction = true;
-
-    /* Save the hosts */
-    ret = ipa_common_entries_and_groups_sysdb_save(domain,
-                                                   HBAC_HOSTS_SUBDIR,
-                                                   SYSDB_FQDN,
-                                                   state->host_count,
-                                                   state->hosts,
-                                                   HBAC_HOSTGROUPS_SUBDIR,
-                                                   SYSDB_NAME,
-                                                   state->hostgroup_count,
-                                                   state->hostgroups);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts [%d]: %s\n",
-              ret, sss_strerror(ret));
+    hosts = talloc_zero(mem_ctx, struct ipa_common_entries);
+    if (hosts == NULL) {
+        ret = ENOMEM;
         goto done;
     }
 
-    /* Save the services */
-    ret = ipa_common_entries_and_groups_sysdb_save(domain,
-                                                   HBAC_SERVICES_SUBDIR,
-                                                   IPA_CN,
-                                                   state->service_count,
-                                                   state->services,
-                                                   HBAC_SERVICEGROUPS_SUBDIR,
-                                                   IPA_CN,
-                                                   state->servicegroup_count,
-                                                   state->servicegroups);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services [%d]: %s\n",
-              ret, sss_strerror(ret));
+    hosts->entry_subdir = HBAC_HOSTS_SUBDIR;
+    hosts->entry_count = state->host_count;
+    hosts->entries = state->hosts;
+    hosts->group_subdir = HBAC_HOSTGROUPS_SUBDIR;
+    hosts->group_count = state->hostgroup_count;
+    hosts->groups = state->hostgroups;
+
+    services = talloc_zero(mem_ctx, struct ipa_common_entries);
+    if (services == NULL) {
+        ret = ENOMEM;
         goto done;
     }
-    /* Save the rules */
-    ret = ipa_common_entries_and_groups_sysdb_save(domain,
-                                                   HBAC_RULES_SUBDIR,
-                                                   IPA_UNIQUE_ID,
-                                                   state->rule_count,
-                                                   state->rules,
-                                                   NULL, NULL, 0, NULL);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules [%d]: %s\n",
-              ret, sss_strerror(ret));
+
+    services->entry_subdir = HBAC_SERVICES_SUBDIR;
+    services->entry_count = state->service_count;
+    services->entries = state->services;
+    services->group_subdir = HBAC_SERVICEGROUPS_SUBDIR;
+    services->group_count = state->servicegroup_count;
+    services->groups = state->servicegroups;
+
+    rules = talloc_zero(mem_ctx, struct ipa_common_entries);
+    if (rules == NULL) {
+        ret = ENOMEM;
         goto done;
     }
 
-    ret = sysdb_transaction_commit(domain->sysdb);
+    rules->entry_subdir = HBAC_RULES_SUBDIR;
+    rules->entry_count = state->rule_count;
+    rules->entries = state->rules;
+
+    ret = ipa_common_save_rules(domain, hosts, services, rules,
+                                &state->access_ctx->last_update);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_common_save_rules() failed [%d]: [%s]\n",
+              ret, sss_strerror(ret));
         goto done;
     }
-    in_transaction = false;
-
-    state->access_ctx->last_update = time(NULL);
 
     ret = EOK;
 
 done:
-    if (in_transaction) {
-        sret = sysdb_transaction_cancel(domain->sysdb);
-        if (sret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
-        }
-    }
-
     return ret;
 }
 
diff --git a/src/providers/ipa/ipa_rules_common.c b/src/providers/ipa/ipa_rules_common.c
index 971870c48..ffe0aa275 100644
--- a/src/providers/ipa/ipa_rules_common.c
+++ b/src/providers/ipa/ipa_rules_common.c
@@ -254,3 +254,95 @@ ipa_common_purge_rules(struct sss_domain_info *domain,
     talloc_free(tmp_ctx);
     return ret;
 }
+
+errno_t ipa_common_save_rules(struct sss_domain_info *domain,
+                              struct ipa_common_entries *hosts,
+                              struct ipa_common_entries *services,
+                              struct ipa_common_entries *rules,
+                              time_t *last_update)
+{
+    bool in_transaction = false;
+    errno_t ret;
+    errno_t sret;
+
+    ret = sysdb_transaction_start(domain->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Could not start transaction\n");
+        goto done;
+    }
+    in_transaction = true;
+
+    /* Save the hosts */
+    if (hosts != NULL) {
+        ret = ipa_common_entries_and_groups_sysdb_save(domain,
+                                                       hosts->entry_subdir,
+                                                       SYSDB_FQDN,
+                                                       hosts->entry_count,
+                                                       hosts->entries,
+                                                       hosts->group_subdir,
+                                                       SYSDB_NAME,
+                                                       hosts->group_count,
+                                                       hosts->groups);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Error saving hosts [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto done;
+        }
+    }
+
+    /* Save the services */
+    if (services != NULL) {
+        ret = ipa_common_entries_and_groups_sysdb_save(domain,
+                                                       services->entry_subdir,
+                                                       IPA_CN,
+                                                       services->entry_count,
+                                                       services->entries,
+                                                       services->group_subdir,
+                                                       IPA_CN,
+                                                       services->group_count,
+                                                       services->groups);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Error saving services [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto done;
+        }
+    }
+
+    /* Save the rules */
+    if (rules != NULL) {
+        ret = ipa_common_entries_and_groups_sysdb_save(domain,
+                                                       rules->entry_subdir,
+                                                       IPA_UNIQUE_ID,
+                                                       rules->entry_count,
+                                                       rules->entries,
+                                                       NULL, NULL, 0, NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Error saving rules [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto done;
+        }
+    }
+
+    ret = sysdb_transaction_commit(domain->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+        goto done;
+    }
+    in_transaction = false;
+
+    *last_update = time(NULL);
+
+    ret = EOK;
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(domain->sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
+        }
+    }
+
+    return ret;
+}
+
+
diff --git a/src/providers/ipa/ipa_rules_common.h b/src/providers/ipa/ipa_rules_common.h
index 7c62f4538..7882ce213 100644
--- a/src/providers/ipa/ipa_rules_common.h
+++ b/src/providers/ipa/ipa_rules_common.h
@@ -38,6 +38,17 @@
 #define IPA_TRUE_VALUE "TRUE"
 
 /* From ipa_rules_common.c */
+
+struct ipa_common_entries {
+    const char *entry_subdir;
+    size_t entry_count;
+    struct sysdb_attrs **entries;
+
+    const char *group_subdir;
+    size_t group_count;
+    struct sysdb_attrs **groups;
+};
+
 errno_t
 ipa_common_entries_and_groups_sysdb_save(struct sss_domain_info *domain,
                                          const char *primary_subdir,
@@ -62,4 +73,11 @@ errno_t
 ipa_common_purge_rules(struct sss_domain_info *domain,
                        const char *subtree_name);
 
+errno_t
+ipa_common_save_rules(struct sss_domain_info *domain,
+                      struct ipa_common_entries *hosts,
+                      struct ipa_common_entries *services,
+                      struct ipa_common_entries *rules,
+                      time_t *last_update);
+
 #endif /* IPA_RULES_COMMON_H_ */

From 215e0fc87c40c962a7e89e18a46ac7191e021c2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 7 Aug 2017 11:40:31 +0200
Subject: [PATCH 07/20] IPA_RULES_COMMON: Introduce
 ipa_common_get_hostgroupname()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

By moving the get_ipa_hostgroupname() method from ipa_hbac_hosts.[ch] to
ipa_rules_common.[ch] it can be used by both HBAC and, in the future,
for new backend modules.

The method got renamed to ipa_common_get_hostgroupname() and some coding
style changes have been made in order to match with what SSSD follows.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_common.c  |   6 +-
 src/providers/ipa/ipa_hbac_hosts.c   | 109 -----------------------------------
 src/providers/ipa/ipa_hbac_private.h |   5 --
 src/providers/ipa/ipa_rules_common.c | 107 ++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_rules_common.h |   6 ++
 5 files changed, 116 insertions(+), 117 deletions(-)

diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
index 941441912..31e53d24d 100644
--- a/src/providers/ipa/ipa_hbac_common.c
+++ b/src/providers/ipa/ipa_hbac_common.c
@@ -686,9 +686,9 @@ hbac_eval_host_element(TALLOC_CTX *mem_ctx,
     }
 
     for (i = j = 0; i < el->num_values; i++) {
-        ret = get_ipa_hostgroupname(tmp_ctx, domain->sysdb,
-                                    (const char *)el->values[i].data,
-                                    &name);
+        ret = ipa_common_get_hostgroupname(tmp_ctx, domain->sysdb,
+                                           (const char *)el->values[i].data,
+                                           &name);
         if (ret != EOK && ret != ERR_UNEXPECTED_ENTRY_TYPE) {
             DEBUG(SSSDBG_MINOR_FAILURE, "Skipping malformed entry [%s]\n",
                                         (const char *)el->values[i].data);
diff --git a/src/providers/ipa/ipa_hbac_hosts.c b/src/providers/ipa/ipa_hbac_hosts.c
index 74d91e513..f85ce533f 100644
--- a/src/providers/ipa/ipa_hbac_hosts.c
+++ b/src/providers/ipa/ipa_hbac_hosts.c
@@ -333,112 +333,3 @@ hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx,
     talloc_free(tmp_ctx);
     return ret;
 }
-
-errno_t
-get_ipa_hostgroupname(TALLOC_CTX *mem_ctx,
-                      struct sysdb_ctx *sysdb,
-                      const char *host_dn,
-                      char **hostgroupname)
-{
-    errno_t ret;
-    struct ldb_dn *dn;
-    const char *rdn_name;
-    const char *hostgroup_comp_name;
-    const char *account_comp_name;
-    const struct ldb_val *rdn_val;
-    const struct ldb_val *hostgroup_comp_val;
-    const struct ldb_val *account_comp_val;
-
-    /* This is an IPA-specific hack. It may not
-     * work for non-IPA servers and will need to
-     * be changed if SSSD ever supports HBAC on
-     * a non-IPA server.
-     */
-    *hostgroupname = NULL;
-
-    dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), host_dn);
-    if (dn == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    if (!ldb_dn_validate(dn)) {
-        ret = ERR_MALFORMED_ENTRY;
-        goto done;
-    }
-
-    if (ldb_dn_get_comp_num(dn) < 4) {
-        /* RDN, hostgroups, accounts, and at least one DC= */
-        /* If it's fewer, it's not a group DN */
-        ret = ERR_UNEXPECTED_ENTRY_TYPE;
-        goto done;
-    }
-
-    /* If the RDN name is 'cn' */
-    rdn_name = ldb_dn_get_rdn_name(dn);
-    if (rdn_name == NULL) {
-        /* Shouldn't happen if ldb_dn_validate()
-         * passed, but we'll be careful.
-         */
-        ret = ERR_MALFORMED_ENTRY;
-        goto done;
-    }
-
-    if (strcasecmp("cn", rdn_name) != 0) {
-        /* RDN has the wrong attribute name.
-         * It's not a host.
-         */
-        ret = ERR_UNEXPECTED_ENTRY_TYPE;
-        goto done;
-    }
-
-    /* and the second component is "cn=hostgroups" */
-    hostgroup_comp_name = ldb_dn_get_component_name(dn, 1);
-    if (strcasecmp("cn", hostgroup_comp_name) != 0) {
-        /* The second component name is not "cn" */
-        ret = ERR_UNEXPECTED_ENTRY_TYPE;
-        goto done;
-    }
-
-    hostgroup_comp_val = ldb_dn_get_component_val(dn, 1);
-    if (strncasecmp("hostgroups",
-                    (const char *) hostgroup_comp_val->data,
-                    hostgroup_comp_val->length) != 0) {
-        /* The second component value is not "hostgroups" */
-        ret = ERR_UNEXPECTED_ENTRY_TYPE;
-        goto done;
-    }
-
-    /* and the third component is "accounts" */
-    account_comp_name = ldb_dn_get_component_name(dn, 2);
-    if (strcasecmp("cn", account_comp_name) != 0) {
-        /* The third component name is not "cn" */
-        ret = ERR_UNEXPECTED_ENTRY_TYPE;
-        goto done;
-    }
-
-    account_comp_val = ldb_dn_get_component_val(dn, 2);
-    if (strncasecmp("accounts",
-                    (const char *) account_comp_val->data,
-                    account_comp_val->length) != 0) {
-        /* The third component value is not "accounts" */
-        ret = ERR_UNEXPECTED_ENTRY_TYPE;
-        goto done;
-    }
-
-    /* Then the value of the RDN is the group name */
-    rdn_val = ldb_dn_get_rdn_val(dn);
-    *hostgroupname = talloc_strndup(mem_ctx,
-                                    (const char *)rdn_val->data,
-                                    rdn_val->length);
-    if (*hostgroupname == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    ret = EOK;
-
-done:
-    talloc_free(dn);
-    return ret;
-}
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
index b11814b83..8ca7d09c9 100644
--- a/src/providers/ipa/ipa_hbac_private.h
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -83,11 +83,6 @@ hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx,
                          struct sysdb_attrs *rule_attrs,
                          bool support_srchost,
                          struct hbac_rule_element **source_hosts);
-errno_t
-get_ipa_hostgroupname(TALLOC_CTX *mem_ctx,
-                      struct sysdb_ctx *sysdb,
-                      const char *host_dn,
-                      char **hostgroupname);
 
 const char **
 hbac_get_attrs_to_get_cached_rules(TALLOC_CTX *mem_ctx);
diff --git a/src/providers/ipa/ipa_rules_common.c b/src/providers/ipa/ipa_rules_common.c
index ffe0aa275..11823476b 100644
--- a/src/providers/ipa/ipa_rules_common.c
+++ b/src/providers/ipa/ipa_rules_common.c
@@ -345,4 +345,111 @@ errno_t ipa_common_save_rules(struct sss_domain_info *domain,
     return ret;
 }
 
+errno_t
+ipa_common_get_hostgroupname(TALLOC_CTX *mem_ctx,
+                             struct sysdb_ctx *sysdb,
+                             const char *host_dn,
+                             char **_hostgroupname)
+{
+    errno_t ret;
+    struct ldb_dn *dn;
+    const char *rdn_name;
+    const char *hostgroup_comp_name;
+    const char *account_comp_name;
+    const struct ldb_val *rdn_val;
+    const struct ldb_val *hostgroup_comp_val;
+    const struct ldb_val *account_comp_val;
+
+    /* This is an IPA-specific hack. It may not
+     * work for non-IPA servers and will need to
+     * be changed if SSSD ever supports HBAC on
+     * a non-IPA server.
+     */
+    *_hostgroupname = NULL;
+
+    dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), host_dn);
+    if (dn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
 
+    if (!ldb_dn_validate(dn)) {
+        ret = ERR_MALFORMED_ENTRY;
+        goto done;
+    }
+
+    if (ldb_dn_get_comp_num(dn) < 4) {
+        /* RDN, hostgroups, accounts, and at least one DC= */
+        /* If it's fewer, it's not a group DN */
+        ret = ERR_UNEXPECTED_ENTRY_TYPE;
+        goto done;
+    }
+
+    /* If the RDN name is 'cn' */
+    rdn_name = ldb_dn_get_rdn_name(dn);
+    if (rdn_name == NULL) {
+        /* Shouldn't happen if ldb_dn_validate()
+         * passed, but we'll be careful.
+         */
+        ret = ERR_MALFORMED_ENTRY;
+        goto done;
+    }
+
+    if (strcasecmp("cn", rdn_name) != 0) {
+        /* RDN has the wrong attribute name.
+         * It's not a host.
+         */
+        ret = ERR_UNEXPECTED_ENTRY_TYPE;
+        goto done;
+    }
+
+    /* and the second component is "cn=hostgroups" */
+    hostgroup_comp_name = ldb_dn_get_component_name(dn, 1);
+    if (strcasecmp("cn", hostgroup_comp_name) != 0) {
+        /* The second component name is not "cn" */
+        ret = ERR_UNEXPECTED_ENTRY_TYPE;
+        goto done;
+    }
+
+    hostgroup_comp_val = ldb_dn_get_component_val(dn, 1);
+    if (strncasecmp("hostgroups",
+                    (const char *) hostgroup_comp_val->data,
+                    hostgroup_comp_val->length) != 0) {
+        /* The second component value is not "hostgroups" */
+        ret = ERR_UNEXPECTED_ENTRY_TYPE;
+        goto done;
+    }
+
+    /* and the third component is "accounts" */
+    account_comp_name = ldb_dn_get_component_name(dn, 2);
+    if (strcasecmp("cn", account_comp_name) != 0) {
+        /* The third component name is not "cn" */
+        ret = ERR_UNEXPECTED_ENTRY_TYPE;
+        goto done;
+    }
+
+    account_comp_val = ldb_dn_get_component_val(dn, 2);
+    if (strncasecmp("accounts",
+                    (const char *) account_comp_val->data,
+                    account_comp_val->length) != 0) {
+        /* The third component value is not "accounts" */
+        ret = ERR_UNEXPECTED_ENTRY_TYPE;
+        goto done;
+    }
+
+    /* Then the value of the RDN is the group name */
+    rdn_val = ldb_dn_get_rdn_val(dn);
+    *_hostgroupname = talloc_strndup(mem_ctx,
+                                     (const char *)rdn_val->data,
+                                     rdn_val->length);
+    if (*_hostgroupname == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(dn);
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_rules_common.h b/src/providers/ipa/ipa_rules_common.h
index 7882ce213..6cf57eb29 100644
--- a/src/providers/ipa/ipa_rules_common.h
+++ b/src/providers/ipa/ipa_rules_common.h
@@ -80,4 +80,10 @@ ipa_common_save_rules(struct sss_domain_info *domain,
                       struct ipa_common_entries *rules,
                       time_t *last_update);
 
+errno_t
+ipa_common_get_hostgroupname(TALLOC_CTX *mem_ctx,
+                             struct sysdb_ctx *sysdb,
+                             const char *host_dn,
+                             char **_hostgroupname);
+
 #endif /* IPA_RULES_COMMON_H_ */

From c12757af2a4bcb7ad75212dd144b57b3a4562c42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Mon, 10 Apr 2017 15:47:50 +0200
Subject: [PATCH 08/20] IPA_ACCESS: Make use of struct ipa_common_entries
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Just by doing so ipa_save_hbac() can be completely removed.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_access.c | 123 ++++++++++++++---------------------------
 1 file changed, 41 insertions(+), 82 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index fe475a25f..36f05ed60 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -95,21 +95,14 @@ struct ipa_fetch_hbac_state {
     struct sdap_search_base **search_bases;
 
     /* Hosts */
-    size_t host_count;
-    struct sysdb_attrs **hosts;
-    size_t hostgroup_count;
-    struct sysdb_attrs **hostgroups;
+    struct ipa_common_entries *hosts;
     struct sysdb_attrs *ipa_host;
 
     /* Rules */
-    size_t rule_count;
-    struct sysdb_attrs **rules;
+    struct ipa_common_entries *rules;
 
     /* Services */
-    size_t service_count;
-    struct sysdb_attrs **services;
-    size_t servicegroup_count;
-    struct sysdb_attrs **servicegroups;
+    struct ipa_common_entries *services;
 };
 
 static errno_t ipa_fetch_hbac_retry(struct tevent_req *req);
@@ -118,9 +111,6 @@ static errno_t ipa_fetch_hbac_hostinfo(struct tevent_req *req);
 static void ipa_fetch_hbac_hostinfo_done(struct tevent_req *subreq);
 static void ipa_fetch_hbac_services_done(struct tevent_req *subreq);
 static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq);
-static errno_t ipa_save_hbac(TALLOC_CTX *mem_ctx,
-                             struct sss_domain_info *domain,
-                             struct ipa_fetch_hbac_state *state);
 
 static struct tevent_req *
 ipa_fetch_hbac_send(TALLOC_CTX *mem_ctx,
@@ -147,6 +137,21 @@ ipa_fetch_hbac_send(TALLOC_CTX *mem_ctx,
     state->sdap_ctx = access_ctx->sdap_ctx;
     state->ipa_options = access_ctx->ipa_options;
     state->search_bases = access_ctx->hbac_search_bases;
+    state->hosts = talloc_zero(state, struct ipa_common_entries);
+    if (state->hosts == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+    state->services = talloc_zero(state, struct ipa_common_entries);
+    if (state->hosts == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+    state->rules = talloc_zero(state, struct ipa_common_entries);
+    if (state->rules == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
 
     if (state->search_bases == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "No HBAC search base found.\n");
@@ -296,8 +301,12 @@ static void ipa_fetch_hbac_hostinfo_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct ipa_fetch_hbac_state);
 
     ret = ipa_host_info_recv(subreq, state,
-                             &state->host_count, &state->hosts,
-                             &state->hostgroup_count, &state->hostgroups);
+                             &state->hosts->entry_count,
+                             &state->hosts->entries,
+                             &state->hosts->group_count,
+                             &state->hosts->groups);
+    state->hosts->entry_subdir = HBAC_HOSTS_SUBDIR;
+    state->hosts->group_subdir = HBAC_HOSTGROUPS_SUBDIR;
     talloc_zfree(subreq);
     if (ret != EOK) {
         goto done;
@@ -338,8 +347,12 @@ static void ipa_fetch_hbac_services_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct ipa_fetch_hbac_state);
 
     ret = ipa_hbac_service_info_recv(subreq, state,
-                             &state->service_count, &state->services,
-                             &state->servicegroup_count, &state->servicegroups);
+                                     &state->services->entry_count,
+                                     &state->services->entries,
+                                     &state->services->group_count,
+                                     &state->services->groups);
+    state->services->entry_subdir = HBAC_SERVICES_SUBDIR;
+    state->services->group_subdir = HBAC_SERVICEGROUPS_SUBDIR;
     talloc_zfree(subreq);
     if (ret != EOK) {
         goto done;
@@ -355,15 +368,16 @@ static void ipa_fetch_hbac_services_done(struct tevent_req *subreq)
         goto done;
     }
 
-    for (i = 0; i < state->host_count; i++) {
-        ret = sysdb_attrs_get_string(state->hosts[i], SYSDB_FQDN, &hostname);
+    for (i = 0; i < state->hosts->entry_count; i++) {
+        ret = sysdb_attrs_get_string(state->hosts->entries[i], SYSDB_FQDN,
+                                     &hostname);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
             goto done;
         }
 
         if (strcasecmp(hostname, ipa_hostname) == 0) {
-            state->ipa_host = state->hosts[i];
+            state->ipa_host = state->hosts->entries[i];
             break;
         }
     }
@@ -409,7 +423,9 @@ static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct ipa_fetch_hbac_state);
 
     ret = ipa_hbac_rule_info_recv(subreq, state,
-                                  &state->rule_count, &state->rules);
+                                  &state->rules->entry_count,
+                                  &state->rules->entries);
+    state->rules->entry_subdir = HBAC_RULES_SUBDIR;
     talloc_zfree(subreq);
     if (ret == ENOENT) {
         /* Set ret to EOK so we can safely call sdap_id_op_done. */
@@ -447,7 +463,10 @@ static void ipa_fetch_hbac_rules_done(struct tevent_req *subreq)
         goto done;
     }
 
-    ret = ipa_save_hbac(state, state->be_ctx->domain, state);
+    ret = ipa_common_save_rules(state->be_ctx->domain,
+                                state->hosts, state->services, state->rules,
+                                &state->access_ctx->last_update);
+
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save HBAC rules\n");
         goto done;
@@ -471,66 +490,6 @@ static errno_t ipa_fetch_hbac_recv(struct tevent_req *req)
     return EOK;
 }
 
-static errno_t ipa_save_hbac(TALLOC_CTX *mem_ctx,
-                             struct sss_domain_info *domain,
-                             struct ipa_fetch_hbac_state *state)
-{
-    struct ipa_common_entries *hosts;
-    struct ipa_common_entries *services;
-    struct ipa_common_entries *rules;
-    errno_t ret;
-
-    hosts = talloc_zero(mem_ctx, struct ipa_common_entries);
-    if (hosts == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    hosts->entry_subdir = HBAC_HOSTS_SUBDIR;
-    hosts->entry_count = state->host_count;
-    hosts->entries = state->hosts;
-    hosts->group_subdir = HBAC_HOSTGROUPS_SUBDIR;
-    hosts->group_count = state->hostgroup_count;
-    hosts->groups = state->hostgroups;
-
-    services = talloc_zero(mem_ctx, struct ipa_common_entries);
-    if (services == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    services->entry_subdir = HBAC_SERVICES_SUBDIR;
-    services->entry_count = state->service_count;
-    services->entries = state->services;
-    services->group_subdir = HBAC_SERVICEGROUPS_SUBDIR;
-    services->group_count = state->servicegroup_count;
-    services->groups = state->servicegroups;
-
-    rules = talloc_zero(mem_ctx, struct ipa_common_entries);
-    if (rules == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    rules->entry_subdir = HBAC_RULES_SUBDIR;
-    rules->entry_count = state->rule_count;
-    rules->entries = state->rules;
-
-    ret = ipa_common_save_rules(domain, hosts, services, rules,
-                                &state->access_ctx->last_update);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "ipa_common_save_rules() failed [%d]: [%s]\n",
-              ret, sss_strerror(ret));
-        goto done;
-    }
-
-    ret = EOK;
-
-done:
-    return ret;
-}
-
 errno_t ipa_hbac_evaluate_rules(struct be_ctx *be_ctx,
                                 struct dp_option *ipa_options,
                                 struct pam_data *pd)

From 8e1d9153ee4f9969c89a86f25f1d0bc8b68ee481 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Tue, 18 Apr 2017 14:33:20 +0200
Subject: [PATCH 09/20] IPA_COMMON: Introduce ipa_get_host_attrs()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

By adding this method it can reused in the future for new backend
modules.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_access.c | 35 ++++++----------------------------
 src/providers/ipa/ipa_common.c | 43 ++++++++++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.h |  6 ++++++
 3 files changed, 55 insertions(+), 29 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 36f05ed60..32ccf541c 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -338,10 +338,7 @@ static void ipa_fetch_hbac_services_done(struct tevent_req *subreq)
 {
     struct ipa_fetch_hbac_state *state;
     struct tevent_req *req;
-    const char *ipa_hostname;
-    const char *hostname;
     errno_t ret;
-    size_t i;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ipa_fetch_hbac_state);
@@ -359,32 +356,12 @@ static void ipa_fetch_hbac_services_done(struct tevent_req *subreq)
     }
 
     /* Get the ipa_host attrs */
-    state->ipa_host = NULL;
-    ipa_hostname = dp_opt_get_cstring(state->ipa_options, IPA_HOSTNAME);
-    if (ipa_hostname == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Missing ipa_hostname, this should never happen.\n");
-        ret = EINVAL;
-        goto done;
-    }
-
-    for (i = 0; i < state->hosts->entry_count; i++) {
-        ret = sysdb_attrs_get_string(state->hosts->entries[i], SYSDB_FQDN,
-                                     &hostname);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
-            goto done;
-        }
-
-        if (strcasecmp(hostname, ipa_hostname) == 0) {
-            state->ipa_host = state->hosts->entries[i];
-            break;
-        }
-    }
-
-    if (state->ipa_host == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
-        ret = EINVAL;
+    ret = ipa_get_host_attrs(state->ipa_options,
+                             state->hosts->entry_count,
+                             state->hosts->entries,
+                             &state->ipa_host);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host.\n");
         goto done;
     }
 
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 657994508..6b29f2fde 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -1194,3 +1194,46 @@ errno_t ipa_get_dyndns_options(struct be_ctx *be_ctx,
 
     return EOK;
 }
+
+errno_t ipa_get_host_attrs(struct dp_option *ipa_options,
+                           size_t host_count,
+                           struct sysdb_attrs **hosts,
+                           struct sysdb_attrs **_ipa_host)
+{
+    const char *ipa_hostname;
+    const char *hostname;
+    errno_t ret;
+
+    *_ipa_host = NULL;
+    ipa_hostname = dp_opt_get_cstring(ipa_options, IPA_HOSTNAME);
+    if (ipa_hostname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Missing ipa_hostname, this should never happen.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    for (size_t i = 0; i < host_count; i++) {
+        ret = sysdb_attrs_get_string(hosts[i], SYSDB_FQDN, &hostname);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
+            goto done;
+        }
+
+        if (strcasecmp(hostname, ipa_hostname) == 0) {
+            *_ipa_host = hosts[i];
+            break;
+        }
+    }
+
+    if (*_ipa_host == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index add9df876..b1d90d362 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -292,4 +292,10 @@ errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
 
 
 struct krb5_ctx *ipa_init_get_krb5_auth_ctx(void *data);
+
+errno_t ipa_get_host_attrs(struct dp_option *ipa_options,
+                           size_t host_count,
+                           struct sysdb_attrs **hosts,
+                           struct sysdb_attrs **_ipa_host);
+
 #endif /* _IPA_COMMON_H_ */

From ea0636d147eec8d3f6a54968710ae20756111d22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 2 Aug 2017 12:10:10 +0200
Subject: [PATCH 10/20] UTIL: move {files,selinux}.c under util directory
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

files.c has at least one function that will be re-used for the new
session provider that's about to be added. Also, a few other functions
may be added and files.c seems the right place for those.

selinux.c has been moved together with files.c as the latter takes
advantage of some functions from the former and we do not want to always
link agains the tools code.

The public functions from files.c got a "sss_" prefix and it has been
changed whenever they're used.

Last but not least, all the places that included "tools/tools_util.h"
due to the functions on files.c had this include removed (as they were
already including "util/util.h".

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 Makefile.am                    |  8 ++++----
 src/tests/files-tests.c        | 13 ++++++-------
 src/tools/sssctl/sssctl_data.c |  3 +--
 src/tools/sssctl/sssctl_logs.c |  3 +--
 src/tools/tools_util.c         |  5 ++---
 src/tools/tools_util.h         | 17 -----------------
 src/{tools => util}/files.c    | 22 +++++++++++-----------
 src/{tools => util}/selinux.c  |  0
 src/util/util.h                | 19 +++++++++++++++++++
 9 files changed, 44 insertions(+), 46 deletions(-)
 rename src/{tools => util}/files.c (98%)
 rename src/{tools => util}/selinux.c (100%)

diff --git a/Makefile.am b/Makefile.am
index 161db1980..c292c1317 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -583,8 +583,6 @@ SSSD_RESPONDER_OBJ = \
 SSSD_TOOLS_OBJ = \
     src/tools/sss_sync_ops.c \
     src/tools/tools_util.c \
-    src/tools/files.c \
-    src/tools/selinux.c \
     src/tools/common/sss_tools.c \
     src/tools/common/sss_process.c \
     src/confdb/confdb_setup.c \
@@ -1052,6 +1050,8 @@ libsss_util_la_SOURCES = \
     src/util/become_user.c \
     src/util/util_watchdog.c \
     src/util/sss_ptr_hash.c \
+    src/util/files.c \
+    src/util/selinux.c \
     $(NULL)
 libsss_util_la_CFLAGS = \
     $(AM_CFLAGS) \
@@ -2016,8 +2016,8 @@ files_tests_SOURCES = \
     src/tests/files-tests.c \
     src/util/check_and_open.c \
     src/util/atomic_io.c \
-    src/tools/selinux.c \
-    src/tools/files.c
+    src/util/selinux.c \
+    src/util/files.c
 files_tests_CFLAGS = \
     $(AM_CFLAGS) \
     $(CHECK_CFLAGS)
diff --git a/src/tests/files-tests.c b/src/tests/files-tests.c
index e96a60af1..9feb9274a 100644
--- a/src/tests/files-tests.c
+++ b/src/tests/files-tests.c
@@ -32,7 +32,6 @@
 #include <popt.h>
 
 #include "config.h"
-#include "tools/tools_util.h"
 #include "util/util.h"
 #include "tests/common.h"
 
@@ -144,7 +143,7 @@ START_TEST(test_remove_tree)
     fail_if(ret == -1, "Cannot chdir\n");
 
     /* and finally wipe it out.. */
-    ret = remove_tree(dir_path);
+    ret = sss_remove_tree(dir_path);
     fail_unless(ret == EOK, "remove_tree failed\n");
 
     /* check if really gone */
@@ -193,7 +192,7 @@ START_TEST(test_remove_subtree)
     fail_if(ret == -1, "Cannot chdir\n");
 
     /* and finally wipe it out.. */
-    ret = remove_subtree(dir_path);
+    ret = sss_remove_subtree(dir_path);
     fail_unless(ret == EOK, "remove_subtree failed\n");
 
     /* check if really gone */
@@ -240,7 +239,7 @@ START_TEST(test_simple_copy)
     /* and finally copy.. */
     DEBUG(SSSDBG_FUNC_DATA,
           "Will copy from '%s' to '%s'\n", dir_path, dst_path);
-    ret = copy_tree(dir_path, dst_path, 0700, uid, gid);
+    ret = sss_copy_tree(dir_path, dst_path, 0700, uid, gid);
     fail_unless(ret == EOK, "copy_tree failed\n");
 
     /* check if really copied */
@@ -284,7 +283,7 @@ START_TEST(test_copy_file)
     /* Copy this file to a new file */
     DEBUG(SSSDBG_FUNC_DATA,
           "Will copy from 'foo' to 'bar'\n");
-    ret = copy_file_secure(foo_path, bar_path, 0700, uid, gid, 0);
+    ret = sss_copy_file_secure(foo_path, bar_path, 0700, uid, gid, 0);
     fail_unless(ret == EOK, "copy_file_secure failed\n");
 
     /* check if really copied */
@@ -326,7 +325,7 @@ START_TEST(test_copy_symlink)
     /* and finally copy.. */
     DEBUG(SSSDBG_FUNC_DATA,
           "Will copy from '%s' to '%s'\n", dir_path, dst_path);
-    ret = copy_tree(dir_path, dst_path, 0700, uid, gid);
+    ret = sss_copy_tree(dir_path, dst_path, 0700, uid, gid);
     fail_unless(ret == EOK, "copy_tree failed\n");
 
     /* check if really copied */
@@ -365,7 +364,7 @@ START_TEST(test_copy_node)
     /* and finally copy.. */
     DEBUG(SSSDBG_FUNC_DATA,
           "Will copy from '%s' to '%s'\n", dir_path, dst_path);
-    ret = copy_tree(dir_path, dst_path, 0700, uid, gid);
+    ret = sss_copy_tree(dir_path, dst_path, 0700, uid, gid);
     fail_unless(ret == EOK, "copy_tree failed\n");
 
     /* check if really copied and without special files */
diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
index 29c5e6760..860c5df55 100644
--- a/src/tools/sssctl/sssctl_data.c
+++ b/src/tools/sssctl/sssctl_data.c
@@ -23,7 +23,6 @@
 
 #include "util/util.h"
 #include "db/sysdb.h"
-#include "tools/common/sss_tools.h"
 #include "tools/common/sss_process.h"
 #include "tools/sssctl/sssctl.h"
 #include "tools/tools_util.h"
@@ -241,7 +240,7 @@ errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
     }
 
     printf(_("Removing cache files...\n"));
-    ret = remove_subtree(DB_PATH);
+    ret = sss_remove_subtree(DB_PATH);
     if (ret != EOK) {
         fprintf(stderr, _("Unable to remove cache files\n"));
         return ret;
diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
index 1aea54e36..472a553d1 100644
--- a/src/tools/sssctl/sssctl_logs.c
+++ b/src/tools/sssctl/sssctl_logs.c
@@ -23,7 +23,6 @@
 #include <signal.h>
 
 #include "util/util.h"
-#include "tools/common/sss_tools.h"
 #include "tools/common/sss_process.h"
 #include "tools/sssctl/sssctl.h"
 #include "tools/tools_util.h"
@@ -57,7 +56,7 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
 
     if (opts.delete) {
         printf(_("Deleting log files...\n"));
-        ret = remove_subtree(LOG_PATH);
+        ret = sss_remove_subtree(LOG_PATH);
         if (ret != EOK) {
             fprintf(stderr, _("Unable to remove log files\n"));
             return ret;
diff --git a/src/tools/tools_util.c b/src/tools/tools_util.c
index 5e51a4089..87a17491d 100644
--- a/src/tools/tools_util.c
+++ b/src/tools/tools_util.c
@@ -33,7 +33,6 @@
 #include "util/util.h"
 #include "confdb/confdb.h"
 #include "db/sysdb.h"
-#include "tools/tools_util.h"
 #include "tools/sss_sync_ops.h"
 
 static int setup_db(struct tools_ctx *ctx)
@@ -414,7 +413,7 @@ int remove_homedir(TALLOC_CTX *mem_ctx,
     }
 
     /* Remove the tree */
-    ret = remove_tree(homedir);
+    ret = sss_remove_tree(homedir);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot remove homedir %s: %d\n",
                   homedir, ret);
@@ -502,7 +501,7 @@ int create_homedir(const char *skeldir,
 
     selinux_file_context(homedir);
 
-    ret = copy_tree(skeldir, homedir, 0777 & ~default_umask, uid, gid);
+    ret = sss_copy_tree(skeldir, homedir, 0777 & ~default_umask, uid, gid);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Cannot populate user's home directory: [%d][%s].\n",
diff --git a/src/tools/tools_util.h b/src/tools/tools_util.h
index 389c7b5c4..fcfd8a659 100644
--- a/src/tools/tools_util.h
+++ b/src/tools/tools_util.h
@@ -111,21 +111,4 @@ errno_t sss_mc_refresh_group(const char *groupname);
 errno_t sss_mc_refresh_grouplist(struct tools_ctx *tctx,
                                  char **groupnames);
 
-/* from files.c */
-int remove_tree(const char *root);
-int remove_subtree(const char *root);
-
-int copy_tree(const char *src_root, const char *dst_root,
-              mode_t mode_root, uid_t uid, gid_t gid);
-int
-copy_file_secure(const char *src,
-                 const char *dest,
-                 mode_t mode,
-                 uid_t uid, gid_t gid,
-                 bool force);
-
-/* from selinux.c */
-int selinux_file_context(const char *dst_name);
-int reset_selinux_file_context(void);
-
 #endif  /* __TOOLS_UTIL_H__ */
diff --git a/src/tools/files.c b/src/util/files.c
similarity index 98%
rename from src/tools/files.c
rename to src/util/files.c
index 9f4e7caa7..5827b29d8 100644
--- a/src/tools/files.c
+++ b/src/util/files.c
@@ -65,7 +65,6 @@
 #include <talloc.h>
 
 #include "util/util.h"
-#include "tools/tools_util.h"
 
 struct copy_ctx {
     const char *src_orig;
@@ -140,7 +139,7 @@ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
                                 dev_t parent_dev,
                                 bool keep_root_dir);
 
-int remove_tree(const char *root)
+int sss_remove_tree(const char *root)
 {
     TALLOC_CTX *tmp_ctx = NULL;
     int ret;
@@ -155,7 +154,7 @@ int remove_tree(const char *root)
     return ret;
 }
 
-int remove_subtree(const char *root)
+int sss_remove_subtree(const char *root)
 {
     TALLOC_CTX *tmp_ctx = NULL;
     int ret;
@@ -489,11 +488,11 @@ copy_file(int ifd,
 }
 
 int
-copy_file_secure(const char *src,
-                 const char *dest,
-                 mode_t mode,
-                 uid_t uid, gid_t gid,
-                 bool force)
+sss_copy_file_secure(const char *src,
+                     const char *dest,
+                     mode_t mode,
+                     uid_t uid, gid_t gid,
+                     bool force)
 {
     int ifd = -1;
     int ofd = -1;
@@ -761,8 +760,10 @@ copy_dir(struct copy_ctx *cctx,
  * For several reasons, including the fact that we copy even special files
  * (pipes, etc) from the skeleton directory, the skeldir needs to be trusted
  */
-int copy_tree(const char *src_root, const char *dst_root,
-              mode_t mode_root, uid_t uid, gid_t gid)
+int sss_copy_tree(const char *src_root,
+                  const char *dst_root,
+                  mode_t mode_root,
+                  uid_t uid, gid_t gid)
 {
     int ret = EOK;
     struct copy_ctx *cctx = NULL;
@@ -806,4 +807,3 @@ int copy_tree(const char *src_root, const char *dst_root,
     talloc_free(cctx);
     return ret;
 }
-
diff --git a/src/tools/selinux.c b/src/util/selinux.c
similarity index 100%
rename from src/tools/selinux.c
rename to src/util/selinux.c
diff --git a/src/util/util.h b/src/util/util.h
index 9b64dead8..80411ec91 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -681,4 +681,23 @@ int sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl);
 int setup_watchdog(struct tevent_context *ev, int interval);
 void teardown_watchdog(void);
 
+/* from files.c */
+int sss_remove_tree(const char *root);
+int sss_remove_subtree(const char *root);
+
+int sss_copy_tree(const char *src_root,
+                  const char *dst_root,
+                  mode_t mode_root,
+                  uid_t uid, gid_t gid);
+
+int sss_copy_file_secure(const char *src,
+                         const char *dest,
+                         mode_t mode,
+                         uid_t uid, gid_t gid,
+                         bool force);
+
+/* from selinux.c */
+int selinux_file_context(const char *dst_name);
+int reset_selinux_file_context(void);
+
 #endif /* __SSSD_UTIL_H__ */

From 096c96b9f4bdbdfdb75e0ff42a1970c4a8acd02d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 2 Aug 2017 14:00:03 +0200
Subject: [PATCH 11/20] UTIL: Add sss_create_dir()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The newly added function helps us to create a new dir avoiding a
possible TUCTOU issue.

It's going to be used by the new session provider code.

A simple test for this new function has also been provided.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/tests/files-tests.c | 37 ++++++++++++++++++++++++
 src/util/files.c        | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/util.h         |  5 ++++
 3 files changed, 119 insertions(+)

diff --git a/src/tests/files-tests.c b/src/tests/files-tests.c
index 9feb9274a..1ccf404b9 100644
--- a/src/tests/files-tests.c
+++ b/src/tests/files-tests.c
@@ -378,6 +378,42 @@ START_TEST(test_copy_node)
 }
 END_TEST
 
+START_TEST(test_create_dir)
+{
+    int ret;
+    char origpath[PATH_MAX+1];
+    char *new_dir;
+    struct stat info;
+
+    errno = 0;
+
+    fail_unless(getcwd(origpath, PATH_MAX) == origpath, "Cannot getcwd\n");
+    fail_unless(errno == 0, "Cannot getcwd\n");
+
+    /* create a dir */
+    ret = sss_create_dir(dir_path, "testdir", S_IRUSR | S_IXUSR, uid, gid);
+    fail_unless(ret == EOK, "cannot create dir: %s", strerror(ret));
+
+    new_dir = talloc_asprintf(NULL, "%s/testdir", dir_path);
+    ret = stat(new_dir, &info);
+    fail_unless(ret == EOK, "failed to stat '%s'\n", new_dir);
+
+    /* check the dir has been created */
+    fail_unless(S_ISDIR(info.st_mode) != 0, "'%s' is not a dir.\n", new_dir);
+
+    /* check the permissions are okay */
+    fail_unless((info.st_mode & S_IRUSR) != 0, "Read permission is not set\n");
+    fail_unless((info.st_mode & S_IWUSR) == 0, "Write permission is set\n");
+    fail_unless((info.st_mode & S_IXUSR) != 0, "Exec permission is not set\n");
+
+    /* check the owner is okay */
+    fail_unless(info.st_uid == uid, "Dir created with the wrong uid\n");
+    fail_unless(info.st_gid == gid, "Dir created with the wrong gid\n");
+
+    talloc_free(new_dir);
+}
+END_TEST
+
 static Suite *files_suite(void)
 {
     Suite *s = suite_create("files_suite");
@@ -393,6 +429,7 @@ static Suite *files_suite(void)
     tcase_add_test(tc_files, test_copy_file);
     tcase_add_test(tc_files, test_copy_symlink);
     tcase_add_test(tc_files, test_copy_node);
+    tcase_add_test(tc_files, test_create_dir);
     suite_add_tcase(s, tc_files);
 
     return s;
diff --git a/src/util/files.c b/src/util/files.c
index 5827b29d8..33b21e2ea 100644
--- a/src/util/files.c
+++ b/src/util/files.c
@@ -807,3 +807,80 @@ int sss_copy_tree(const char *src_root,
     talloc_free(cctx);
     return ret;
 }
+
+int sss_create_dir(const char *parent_dir_path,
+                   const char *dir_name,
+                   mode_t mode,
+                   uid_t uid, gid_t gid)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *dir_path;
+    int ret = EOK;
+    int parent_dir_fd = -1;
+    int dir_fd = -1;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    parent_dir_fd = sss_open_cloexec(parent_dir_path, O_RDONLY | O_DIRECTORY,
+                                     &ret);
+    if (parent_dir_fd == -1) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Cannot open() directory '%s' [%d]: %s\n",
+              parent_dir_path, ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    dir_path = talloc_asprintf(tmp_ctx, "%s/%s", parent_dir_path, dir_name);
+    if (dir_path == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    errno = 0;
+    ret = mkdirat(parent_dir_fd, dir_name, mode);
+    if (ret == -1) {
+        if (errno == EEXIST) {
+            ret = EOK;
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Directory '%s' already created!\n", dir_path);
+        } else {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Error reading '%s': %s\n", parent_dir_path, strerror(ret));
+            goto fail;
+        }
+    }
+
+    dir_fd = sss_open_cloexec(dir_path, O_RDONLY | O_DIRECTORY, &ret);
+    if (dir_fd == -1) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Cannot open() directory '%s' [%d]: %s\n",
+              dir_path, ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    errno = 0;
+    ret = fchown(dir_fd, uid, gid);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to own the newly created directory '%s' [%d]: %s\n",
+              dir_path, ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    ret = EOK;
+
+fail:
+    if (parent_dir_fd != -1) {
+        close(parent_dir_fd);
+    }
+    if (dir_fd != -1) {
+        close(dir_fd);
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/util/util.h b/src/util/util.h
index 80411ec91..3d8bfe479 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -696,6 +696,11 @@ int sss_copy_file_secure(const char *src,
                          uid_t uid, gid_t gid,
                          bool force);
 
+int sss_create_dir(const char *parent_dir_path,
+                   const char *dir_name,
+                   mode_t mode,
+                   uid_t uid, gid_t gid);
+
 /* from selinux.c */
 int selinux_file_context(const char *dst_name);
 int reset_selinux_file_context(void);

From d40ff70de5340f23567e0b38b1bc9960edb484db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 2 Nov 2016 00:15:16 +0100
Subject: [PATCH 12/20] DESKPROFILE: Introduce the new IPA session provider
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In order to provide FleetCommander[0] integration, a session provider
has been introduced for IPA. The design of this feature and more
technical details can be found at [1] and [2], which are the design
pages of both freeIPA and SSSD parts.

As there's no way to test freeIPA integration with our upstream tests,
no test has been provided yet.

Is also worth to mention that the name "deskprofile" has been chosen
instead of "fleetcmd" in order to match with the freeIPA plugin. It
means that, for consistence, all source files, directories created,
options added, functions prefixes and so on are following the choice
accordingly.

[0]: https://wiki.gnome.org/Projects/FleetCommander
[1]: https://github.com/abbra/freeipa-desktop-profile/blob/master/plugin/Feature.mediawiki
[2]: https://docs.pagure.org/SSSD.sssd/design_pages/fleet_commander_integration.html

Resolves:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 Makefile.am                                    |  11 +
 contrib/sssd.spec.in                           |   2 +
 src/config/SSSDConfig/__init__.py.in           |   3 +
 src/config/cfg_rules.ini                       |   3 +
 src/config/etc/sssd.api.d/sssd-ipa.conf        |   3 +
 src/man/sssd-ipa.5.xml                         |  43 ++
 src/providers/data_provider/dp.h               |   2 +
 src/providers/data_provider/dp_target_auth.c   |   5 +-
 src/providers/data_provider/dp_targets.c       |   2 +
 src/providers/ipa/ipa_common.c                 |  26 +
 src/providers/ipa/ipa_common.h                 |   5 +
 src/providers/ipa/ipa_deskprofile_config.c     | 156 +++++
 src/providers/ipa/ipa_deskprofile_config.h     |  45 ++
 src/providers/ipa/ipa_deskprofile_private.h    |  50 ++
 src/providers/ipa/ipa_deskprofile_rules.c      | 367 ++++++++++
 src/providers/ipa/ipa_deskprofile_rules.h      |  43 ++
 src/providers/ipa/ipa_deskprofile_rules_util.c | 932 +++++++++++++++++++++++++
 src/providers/ipa/ipa_deskprofile_rules_util.h |  57 ++
 src/providers/ipa/ipa_init.c                   |  58 ++
 src/providers/ipa/ipa_opts.c                   |   3 +
 src/providers/ipa/ipa_session.c                | 832 ++++++++++++++++++++++
 src/providers/ipa/ipa_session.h                |  52 ++
 22 files changed, 2697 insertions(+), 3 deletions(-)
 create mode 100644 src/providers/ipa/ipa_deskprofile_config.c
 create mode 100644 src/providers/ipa/ipa_deskprofile_config.h
 create mode 100644 src/providers/ipa/ipa_deskprofile_private.h
 create mode 100644 src/providers/ipa/ipa_deskprofile_rules.c
 create mode 100644 src/providers/ipa/ipa_deskprofile_rules.h
 create mode 100644 src/providers/ipa/ipa_deskprofile_rules_util.c
 create mode 100644 src/providers/ipa/ipa_deskprofile_rules_util.h
 create mode 100644 src/providers/ipa/ipa_session.c
 create mode 100644 src/providers/ipa/ipa_session.h

diff --git a/Makefile.am b/Makefile.am
index c292c1317..6cda729d3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -88,6 +88,7 @@ polkitdir = @polkitdir@
 pamconfdir = $(sysconfdir)/pam.d
 systemtap_tapdir = @tapset_dir@
 sssdkcmdatadir = $(datadir)/sssd-kcm
+deskprofilepath = $(sss_statedir)/deskprofile
 
 if HAVE_SYSTEMD_UNIT
 ifp_exec_cmd = $(sssdlibexecdir)/sssd_ifp --uid 0 --gid 0 --debug-to-files --dbus-activated
@@ -801,6 +802,7 @@ dist_noinst_HEADERS = \
     src/providers/ipa/ipa_srv.h \
     src/providers/ipa/ipa_dn.h \
     src/providers/ipa/ipa_sudo.h \
+    src/providers/ipa/ipa_session.h \
     src/providers/ad/ad_srv.h \
     src/providers/ad/ad_common.h \
     src/providers/ad/ad_pac.h \
@@ -3892,6 +3894,14 @@ libsss_ipa_la_SOURCES = \
     src/providers/ipa/ipa_hbac_common.c \
     src/providers/ipa/ipa_rules_common.c \
     src/providers/ipa/ipa_rules_common.h \
+    src/providers/ipa/ipa_session.c \
+    src/providers/ipa/ipa_deskprofile_private.h \
+    src/providers/ipa/ipa_deskprofile_config.c \
+    src/providers/ipa/ipa_deskprofile_config.h \
+    src/providers/ipa/ipa_deskprofile_rules.c \
+    src/providers/ipa/ipa_deskprofile_rules.h \
+    src/providers/ipa/ipa_deskprofile_rules_util.c \
+    src/providers/ipa/ipa_deskprofile_rules_util.h \
     src/providers/ipa/ipa_srv.c \
     src/providers/ipa/ipa_idmap.c \
     src/providers/ipa/ipa_dn.c \
@@ -4617,6 +4627,7 @@ SSSD_USER_DIRS = \
     $(DESTDIR)$(sssdconfdir)/conf.d \
     $(DESTDIR)$(sssddefaultconfdir) \
     $(DESTDIR)$(logpath) \
+    $(DESTDIR)$(deskprofilepath) \
     $(NULL)
 
 installsssddirs::
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 0b7a61157..942d57f8e 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -156,6 +156,7 @@ Requires: python2-sssdconfig = %{version}-%{release}
 %global pubconfpath %{sssdstatedir}/pubconf
 %global gpocachepath %{sssdstatedir}/gpo_cache
 %global secdbpath %{sssdstatedir}/secrets
+%global deskprofilepath %{sssdstatedir}/deskprofile
 
 ### Build Dependencies ###
 
@@ -958,6 +959,7 @@ done
 %if (0%{?with_secrets} == 1)
 %attr(700,root,root) %dir %{secdbpath}
 %endif
+%attr(700,root,root) %dir %{deskprofilepath}
 %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd
 %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group
 %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 0e0c3be6d..d329fb2e8 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -216,6 +216,9 @@ option_strings = {
     'ipa_anchor_uuid': _("Attribute with the reference to the original object"),
     'ipa_user_override_object_class': _("Objectclass for user override objects"),
     'ipa_group_override_object_class': _("Objectclass for group override objects"),
+    'ipa_enable_deskprofile': _("Enable Desktop Profile integration"),
+    'ipa_deskprofile_search_base': _("Search base for Desktop Profile related objects"),
+    'ipa_deskprofile_refresh': _("The amount of time in seconds between lookups of the Desktop Profile rules against the IPA server"),
 
     # [provider/ad]
     'ad_domain' : _('Active Directory domain'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 4537d0fe8..1487b5f8e 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -437,10 +437,13 @@ option = ad_site
 option = ipa_anchor_uuid
 option = ipa_automount_location
 option = ipa_backup_server
+option = ipa_deskprofile_refresh
+option = ipa_deskprofile_search_base
 option = ipa_domain
 option = ipa_dyndns_iface
 option = ipa_dyndns_ttl
 option = ipa_dyndns_update
+option = ipa_enable_deskprofile
 option = ipa_enable_dns_sites
 option = ipa_group_override_object_class
 option = ipa_hbac_refresh
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index f36b568c3..802435acf 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -3,9 +3,11 @@ ipa_domain = str, None, false
 ipa_server = str, None, false
 ipa_backup_server = str, None, false
 ipa_hostname = str, None, false
+ipa_deskprofile_search_base = str, None, false
 ipa_dyndns_update = bool, None, false
 ipa_dyndns_ttl = int, None, false
 ipa_dyndns_iface = str, None, false
+ipa_enable_deskprofile = bool, None, false
 ipa_hbac_search_base = str, None, false
 ipa_host_search_base = str, None, false
 ipa_master_domain_search_base = str, None, false
@@ -193,6 +195,7 @@ ldap_autofs_search_base = str, None, false
 [provider/ipa/chpass]
 
 [provider/ipa/session]
+ipa_deskprofile_refresh = int, None, false
 ipa_host_object_class = str, None, false
 ipa_host_name = str, None, false
 ipa_host_fqdn = str, None, false
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index 5e7f8ff1a..93fc9060d 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -307,6 +307,19 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ipa_deskprofile_search_base (string)</term>
+                    <listitem>
+                        <para>
+                            Optional. Use the given string as search base for
+                            Desktop Profile related objects.
+                        </para>
+                        <para>
+                            Default: Use base DN
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ipa_hbac_search_base (string)</term>
                     <listitem>
                         <para>
@@ -448,6 +461,36 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ipa_deskprofile_refresh (integer)</term>
+                    <listitem>
+                        <para>
+                            The amount of time between lookups of the Desktop
+                            Profile rules against the IPA server. This will
+                            reduce the latency and load on the IPA server if
+                            there are many desktop profiles requests made in a
+                            short period.
+                        </para>
+                        <para>
+                            Default: 5 (seconds)
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ipa_enable_deskprofile (boolean)</term>
+                    <listitem>
+                        <para>
+                            The option denotes that the SSSD should perform
+                            lookups and fetch the Desktop Profile Rules from
+                            the IPA server.
+                        </para>
+                        <para>
+                            Default: true
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ipa_hbac_refresh (integer)</term>
                     <listitem>
                         <para>
diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h
index e80a6c339..9cdbe5b3a 100644
--- a/src/providers/data_provider/dp.h
+++ b/src/providers/data_provider/dp.h
@@ -66,6 +66,7 @@ enum dp_targets {
     DPT_SELINUX,
     DPT_HOSTID,
     DPT_SUBDOMAINS,
+    DPT_SESSION,
 
     DP_TARGET_SENTINEL
 };
@@ -80,6 +81,7 @@ enum dp_methods {
     DPM_AUTOFS_HANDLER,
     DPM_HOSTID_HANDLER,
     DPM_DOMAINS_HANDLER,
+    DPM_SESSION_HANDLER,
 
     DP_METHOD_SENTINEL
 };
diff --git a/src/providers/data_provider/dp_target_auth.c b/src/providers/data_provider/dp_target_auth.c
index 78c4cce7e..b4b92c525 100644
--- a/src/providers/data_provider/dp_target_auth.c
+++ b/src/providers/data_provider/dp_target_auth.c
@@ -126,10 +126,9 @@ static void choose_target(struct data_provider *provider,
             name = "PAM Chpass 2nd";
             break;
         case SSS_PAM_OPEN_SESSION:
-            target = DP_TARGET_SENTINEL;
-            method = DP_METHOD_SENTINEL;
+            target = DPT_SESSION;
+            method = DPM_SESSION_HANDLER;
             name = "PAM Open Session";
-            pd->pam_status = PAM_SUCCESS;
             break;
         case SSS_PAM_SETCRED:
             target = DP_TARGET_SENTINEL;
diff --git a/src/providers/data_provider/dp_targets.c b/src/providers/data_provider/dp_targets.c
index e2a45bbac..2dd15d80d 100644
--- a/src/providers/data_provider/dp_targets.c
+++ b/src/providers/data_provider/dp_targets.c
@@ -114,6 +114,8 @@ const char *dp_target_to_string(enum dp_targets target)
         return "hostid";
     case DPT_SUBDOMAINS:
         return "subdomains";
+    case DPT_SESSION:
+        return "session";
     case DP_TARGET_SENTINEL:
         return NULL;
     }
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 6b29f2fde..6bb1e679c 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -152,6 +152,9 @@ static errno_t ipa_parse_search_base(TALLOC_CTX *mem_ctx,
     case IPA_VIEWS_SEARCH_BASE:
         class_name = "IPA_VIEWS";
         break;
+    case IPA_DESKPROFILE_SEARCH_BASE:
+        class_name = "IPA_DESKPROFILE";
+        break;
     default:
         DEBUG(SSSDBG_CONF_SETTINGS,
               "Unknown search base type: [%d]\n", class);
@@ -398,6 +401,29 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
                                 &ipa_opts->selinux_search_bases);
     if (ret != EOK) goto done;
 
+    if (NULL == dp_opt_get_string(ipa_opts->basic,
+                                  IPA_DESKPROFILE_SEARCH_BASE)) {
+        value = talloc_asprintf(tmpctx, "cn=desktop-profile,%s", basedn);
+        if (!value) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = dp_opt_set_string(ipa_opts->basic, IPA_DESKPROFILE_SEARCH_BASE, value);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC, "Option %s set to %s\n",
+                  ipa_opts->basic[IPA_DESKPROFILE_SEARCH_BASE].opt_name,
+                  dp_opt_get_string(ipa_opts->basic,
+                                    IPA_DESKPROFILE_SEARCH_BASE));
+    }
+    ret = ipa_parse_search_base(ipa_opts->basic, ipa_opts->basic,
+                                IPA_DESKPROFILE_SEARCH_BASE,
+                                &ipa_opts->deskprofile_search_bases);
+    if (ret != EOK) goto done;
+
     value = dp_opt_get_string(ipa_opts->id->basic, SDAP_DEREF);
     if (value != NULL) {
         ret = deref_string_to_val(value, &i);
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index b1d90d362..d8ad945bc 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -56,6 +56,9 @@ enum ipa_basic_opt {
     IPA_SERVER_MODE,
     IPA_VIEWS_SEARCH_BASE,
     IPA_KRB5_CONFD_PATH,
+    IPA_ENABLE_DESKPROFILE,
+    IPA_DESKPROFILE_SEARCH_BASE,
+    IPA_DESKPROFILE_REFRESH,
 
     IPA_OPTS_BASIC /* opts counter */
 };
@@ -218,7 +221,9 @@ struct ipa_options {
     struct sdap_search_base **master_domain_search_bases;
     struct sdap_search_base **ranges_search_bases;
     struct sdap_search_base **views_search_bases;
+    struct sdap_search_base **deskprofile_search_bases;
     struct ipa_service *service;
+    bool ipa_enable_deskprofile;
 
     /* id provider */
     struct sdap_options *id;
diff --git a/src/providers/ipa/ipa_deskprofile_config.c b/src/providers/ipa/ipa_deskprofile_config.c
new file mode 100644
index 000000000..8c66dda75
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_config.c
@@ -0,0 +1,156 @@
+/*
+    SSSD
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_deskprofile_private.h"
+#include "providers/ipa/ipa_deskprofile_config.h"
+#include "providers/ldap/sdap_async.h"
+
+struct ipa_deskprofile_config_state {
+    struct sysdb_attrs *config;
+};
+
+static void
+ipa_deskprofile_get_config_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_deskprofile_get_config_send(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct sdap_handle *sh,
+                                struct sdap_options *opts,
+                                struct dp_option *ipa_opts)
+{
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq;
+    struct ipa_deskprofile_rule_state *state;
+    char *rule_filter;
+    const char *attrs[] = { IPA_DESKPROFILE_PRIORITY, NULL };
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_deskprofile_config_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed.\n");
+        return NULL;
+    }
+
+    rule_filter = talloc_asprintf(state, "(objectclass=%s)",
+                                  IPA_DESKPROFILE_CONFIG);
+    if (rule_filter == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    subreq = sdap_get_generic_send(state, ev, opts, sh,
+                                   dp_opt_get_string(ipa_opts,
+                                                     IPA_DESKPROFILE_SEARCH_BASE),
+                                   LDAP_SCOPE_BASE, rule_filter,
+                                   attrs, NULL, 0,
+                                   dp_opt_get_int(opts->basic,
+                                                  SDAP_ENUM_SEARCH_TIMEOUT),
+                                   false);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_get_generic_send failed.\n");
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, ipa_deskprofile_get_config_done, req);
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+}
+
+static void
+ipa_deskprofile_get_config_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ipa_deskprofile_config_state *state;
+    size_t reply_count;
+    struct sysdb_attrs **reply = NULL;
+    errno_t ret;
+
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_deskprofile_config_state);
+
+    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Could not retrieve Desktop Profile config\n");
+        goto done;
+    }
+
+    if (reply_count == 0) {
+        /*
+         * When connecting to an old server that doesn't support Desktop
+         * Profile, the reply_count will be zero.
+         * In order to not throw a unnecessary error and fail let's just
+         * return ENOENT and print a debug message about it.
+         */
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Server doesn't support Desktop Profile.\n");
+        ret = ENOENT;
+        goto done;
+    } else if (reply_count != 1) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unexpected number of results, expected 1, got %zu.\n",
+              reply_count);
+        ret = EINVAL;
+        goto done;
+    }
+
+    state->config = reply[0];
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+errno_t
+ipa_deskprofile_get_config_recv(struct tevent_req *req,
+                                TALLOC_CTX *mem_ctx,
+                                struct sysdb_attrs **config)
+{
+    struct ipa_deskprofile_config_state *state;
+
+    state = tevent_req_data(req, struct ipa_deskprofile_config_state);
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *config = talloc_steal(mem_ctx, state->config);
+
+    return EOK;
+}
diff --git a/src/providers/ipa/ipa_deskprofile_config.h b/src/providers/ipa/ipa_deskprofile_config.h
new file mode 100644
index 000000000..c4a05b215
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_config.h
@@ -0,0 +1,45 @@
+/*
+    SSSD
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_DESKPROFILE_CONFIG_H_
+#define IPA_DESKPROFILE_CONFIG_H_
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/ldap/ldap_common.h"
+#include "db/sysdb.h"
+
+/* From ipa_deskprofile_config.c */
+struct tevent_req *
+ipa_deskprofile_get_config_send(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct sdap_handle *sh,
+                                struct sdap_options *opts,
+                                struct dp_option *ipa_opts);
+
+errno_t
+ipa_deskprofile_get_config_recv(struct tevent_req *req,
+                                TALLOC_CTX *mem_ctx,
+                                struct sysdb_attrs **config);
+
+#endif /* IPA_DESKPROFILE_CONFIG_H_ */
diff --git a/src/providers/ipa/ipa_deskprofile_private.h b/src/providers/ipa/ipa_deskprofile_private.h
new file mode 100644
index 000000000..1db154b76
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_private.h
@@ -0,0 +1,50 @@
+/*
+   SSSD
+
+   Authors:
+       Fabiano Fidêncio <fidencio@redhat.com>
+
+   Copyright (C) 2017 Red Hat
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_DESKPROFILE_PRIVATE_H_
+#define IPA_DESKPROFILE_PRIVATE_H_
+
+#define IPA_DESKPROFILE_CONFIG "ipaDeskProfileConfig"
+#define IPA_DESKPROFILE_RULE "ipaDeskProfileRule"
+#define IPA_DESKPROFILE_PRIORITY "ipaDeskProfilePriority"
+#define IPA_DESKPROFILE_DATA "ipaDeskData"
+
+#define DESKPROFILE_HOSTS_SUBDIR "deskprofile_hosts"
+#define DESKPROFILE_HOSTGROUPS_SUBDIR "deskprofile_hostgroups"
+
+#define IPA_SESSION_RULE_TYPE "sessionRuleType"
+
+#define IPA_DESKPROFILE_BASE_TMPL "cn=desktop-profile,%s"
+
+#define SYSDB_DESKPROFILE_BASE_TMPL "cn=desktop-profile,"SYSDB_TMPL_CUSTOM_BASE
+
+#define DESKPROFILE_RULES_SUBDIR "deskprofile_rules"
+
+#define DESKPROFILE_CONFIG_SUBDIR "deskprofile_config"
+
+struct deskprofile_rule {
+    const char *name;
+    int priority;
+    const char *data;
+};
+
+#endif /* IPA_DESKPROFILE_PRIVATE_H_ */
diff --git a/src/providers/ipa/ipa_deskprofile_rules.c b/src/providers/ipa/ipa_deskprofile_rules.c
new file mode 100644
index 000000000..65994356e
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_rules.c
@@ -0,0 +1,367 @@
+/*
+    SSSD
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async_private.h"
+#include "providers/ipa/ipa_rules_common.h"
+#include "providers/ipa/ipa_deskprofile_private.h"
+#include "providers/ipa/ipa_deskprofile_rules.h"
+#include "providers/ipa/ipa_deskprofile_rules_util.h"
+
+struct ipa_deskprofile_rule_state {
+    struct tevent_context *ev;
+    struct sdap_handle *sh;
+    struct sdap_options *opts;
+
+    int search_base_iter;
+    struct sdap_search_base **search_bases;
+
+    const char **attrs;
+    char *rules_filter;
+    char *cur_filter;
+
+    size_t rule_count;
+    struct sysdb_attrs **rules;
+};
+
+static errno_t
+ipa_deskprofile_rule_info_next(struct tevent_req *req,
+                               struct ipa_deskprofile_rule_state *state);
+static void
+ipa_deskprofile_rule_info_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_deskprofile_rule_info_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct sdap_handle *sh,
+                               struct sdap_options *opts,
+                               struct sdap_search_base **search_bases,
+                               struct sysdb_attrs *ipa_host,
+                               struct sss_domain_info *domain,
+                               const char *username)
+{
+    struct tevent_req *req = NULL;
+    struct ipa_deskprofile_rule_state *state;
+    char *user;
+    char *group;
+    char *host_dn_clean;
+    char *group_clean;
+    char *host_group_clean;
+    char *rule_filter;
+    const char *host_dn;
+    const char **memberof_list;
+    char **groups_list;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ipa_deskprofile_rule_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
+        return NULL;
+    }
+
+    if (ipa_host == NULL) {
+        ret = EINVAL;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing host\n");
+        goto immediate;
+    }
+
+    ret = sysdb_attrs_get_string(ipa_host, SYSDB_ORIG_DN, &host_dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not identify IPA hostname\n");
+        goto immediate;
+    }
+
+    ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
+    if (ret != EOK) {
+        goto immediate;
+    }
+
+    state->ev = ev;
+    state->sh = sh;
+    state->opts = opts;
+    state->search_bases = search_bases;
+    state->search_base_iter = 0;
+    state->attrs = deskprofile_get_attrs_to_get_cached_rules(state);
+    if (state->attrs == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "deskprofile_get_attrs_get_cached_rules() failed\n");
+        goto immediate;
+    }
+
+    rule_filter = talloc_asprintf(state,
+                                  "(&(objectclass=%s)"
+                                  "(%s=%s)"
+                                  "(|(%s=%s)(%s=%s)(%s=%s)",
+                                  IPA_DESKPROFILE_RULE,
+                                  IPA_ENABLED_FLAG, IPA_TRUE_VALUE,
+                                  IPA_HOST_CATEGORY, "all",
+                                  IPA_USER_CATEGORY, "all",
+                                  IPA_MEMBER_HOST, host_dn_clean);
+    if (rule_filter == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+
+    /* Add all parent groups of ipa_hostname to the filter */
+    ret = sysdb_attrs_get_string_array(ipa_host, SYSDB_ORIG_MEMBEROF,
+                                       state, &memberof_list);
+    if (ret != EOK && ret != ENOENT) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not identify.\n");
+    } else if (ret == ENOENT) {
+        /* This host is not a member of any hostgroups */
+        memberof_list = talloc_array(state, const char *, 1);
+        if (memberof_list == NULL) {
+            ret = ENOMEM;
+            goto immediate;
+        }
+        memberof_list[0] = NULL;
+    }
+
+    for (size_t i = 0; memberof_list[i] != NULL; i++) {
+        ret = sss_filter_sanitize(state,
+                                  memberof_list[i],
+                                  &host_group_clean);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "sss_filter_sanitize() failed [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto immediate;
+        }
+
+        rule_filter = talloc_asprintf_append(rule_filter, "(%s=%s)",
+                                             IPA_MEMBER_HOST,
+                                             host_group_clean);
+        if (rule_filter == NULL) {
+            ret = ENOMEM;
+            goto immediate;
+        }
+    }
+
+    /* Add the username to the filter */
+    ret = sss_parse_internal_fqname(state, username, &user, NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sss_parse_internal_fqname() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto immediate;
+    }
+
+    rule_filter = talloc_asprintf_append(rule_filter, "(%s=%s)",
+                                         IPA_MEMBER_USER, user);
+    if (rule_filter == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+
+    /* Add all parent groups of `username` to the filter */
+    ret = get_sysdb_grouplist(state, domain->sysdb, domain, username,
+                              &groups_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "get_sysdb_grouplist() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto immediate;
+    }
+
+    for (size_t i = 0; groups_list[i] != NULL; i++) {
+        ret = sss_filter_sanitize(state, groups_list[i], &group_clean);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "sss_filter_sanitize() failed [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto immediate;
+        }
+
+        ret = sss_parse_internal_fqname(state, group_clean, &group, NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "sss_parse_internal_fqname() failed [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto immediate;
+        }
+
+        rule_filter = talloc_asprintf_append(rule_filter, "(%s=%s)",
+                                             IPA_MEMBER_USER, group);
+        if (rule_filter == NULL) {
+            ret = ENOMEM;
+            goto immediate;
+        }
+    }
+
+    rule_filter = talloc_asprintf_append(rule_filter, "))");
+    if (rule_filter == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+    state->rules_filter = talloc_steal(state, rule_filter);
+
+    ret = ipa_deskprofile_rule_info_next(req, state);
+    if (ret != EAGAIN) {
+        if (ret == EOK) {
+            /* ipa_deskprofile_rule_info_next should always have a search base
+             * when called for the first time.
+             *
+             * For the subsequent iterations, not finding any more search bases
+             * is fine though (thus the function returns EOK).
+             *
+             * As, here, it's the first case happening, let's return EINVAL.
+             */
+            DEBUG(SSSDBG_CRIT_FAILURE, "No search base found\n");
+            ret = EINVAL;
+        }
+        goto immediate;
+    }
+
+    return req;
+
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t
+ipa_deskprofile_rule_info_next(struct tevent_req *req,
+                               struct ipa_deskprofile_rule_state *state)
+{
+    struct tevent_req *subreq;
+    struct sdap_search_base *base;
+
+    base = state->search_bases[state->search_base_iter];
+    if (base == NULL) {
+        return EOK;
+    }
+
+    talloc_zfree(state->cur_filter);
+    state->cur_filter = sdap_combine_filters(state, state->rules_filter,
+                                             base->filter);
+    if (state->cur_filter == NULL) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Sending request for next search base: [%s][%d][%s]\n",
+          base->basedn, base->scope, state->cur_filter);
+
+    subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+                                   base->basedn, base->scope,
+                                   state->cur_filter, state->attrs,
+                                   NULL, 0,
+                                   dp_opt_get_int(state->opts->basic,
+                                                  SDAP_ENUM_SEARCH_TIMEOUT),
+                                   true);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "sdap_get_generic_send failed.\n");
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq, ipa_deskprofile_rule_info_done, req);
+
+    return EAGAIN;
+}
+
+static void
+ipa_deskprofile_rule_info_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct ipa_deskprofile_rule_state *state;
+    size_t rule_count;
+    size_t total_count;
+    struct sysdb_attrs **rules;
+    struct sysdb_attrs **target;
+    int i;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_deskprofile_rule_state);
+
+    ret = sdap_get_generic_recv(subreq, state,
+                                &rule_count,
+                                &rules);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Could not retrieve Desktop Profile rules\n");
+        goto fail;
+    }
+
+    if (rule_count > 0) {
+        total_count = rule_count + state->rule_count;
+        state->rules = talloc_realloc(state, state->rules,
+                                      struct sysdb_attrs *,
+                                      total_count);
+        if (state->rules == NULL) {
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        i = 0;
+        while (state->rule_count < total_count) {
+            target = &state->rules[state->rule_count];
+            *target = talloc_steal(state->rules, rules[i]);
+
+            state->rule_count++;
+            i++;
+        }
+    }
+
+    state->search_base_iter++;
+    ret = ipa_deskprofile_rule_info_next(req, state);
+    if (ret == EAGAIN) {
+        return;
+    } else if (ret != EOK) {
+        goto fail;
+    } else if (ret == EOK && state->rule_count == 0) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No rules apply to this host\n");
+        tevent_req_error(req, ENOENT);
+        return;
+    }
+
+    /* We went through all search bases and we have some results */
+    tevent_req_done(req);
+
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+}
+
+errno_t
+ipa_deskprofile_rule_info_recv(struct tevent_req *req,
+                               TALLOC_CTX *mem_ctx,
+                               size_t *_rule_count,
+                               struct sysdb_attrs ***_rules)
+{
+    struct ipa_deskprofile_rule_state *state;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    state = tevent_req_data(req, struct ipa_deskprofile_rule_state);
+
+    *_rule_count = state->rule_count;
+    *_rules = talloc_steal(mem_ctx, state->rules);
+
+    return EOK;
+}
diff --git a/src/providers/ipa/ipa_deskprofile_rules.h b/src/providers/ipa/ipa_deskprofile_rules.h
new file mode 100644
index 000000000..313e52678
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_rules.h
@@ -0,0 +1,43 @@
+/*
+    SSSD
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_DESKPROFILE_RULES_H_
+#define IPA_DESKPROFILE_RULES_H_
+
+/* From ipa_deskprofile_rules.c */
+struct tevent_req *
+ipa_deskprofile_rule_info_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct sdap_handle *sh,
+                               struct sdap_options *opts,
+                               struct sdap_search_base **search_bases,
+                               struct sysdb_attrs *ipa_host,
+                               struct sss_domain_info *domain,
+                               const char *username);
+
+errno_t
+ipa_deskprofile_rule_info_recv(struct tevent_req *req,
+                               TALLOC_CTX *mem_ctx,
+                               size_t *rule_count,
+                               struct sysdb_attrs ***rules);
+
+#endif /* IPA_DESKPROFILE_RULES_H_ */
diff --git a/src/providers/ipa/ipa_deskprofile_rules_util.c b/src/providers/ipa/ipa_deskprofile_rules_util.c
new file mode 100644
index 000000000..1f5b7f9c5
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_rules_util.c
@@ -0,0 +1,932 @@
+/*
+    SSSD
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "providers/ipa/ipa_deskprofile_rules_util.h"
+#include "providers/ipa/ipa_deskprofile_private.h"
+#include "providers/ipa/ipa_rules_common.h"
+#include <ctype.h>
+#include <fcntl.h>
+
+#define DESKPROFILE_GLOBAL_POLICY_MIN_VALUE 1
+#define DESKPROFILE_GLOBAL_POLICY_MAX_VALUE 24
+
+enum deskprofile_name {
+    RULES_DIR = 0,
+    DOMAIN,
+    USERNAME,
+    PRIORITY,
+    USER,
+    GROUP,
+    HOST,
+    HOSTGROUP,
+    RULE_NAME,
+    EXTENSION,
+    DESKPROFILE_NAME_SENTINEL
+};
+
+/*
+ * The rule's filename has to follow a global policy, used by FleetCommander
+ * client that shows how the profile should be applied.
+ *
+ * This global policy is represented by an integer from 1 to 24 (inclusive) and
+ * has the following meaning:
+ *  1 = user, group, host, hostgroup
+ *  2 = user, group, hostgroup, host
+ *  3 = user, host, group, hostgroup
+ *  4 = user, host, hostgroup, group
+ *  5 = user, hostgroup, group, host
+ *  6 = user, hostgroup, host, group
+ *  7 = group, user, host, hostgroup
+ *  8 = group, user, hostgroup, host
+ *  9 = group, host, user, hostgroup
+ * 10 = group, host, hostgroup, user
+ * 11 = group, hostgroup, user, host
+ * 12 = group, hostgroup, host, user
+ * 13 = host, user, group, hostgroup
+ * 14 = host, user, hostgroup, group
+ * 15 = host, group, user, hostgroup
+ * 16 = host, group, hostgroup, user
+ * 17 = host, hostgroup, user, group
+ * 18 = host, hostgroup, group, user
+ * 19 = hostgroup, user, group, host
+ * 20 = hostgroup, user, host, group
+ * 21 = hostgroup, group, user, host
+ * 22 = hostgroup, group, host, user
+ * 23 = hostgroup, host, user, group
+ * 24 = hostgroup, host, group, user
+ *
+ * Having the table above in mind and considering the following example:
+ * - rule name: testrule
+ * - policy: 22
+ * - priority: 420
+ * - client's machine matches: host and group
+ *
+ * So, the filename will be: "000420_000000_000420_000420_000000_testrule.json"
+ *
+ * The function below not only helps us to create this filename in the correct
+ * format, but also create the whole path for this rule's file.
+ *
+ * An example of the full path would be:
+ * "/var/lib/sss/deskprofile/ipa.example/user_foobar/000420_000000_000420_000420_000000_testrule.json"
+ *  |       RULES DIR       |   DOMAIN  |  USERNAME |      |      |GROUP | HOST | USER |        |
+ *                                                  PRIORITY                            RULE NAME
+ *                                                         HOSTGROUP                            EXTENSION
+ *
+ * In case a element has to be added/remove, please, remember to update:
+ * - deskprofile_name enum;
+ * - permuts's matrix;
+ * - vals array;
+ */
+static errno_t
+ipa_deskprofile_get_filename_path(TALLOC_CTX *mem_ctx,
+                                  uint16_t config_priority,
+                                  const char *rules_dir,
+                                  const char *domain,
+                                  const char *username,
+                                  const char *priority,
+                                  const char *user_priority,
+                                  const char *group_priority,
+                                  const char *host_priority,
+                                  const char *hostgroup_priority,
+                                  const char *rule_name,
+                                  const char *extension,
+                                  char **_filename_path)
+{
+    TALLOC_CTX *tmp_ctx;
+    static const uint8_t permuts[][DESKPROFILE_NAME_SENTINEL] = {
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, USER, GROUP, HOST, HOSTGROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, USER, GROUP, HOSTGROUP, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, USER, HOST, GROUP, HOSTGROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, USER, HOST, HOSTGROUP, GROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, USER, HOSTGROUP, GROUP, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, USER, HOSTGROUP, HOST, GROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, GROUP, USER, HOST, HOSTGROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, GROUP, USER, HOSTGROUP, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, GROUP, HOST, USER, HOSTGROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, GROUP, HOST, HOSTGROUP, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, GROUP, HOSTGROUP, USER, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, GROUP, HOSTGROUP, HOST, USER, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOST, USER, GROUP, HOSTGROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOST, USER, HOSTGROUP, GROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOST, GROUP, USER, HOSTGROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOST, GROUP, HOSTGROUP, USER, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOST, HOSTGROUP, USER, GROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOST, HOSTGROUP, GROUP, USER, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOSTGROUP, USER, GROUP, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOSTGROUP, USER, HOST, GROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOSTGROUP, GROUP, USER, HOST, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOSTGROUP, GROUP, HOST, USER, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOSTGROUP, HOST, USER, GROUP, RULE_NAME, EXTENSION},
+        {RULES_DIR, DOMAIN, USERNAME, PRIORITY, HOSTGROUP, HOST, GROUP, USER, RULE_NAME, EXTENSION},
+    };
+    const char *vals[] = {
+        rules_dir,
+        domain,
+        username,
+        priority,
+        user_priority,
+        group_priority,
+        host_priority,
+        hostgroup_priority,
+        rule_name,
+        extension,
+        NULL,
+    };
+    const uint8_t *perms;
+    char *result;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    if (config_priority < DESKPROFILE_GLOBAL_POLICY_MIN_VALUE ||
+        config_priority > DESKPROFILE_GLOBAL_POLICY_MAX_VALUE) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "The configuration priority has an invalid value: %d!\n",
+              config_priority);
+        ret = EINVAL;
+        goto done;
+    }
+
+    perms = permuts[config_priority - 1];
+
+    result = talloc_strdup(tmp_ctx, "");
+    if (result == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (int i = 0; i < DESKPROFILE_NAME_SENTINEL; i++) {
+        switch(perms[i]) {
+            case RULES_DIR:
+            case DOMAIN:
+            case USERNAME:
+                result = talloc_asprintf_append(result, "%s/", vals[perms[i]]);
+                break;
+            case PRIORITY:
+            case USER:
+            case GROUP:
+            case HOST:
+            case HOSTGROUP:
+                result = talloc_asprintf_append(result, "%s_", vals[perms[i]]);
+                break;
+            case RULE_NAME:
+                result = talloc_asprintf_append(result, "%s", vals[perms[i]]);
+                break;
+            case EXTENSION:
+                result = talloc_asprintf_append(result, ".%s", vals[perms[i]]);
+                break;
+            default:
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      "This situation should never happen\n");
+                ret = EINVAL;
+                goto done;
+        }
+
+        if (result == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    *_filename_path = talloc_steal(mem_ctx, result);
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+errno_t
+ipa_deskprofile_rules_create_user_dir(
+                                    const char *username, /* fully-qualified */
+                                    uid_t uid,
+                                    gid_t gid)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *shortname;
+    char *domain;
+    char *domain_dir;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sss_parse_internal_fqname(tmp_ctx, username, &shortname, &domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sss_parse_internal_fqname() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = sss_create_dir(IPA_DESKPROFILE_RULES_USER_DIR, domain, 0755,
+                         getuid(), getgid());
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to create the directory \"%s/%s\" that would be used to "
+              "store the Desktop Profile rules users' directory [%d]: %s\n",
+              IPA_DESKPROFILE_RULES_USER_DIR, domain,
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    domain_dir = talloc_asprintf(tmp_ctx, IPA_DESKPROFILE_RULES_USER_DIR"/%s",
+                                 domain);
+    if (domain_dir == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sss_create_dir(domain_dir, shortname, 0600, uid, gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+               "Failed to create the directory \"%s/%s/%s\" that would be used "
+               "to store the Desktop Profile rules for the user \"%s\" [%d]: "
+               "%s\n",
+               IPA_DESKPROFILE_RULES_USER_DIR, domain, shortname, username,
+               ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t
+ipa_deskprofile_get_normalized_rule_name(TALLOC_CTX *mem_ctx,
+                                         const char *name,
+                                         char **_rule_name)
+{
+    char buffer[PATH_MAX];
+    size_t buffer_len;
+    size_t name_len;
+
+    name_len = strlen(name);
+    buffer_len = 0;
+    for (size_t i = 0; i < name_len; i++) {
+        char character;
+        bool replace;
+
+        character = name[i];
+        replace = false;
+
+        if (isalnum(character) == 0) {
+            char next_character;
+
+            next_character = name[i+1];
+            if (i + 1 >= name_len || isalnum(next_character) == 0) {
+                continue;
+            }
+
+            replace = true;
+        }
+
+        buffer[buffer_len] = replace ? '_' : character;
+        buffer_len++;
+    }
+    buffer[buffer_len] = '\0';
+
+    *_rule_name = talloc_strdup(mem_ctx, buffer);
+    if (*_rule_name == NULL) {
+        return ENOMEM;
+    }
+
+    return EOK;
+}
+
+static errno_t
+ipa_deskprofile_rule_check_memberuser(
+                                    TALLOC_CTX *mem_ctx,
+                                    struct sss_domain_info *domain,
+                                    struct sysdb_attrs *rule,
+                                    const char *rule_name,
+                                    const char *rule_prio,
+                                    const char *base_dn,
+                                    const char *username, /* fully-qualified */
+                                    char **_user_prio,
+                                    char **_group_prio)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_message_element *el;
+    struct ldb_result *res;
+    size_t num_groups;
+    char **groups = NULL;
+    const char *fqgroupname = NULL;
+    char *groupname = NULL;
+    char *shortname;
+    char *domainname;
+    char *data;
+    char *memberuser;
+    char *membergroup;
+    char *user_prio;
+    char *group_prio;
+    bool user = false;
+    bool group = false;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sss_parse_internal_fqname(tmp_ctx, username,
+                                    &shortname, &domainname);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sss_parse_internal_fqname() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = sysdb_initgroups(tmp_ctx, domain, username, &res);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sysdb_initgroups() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    if (res->count == 0) {
+        /* This really should NOT happen at this point */
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "User [%s] not found in cache\n", username);
+        ret = ENOENT;
+        goto done;
+    }
+
+    groups = talloc_array(tmp_ctx, char *, res->count);
+    if (groups == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    num_groups = 0;
+    /* Start counting from 1 to exclude the user entry */
+    for (size_t i = 1; i < res->count; i++) {
+        fqgroupname = ldb_msg_find_attr_as_string(res->msgs[i],
+                                                  SYSDB_NAME,
+                                                  NULL);
+        if (fqgroupname == NULL) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Skipping malformed entry [%s]\n",
+                  ldb_dn_get_linearized(res->msgs[i]->dn));
+            continue;
+        }
+
+        ret = sss_parse_internal_fqname(tmp_ctx, fqgroupname,
+                                        &groupname, NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Malformed name %s, skipping!\n", fqgroupname);
+            continue;
+        }
+
+        groups[num_groups] = groupname;
+        num_groups++;
+    }
+    groups[num_groups] = NULL;
+
+    ret = sysdb_attrs_get_el(rule, IPA_MEMBER_USER, &el);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Failed to get the Desktop Profile Rule memberUser for rule "
+              "\"%s\" [%d]: %s\n",
+              rule_name, ret, sss_strerror(ret));
+
+        goto done;
+    }
+
+    memberuser = talloc_asprintf(tmp_ctx, "uid=%s,cn=users,cn=accounts,%s",
+                                 shortname, base_dn);
+    if (memberuser == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate memberuser\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (size_t i = 0; i < el->num_values; i++) {
+        if (user && group) {
+            break;
+        }
+
+        data = (char *)el->values[i].data;
+
+        if (!user && data != NULL && strcmp(memberuser, data) == 0) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Desktop Profile rule \"%s\" matches with the user \"%s\" "
+                  "for the \"%s\" domain!\n",
+                  rule_name, shortname, domainname);
+            user = true;
+            continue;
+        }
+
+        if (!group && data != NULL) {
+            for (size_t j = 0; !group && groups[j] != NULL; j++) {
+                membergroup = talloc_asprintf(tmp_ctx,
+                                              "cn=%s,cn=groups,cn=accounts,%s",
+                                              groups[j], base_dn);
+                if (membergroup == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Failed to allocate membergroup\n");
+                    ret = ENOMEM;
+                    goto done;
+                }
+
+                if (strcmp(membergroup, data) == 0) {
+                    DEBUG(SSSDBG_TRACE_FUNC,
+                          "Desktop Profile rule \"%s\" matches with (at least) "
+                          "the group \"%s\" for the \"%s\" domain!\n",
+                          rule_name, groups[j], domainname);
+                    group = true;
+                }
+            }
+        }
+    }
+
+    user_prio = user ? talloc_strdup(tmp_ctx, rule_prio) :
+                       talloc_asprintf(tmp_ctx, "%06d", 0);
+    if (user_prio == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the user priority\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    group_prio = group ? talloc_strdup(tmp_ctx, rule_prio) :
+                         talloc_asprintf(tmp_ctx, "%06d", 0);
+    if (group_prio == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the group priority\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    *_user_prio = talloc_steal(mem_ctx, user_prio);
+    *_group_prio = talloc_steal(mem_ctx, group_prio);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t
+ipa_deskprofile_rule_check_memberhost(TALLOC_CTX *mem_ctx,
+                                      struct sss_domain_info *domain,
+                                      struct sysdb_attrs *rule,
+                                      const char *rule_name,
+                                      const char *rule_prio,
+                                      const char *base_dn,
+                                      const char *hostname,
+                                      char **_host_prio,
+                                      char **_hostgroup_prio)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_dn *host_dn;
+    struct ldb_message_element *el_orig_memberof = NULL;
+    struct ldb_message_element *el = NULL;
+    struct ldb_message **msgs;
+    size_t count;
+    size_t num_memberhostgroup;
+    char **memberhostgroups = NULL;
+    char *data;
+    char *memberhost;
+    char *memberhostgroup;
+    char *name;
+    char *host_prio;
+    char *hostgroup_prio;
+    const char *memberof_attrs[] = { SYSDB_ORIG_MEMBEROF, NULL };
+    bool host = false;
+    bool hostgroup = false;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    host_dn = sysdb_custom_dn(tmp_ctx, domain, hostname,
+                              DESKPROFILE_HOSTS_SUBDIR);
+    if (host_dn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmp_ctx, domain->sysdb, host_dn,
+                             LDB_SCOPE_BASE, NULL,
+                             memberof_attrs,
+                             &count, &msgs);
+    if (ret == ENOENT || count == 0) {
+        memberhostgroups = talloc_array(tmp_ctx, char *, 1);
+        memberhostgroups[0] = NULL;
+    } else if (ret != EOK) {
+        goto done;
+    } else if (count > 1) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "More than one result for a BASE search!\n");
+        ret = EIO;
+        goto done;
+    } else { /* ret == EOK && count == 1 */
+        el_orig_memberof = ldb_msg_find_element(msgs[0], SYSDB_ORIG_MEMBEROF);
+        memberhostgroups = talloc_array(tmp_ctx,
+                                        char *,
+                                        el_orig_memberof->num_values);
+    }
+
+    if (el_orig_memberof != NULL) {
+        num_memberhostgroup = 0;
+        for (size_t i = 0; i < el_orig_memberof->num_values; i++) {
+            data = (char *)el_orig_memberof->values[i].data;
+
+            ret = ipa_common_get_hostgroupname(tmp_ctx, domain->sysdb, data,
+                                               &name);
+
+            /* ERR_UNEXPECTED_ENTRY_TYPE means we had a memberOf entry that
+             * wasn't a host group, thus we'll just ignore those.
+             */
+            if (ret != EOK && ret != ERR_UNEXPECTED_ENTRY_TYPE) {
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      "Skipping malformed entry [%s]\n",
+                      data);
+                continue;
+            } else if (ret == EOK) {
+                memberhostgroups[num_memberhostgroup] = name;
+                num_memberhostgroup++;
+            }
+        }
+        memberhostgroups[num_memberhostgroup] = NULL;
+    }
+
+    ret = sysdb_attrs_get_el(rule, IPA_MEMBER_HOST, &el);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Failed to get the Desktop Profile Rule memberHost for rule "
+              "\"%s\" [%d]: %s\n",
+              rule_name, ret, sss_strerror(ret));
+
+        goto done;
+    }
+
+    memberhost = talloc_asprintf(tmp_ctx, "fqdn=%s,cn=computers,cn=accounts,%s",
+                                 hostname, base_dn);
+    if (memberhost == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate memberhost\n");
+        goto done;
+    }
+
+    for (size_t i = 0; i < el->num_values; i++) {
+        if (host && hostgroup) {
+            break;
+        }
+
+        data = (char *)el->values[i].data;
+
+        if (!host && data != NULL && strcmp(memberhost, data) == 0) {
+            host = true;
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Desktop Profile rule \"%s\" matches with the host \"%s\" "
+                  "for the \"%s\" domain!\n",
+                  rule_name, hostname, domain->name);
+            continue;
+        }
+
+        if (!hostgroup && data != NULL) {
+            for (size_t j = 0; !hostgroup && memberhostgroups[j] != NULL; j++) {
+                memberhostgroup = talloc_asprintf(
+                                        tmp_ctx,
+                                        "cn=%s,cn=hostgroups,cn=accounts,%s",
+                                        memberhostgroups[j], base_dn);
+
+                if (memberhostgroup == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE,
+                          "Failed to allocate memberhostgroup\n");
+                    ret = ENOMEM;
+                    goto done;
+                }
+
+                if (strcmp(memberhostgroup, data) == 0) {
+                    hostgroup = true;
+                    DEBUG(SSSDBG_TRACE_FUNC,
+                          "Desktop Profile rule \"%s\" matches with (at least) "
+                          "the hostgroup \"%s\" for the \"%s\" domain!\n",
+                          rule_name, memberhostgroups[j], domain->name);
+                    continue;
+                }
+            }
+        }
+    }
+
+    host_prio = host ? talloc_strdup(tmp_ctx, rule_prio) :
+                       talloc_asprintf(tmp_ctx, "%06d", 0);
+    if (host_prio == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the host priority\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    hostgroup_prio = hostgroup ? talloc_strdup(tmp_ctx, rule_prio) :
+                                 talloc_asprintf(tmp_ctx, "%06d", 0);
+    if (hostgroup_prio == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the hostgroup priority\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    *_host_prio = talloc_steal(mem_ctx, host_prio);
+    *_hostgroup_prio = talloc_steal(mem_ctx, hostgroup_prio);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+
+errno_t
+ipa_deskprofile_rules_save_rule_to_disk(
+                                    TALLOC_CTX *mem_ctx,
+                                    uint16_t priority,
+                                    struct sysdb_attrs *rule,
+                                    struct sss_domain_info *domain,
+                                    const char *hostname,
+                                    const char *username, /* fully-qualified */
+                                    uid_t uid,
+                                    gid_t gid)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *rule_name;
+    const char *data;
+    char *shortname;
+    char *domainname;
+    char *base_dn;
+    char *rule_prio;
+    char *user_prio;
+    char *group_prio;
+    char *host_prio;
+    char *hostgroup_prio;
+    char *normalized_rule_name = NULL;
+    char *filename_path = NULL;
+    const char *extension = "json";
+    uint32_t prio;
+    int fd = -1;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(mem_ctx);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_attrs_get_string(rule, IPA_CN, &rule_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Failed to get the Desktop Profile Rule name [%d]: %s\n",
+              ret, sss_strerror(ret));
+
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_uint32_t(rule, IPA_DESKPROFILE_PRIORITY, &prio);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Failed to get the Desktop Profile Rule priority for rule "
+              "\"%s\" [%d]: %s\n",
+              rule_name, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    rule_prio = talloc_asprintf(tmp_ctx, "%06d", prio);
+    if (rule_prio == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate rule priority\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_string(rule, IPA_DESKPROFILE_DATA, &data);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Failed to get the Desktop Profile Rule data for rule \"%s\" "
+              "[%d]: %s\n",
+              rule_name, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = sss_parse_internal_fqname(tmp_ctx, username, &shortname, &domainname);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sss_parse_internal_fqname() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = domain_to_basedn(tmp_ctx, domainname, &base_dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "domain_to_basedn() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = ipa_deskprofile_rule_check_memberuser(tmp_ctx, domain, rule,
+                                                rule_name, rule_prio,
+                                                base_dn, username,
+                                                &user_prio, &group_prio);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_deskprofile_rule_check_memberuser() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = ipa_deskprofile_rule_check_memberhost(tmp_ctx, domain, rule,
+                                                rule_name, rule_prio,
+                                                base_dn, hostname,
+                                                &host_prio, &hostgroup_prio);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_deskprofile_rule_check_memberhost() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = ipa_deskprofile_get_normalized_rule_name(mem_ctx, rule_name,
+                                                   &normalized_rule_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_deskprofile_get_normalized_rule_name() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = ipa_deskprofile_get_filename_path(tmp_ctx,
+                                            priority,
+                                            IPA_DESKPROFILE_RULES_USER_DIR,
+                                            domainname,
+                                            shortname,
+                                            rule_prio,
+                                            user_prio,
+                                            group_prio,
+                                            host_prio,
+                                            hostgroup_prio,
+                                            normalized_rule_name,
+                                            extension,
+                                            &filename_path);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_deskprofile_get_filename_path() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    fd = open(filename_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    if (fd == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to create the Desktop Profile rule file \"%s\" "
+              "[%d]: %s\n",
+              filename_path, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = dprintf(fd, "%s", data);
+    if (ret < 0) {
+        ret = EIO;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to write the content of the Desktop Profile rule for "
+              "the \"%s\" file.\n",
+              filename_path);
+        goto done;
+    }
+
+    ret = fchown(fd, uid, gid);
+    if (ret != EOK) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to own the Desktop Profile Rule file \"%s\" [%d]: %s\n",
+              filename_path, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (fd != -1) {
+        close(fd);
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+errno_t
+ipa_deskprofile_rules_remove_user_dir(const char *user_dir)
+{
+    errno_t ret;
+
+    ret = sss_remove_tree(user_dir);
+    if (ret == ENOENT) {
+        return EOK;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Cannot remove \"%s\" directory [%d]: %s\n",
+              user_dir, ret, sss_strerror(ret));
+        return ret;
+    }
+
+    return EOK;
+}
+
+errno_t
+deskprofile_get_cached_priority(struct sss_domain_info *domain,
+                                uint16_t *_priority)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *attrs[] = { IPA_DESKPROFILE_PRIORITY, NULL };
+    struct ldb_message **resp;
+    size_t resp_count;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_search_custom_by_name(tmp_ctx,
+                                      domain,
+                                      IPA_DESKPROFILE_PRIORITY,
+                                      DESKPROFILE_CONFIG_SUBDIR,
+                                      attrs, &resp_count, &resp);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sysdb_search_custom_by_name() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    if (resp_count != 1) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sysdb_search_custom_by_name() got more attributes than "
+              "expected. Expected (%d), got (%"PRIu64")\n", 1, resp_count);
+        ret = EINVAL;
+        goto done;
+    }
+
+    *_priority = ldb_msg_find_attr_as_uint(resp[0],
+                                           IPA_DESKPROFILE_PRIORITY,
+                                           0);
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+const char **
+deskprofile_get_attrs_to_get_cached_rules(TALLOC_CTX *mem_ctx)
+{
+    const char **attrs = talloc_zero_array(mem_ctx, const char *, 11);
+    if (attrs == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array() failed\n");
+        goto done;
+    }
+
+    attrs[0] = OBJECTCLASS;
+    attrs[1] = IPA_CN;
+    attrs[2] = IPA_UNIQUE_ID;
+    attrs[3] = IPA_ENABLED_FLAG;
+    attrs[4] = IPA_MEMBER_USER;
+    attrs[5] = IPA_USER_CATEGORY;
+    attrs[6] = IPA_MEMBER_HOST;
+    attrs[7] = IPA_HOST_CATEGORY;
+    attrs[8] = IPA_DESKPROFILE_PRIORITY;
+    attrs[9] = IPA_DESKPROFILE_DATA;
+    attrs[10] = NULL;
+
+done:
+    return attrs;
+}
diff --git a/src/providers/ipa/ipa_deskprofile_rules_util.h b/src/providers/ipa/ipa_deskprofile_rules_util.h
new file mode 100644
index 000000000..61f404df8
--- /dev/null
+++ b/src/providers/ipa/ipa_deskprofile_rules_util.h
@@ -0,0 +1,57 @@
+/*
+    SSSD
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_DESKPROFILE_RULES_UTIL_H_
+#define IPA_DESKPROFILE_RULES_UTIL_H_
+
+#include "db/sysdb.h"
+
+#ifndef IPA_DESKPROFILE_RULES_USER_DIR
+#define IPA_DESKPROFILE_RULES_USER_DIR  SSS_STATEDIR"/deskprofile"
+#endif /* IPA_DESKPROFILE_RULES_USER_DIR */
+
+errno_t
+ipa_deskprofile_rules_create_user_dir(
+                                    const char *username, /* fully-qualified */
+                                    uid_t uid,
+                                    gid_t gid);
+errno_t
+ipa_deskprofile_rules_save_rule_to_disk(
+                                    TALLOC_CTX *mem_ctx,
+                                    uint16_t priority,
+                                    struct sysdb_attrs *rule,
+                                    struct sss_domain_info *domain,
+                                    const char *hostname,
+                                    const char *username, /* fully-qualified */
+                                    uid_t uid,
+                                    gid_t gid);
+errno_t
+ipa_deskprofile_rules_remove_user_dir(const char *user_dir);
+
+errno_t
+deskprofile_get_cached_priority(struct sss_domain_info *domain,
+                                uint16_t *_priority);
+
+const char **
+deskprofile_get_attrs_to_get_cached_rules(TALLOC_CTX *mem_ctx);
+
+#endif /* IPA_DESKPROFILE_RULES_UTIL_H_ */
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 7dec4d1fb..b3daa4921 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -42,6 +42,7 @@
 #include "providers/ipa/ipa_subdomains.h"
 #include "providers/ipa/ipa_srv.h"
 #include "providers/be_dyndns.h"
+#include "providers/ipa/ipa_session.h"
 
 #define DNS_SRV_MISCONFIGURATION "SRV discovery is enabled on the IPA " \
     "server while using custom dns_discovery_domain. DNS discovery of " \
@@ -940,3 +941,60 @@ errno_t sssm_ipa_sudo_init(TALLOC_CTX *mem_ctx,
     return EOK;
 #endif
 }
+
+errno_t sssm_ipa_session_init(TALLOC_CTX *mem_ctx,
+                              struct be_ctx *be_ctx,
+                              void *module_data,
+                              struct dp_method *dp_methods)
+{
+    struct ipa_session_ctx *session_ctx;
+    struct ipa_init_ctx *init_ctx;
+    struct ipa_id_ctx *id_ctx;
+    bool enabled;
+    errno_t ret;
+
+    init_ctx = talloc_get_type(module_data, struct ipa_init_ctx);
+    id_ctx = init_ctx->id_ctx;
+
+    enabled = dp_opt_get_bool(id_ctx->ipa_options->basic,
+                              IPA_ENABLE_DESKPROFILE);
+    if (!enabled) {
+        DEBUG(SSSDBG_TRACE_FUNC, "ipa_enable_deskprofile is set to FALSE\n");
+
+        return EOK;
+    }
+
+    session_ctx = talloc_zero(mem_ctx, struct ipa_session_ctx);
+    if (session_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed.\n");
+
+        return ENOMEM;
+    }
+
+    session_ctx->sdap_ctx = id_ctx->sdap_id_ctx;
+    session_ctx->host_map = id_ctx->ipa_options->host_map;
+    session_ctx->hostgroup_map = id_ctx->ipa_options->hostgroup_map;
+    session_ctx->host_search_bases = id_ctx->ipa_options->host_search_bases;
+    session_ctx->deskprofile_search_bases = id_ctx->ipa_options->deskprofile_search_bases;
+
+    ret = dp_copy_options(session_ctx, id_ctx->ipa_options->basic,
+                          IPA_OPTS_BASIC, &session_ctx->ipa_options);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "dp_copy_options() failed.\n");
+
+        goto done;
+    }
+
+    dp_set_method(dp_methods, DPM_SESSION_HANDLER,
+                  ipa_pam_session_handler_send, ipa_pam_session_handler_recv, session_ctx,
+                  struct ipa_session_ctx, struct pam_data, struct pam_data *);
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_free(session_ctx);
+    }
+
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index f9f3a2a69..65e1d1453 100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -48,6 +48,9 @@ struct dp_option ipa_basic_opts[] = {
     { "ipa_server_mode", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ipa_views_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_confd_path", DP_OPT_STRING, { KRB5_MAPPING_DIR }, NULL_STRING },
+    { "ipa_enable_deskprofile", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "ipa_deskprofile_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ipa_deskprofike_refresh", DP_OPT_NUMBER, { .number = 5 }, NULL_NUMBER },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_session.c b/src/providers/ipa/ipa_session.c
new file mode 100644
index 000000000..406cf8ba1
--- /dev/null
+++ b/src/providers/ipa/ipa_session.c
@@ -0,0 +1,832 @@
+/*
+    SSSD
+
+    IPA Backend Module -- Session Management
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <security/pam_modules.h>
+
+#include "util/child_common.h"
+#include "providers/ldap/sdap_async.h"
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_config.h"
+#include "providers/ipa/ipa_hosts.h"
+#include "providers/ipa/ipa_subdomains.h"
+#include "providers/ipa/ipa_session.h"
+#include "providers/ipa/ipa_rules_common.h"
+#include "providers/ipa/ipa_deskprofile_private.h"
+#include "providers/ipa/ipa_deskprofile_config.h"
+#include "providers/ipa/ipa_deskprofile_rules.h"
+#include "providers/ipa/ipa_deskprofile_rules_util.h"
+
+/* Those here are used for sending a message to the deskprofile client
+ * informing that our side is done. */
+#define SSS_FLEETCOMMANDERCLIENT_BUS "org.freedesktop.FleetCommanderClient"
+#define SSS_FLEETCOMMANDERCLIENT_PATH "/org/freedesktop/FleetCommanderClient"
+#define SSS_FLEETCOMMANDERCLIENT_IFACE "org.freedesktop.FleetCommanderClient"
+
+struct ipa_fetch_deskprofile_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct sdap_id_ctx *sdap_ctx;
+    struct ipa_session_ctx *session_ctx;
+    struct sdap_id_op *sdap_op;
+    struct dp_option *ipa_options;
+    struct sdap_search_base **search_bases;
+    const char *username;
+
+    /* Hosts */
+    struct ipa_common_entries *hosts;
+    struct sysdb_attrs *ipa_host;
+
+    /* Rules */
+    struct ipa_common_entries *rules;
+    struct sysdb_attrs *config;
+    uint16_t priority;
+};
+
+static errno_t ipa_fetch_deskprofile_retry(struct tevent_req *req);
+static void ipa_fetch_deskprofile_connect_done(struct tevent_req *subreq);
+static errno_t ipa_fetch_deskprofile_hostinfo(struct tevent_req *req);
+static void ipa_fetch_deskprofile_hostinfo_done(struct tevent_req *subreq);
+static void ipa_fetch_deskprofile_config_done(struct tevent_req *subreq);
+static void ipa_fetch_deskprofile_rules_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ipa_fetch_deskprofile_send(TALLOC_CTX *mem_ctx,
+                           struct tevent_context *ev,
+                           struct be_ctx *be_ctx,
+                           struct ipa_session_ctx *session_ctx,
+                           const char *username)
+{
+    struct ipa_fetch_deskprofile_state *state;
+    struct tevent_req *req;
+    time_t now;
+    time_t refresh_interval;
+    bool offline;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_fetch_deskprofile_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->session_ctx = session_ctx;
+    state->sdap_ctx = session_ctx->sdap_ctx;
+    state->ipa_options = session_ctx->ipa_options;
+    state->search_bases = session_ctx->deskprofile_search_bases;
+    state->username = username;
+    state->hosts = talloc_zero(state, struct ipa_common_entries);
+    if (state->hosts == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+    state->rules = talloc_zero(state, struct ipa_common_entries);
+    if (state->rules == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    if (state->search_bases == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "No Desktop Profile search base found.\n");
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    state->sdap_op = sdap_id_op_create(state,
+                                       state->sdap_ctx->conn->conn_cache);
+    if (state->sdap_op == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n");
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    offline = be_is_offline(be_ctx);
+    DEBUG(SSSDBG_TRACE_ALL, "Connection status is [%s].\n",
+          offline ? "offline" : "online");
+
+    refresh_interval = dp_opt_get_int(state->ipa_options,
+                                      IPA_DESKPROFILE_REFRESH);
+    now = time(NULL);
+
+    if (offline || now < session_ctx->last_update + refresh_interval) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Performing cached Desktop Profile evaluation\n");
+        ret = EOK;
+        goto immediately;
+    }
+
+    ret = ipa_fetch_deskprofile_retry(req);
+    if (ret != EAGAIN) {
+        goto immediately;
+    }
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t
+ipa_fetch_deskprofile_retry(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ipa_fetch_deskprofile_state *state;
+    int ret;
+
+    state = tevent_req_data(req, struct ipa_fetch_deskprofile_state);
+
+    subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sdap_id_op_connect_send() failed: %d (%s)\n",
+              ret, strerror(ret));
+
+        return ret;
+    }
+
+    tevent_req_set_callback(subreq, ipa_fetch_deskprofile_connect_done, req);
+
+    return EAGAIN;
+}
+
+static void
+ipa_fetch_deskprofile_connect_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = NULL;
+    int dp_error;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = ipa_fetch_deskprofile_hostinfo(req);
+    if (ret == EAGAIN) {
+        return;
+    }
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+}
+
+static errno_t
+ipa_fetch_deskprofile_hostinfo(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ipa_fetch_deskprofile_state *state;
+    const char *hostname;
+
+    state = tevent_req_data(req, struct ipa_fetch_deskprofile_state);
+    hostname = dp_opt_get_string(state->ipa_options, IPA_HOSTNAME);
+
+    subreq = ipa_host_info_send(state,
+                                state->ev,
+                                sdap_id_op_handle(state->sdap_op),
+                                state->sdap_ctx->opts,
+                                hostname,
+                                state->session_ctx->host_map,
+                                state->session_ctx->hostgroup_map,
+                                state->session_ctx->host_search_bases);
+    if (subreq == NULL) {
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(subreq, ipa_fetch_deskprofile_hostinfo_done, req);
+
+    return EAGAIN;
+}
+
+static void
+ipa_fetch_deskprofile_hostinfo_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ipa_fetch_deskprofile_state *state;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_fetch_deskprofile_state);
+
+    ret = ipa_host_info_recv(subreq, state,
+                             &state->hosts->entry_count,
+                             &state->hosts->entries,
+                             &state->hosts->group_count,
+                             &state->hosts->groups);
+    state->hosts->entry_subdir = DESKPROFILE_HOSTS_SUBDIR;
+    state->hosts->group_subdir = DESKPROFILE_HOSTGROUPS_SUBDIR;
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = ipa_get_host_attrs(state->ipa_options,
+                             state->hosts->entry_count,
+                             state->hosts->entries,
+                             &state->ipa_host);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not locate IPA host.\n");
+        goto done;
+    }
+
+    subreq = ipa_deskprofile_get_config_send(state,
+                                             state->ev,
+                                             sdap_id_op_handle(state->sdap_op),
+                                             state->sdap_ctx->opts,
+                                             state->ipa_options);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, ipa_fetch_deskprofile_config_done, req);
+    return;
+
+done:
+    tevent_req_error(req, ret);
+}
+
+static void
+ipa_fetch_deskprofile_config_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ipa_fetch_deskprofile_state *state;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_fetch_deskprofile_state);
+
+    ret = ipa_deskprofile_get_config_recv(subreq, state, &state->config);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sysdb_store_custom(state->be_ctx->domain, IPA_DESKPROFILE_PRIORITY,
+                             DESKPROFILE_CONFIG_SUBDIR, state->config);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save Desktop Profile policy\n");
+        goto done;
+    }
+
+    subreq = ipa_deskprofile_rule_info_send(state,
+                                            state->ev,
+                                            sdap_id_op_handle(state->sdap_op),
+                                            state->sdap_ctx->opts,
+                                            state->search_bases,
+                                            state->ipa_host,
+                                            state->be_ctx->domain,
+                                            state->username);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, ipa_fetch_deskprofile_rules_done, req);
+    return;
+
+done:
+    tevent_req_error(req, ret);
+}
+
+static void
+ipa_fetch_deskprofile_rules_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ipa_fetch_deskprofile_state *state;
+    int dp_error;
+    errno_t ret;
+    bool found;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_fetch_deskprofile_state);
+
+    ret = ipa_deskprofile_rule_info_recv(subreq,
+                                         state,
+                                         &state->rules->entry_count,
+                                         &state->rules->entries);
+    state->rules->entry_subdir = DESKPROFILE_RULES_SUBDIR;
+    talloc_zfree(subreq);
+    if (ret == ENOENT) {
+        /* Set ret to EOK so we can safely call sdap_id_op_done. */
+        ret = EOK;
+        found = false;
+    } else if (ret == EOK) {
+        found = true;
+    } else {
+        goto done;
+    }
+
+    ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+    if (dp_error == DP_ERR_OK && ret != EOK) {
+        /* retry */
+        ret = ipa_fetch_deskprofile_retry(req);
+        if (ret != EAGAIN) {
+            tevent_req_error(req, ret);
+        }
+        return;
+    } else if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+   /* For now, let's completely purge the previous stored
+    * rules before saving the new ones */
+    ret = ipa_common_purge_rules(state->be_ctx->domain,
+                                 DESKPROFILE_RULES_SUBDIR);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Unable to remove Desktop Profile rules\n");
+        goto done;
+    }
+
+    if (!found) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    ret = ipa_common_save_rules(state->be_ctx->domain,
+                                state->hosts, NULL, state->rules,
+                                &state->session_ctx->last_update);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to save Desktop Profile rules\n");
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static errno_t
+ipa_fetch_deskprofile_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+struct ipa_pam_session_handler_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct ipa_session_ctx *session_ctx;
+    struct pam_data *pd;
+
+    /* Those attributes are used for:
+     * - saving the deskprofile rules to the disk;
+     * - deleting the deskprofile rules from the disk;
+     * - contacting the deskprofile client that everything is ready;
+     */
+    char *shortname;
+    char *domain;
+    char *user_dir;
+    uid_t uid;
+    gid_t gid;
+};
+
+static errno_t
+ipa_pam_session_handler_get_deskprofile_user_info(
+                                                TALLOC_CTX *mem_ctx,
+                                                struct sss_domain_info *domain,
+                                                const char *username,
+                                                char **_shortname,
+                                                char **_domain,
+                                                char **_user_dir,
+                                                uid_t *uid,
+                                                gid_t *gid);
+static void ipa_pam_session_handler_done(struct tevent_req *subreq);
+static errno_t
+ipa_pam_session_handler_save_deskprofile_rules(
+                                    struct be_ctx *be_ctx,
+                                    struct sss_domain_info *domain,
+                                    const char *username, /* fully-qualified */
+                                    const char *user_dir,
+                                    const char *hostname,
+                                    uid_t uid,
+                                    gid_t gid);
+static errno_t
+ipa_pam_session_handler_notify_deskprofile_client(uid_t uid,
+                                                  const char *user_dir,
+                                                  uint16_t prio);
+
+
+struct tevent_req *
+ipa_pam_session_handler_send(TALLOC_CTX *mem_ctx,
+                             struct ipa_session_ctx *session_ctx,
+                             struct pam_data *pd,
+                             struct dp_req_params *params)
+{
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct ipa_pam_session_handler_state *state;
+    errno_t ret;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Retrieving Desktop Profile rules\n");
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_pam_session_handler_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    state->pd = pd;
+    state->ev = params->ev;
+    state->be_ctx = params->be_ctx;
+    state->session_ctx = session_ctx;
+
+    /* Get all the user info that will be needed in order the delete the
+     * user's deskprofile directory from the disk, create the user's directory,
+     * save the fetched rules to the disk and notify the deskprofile client
+     * that this operation is done. */
+    ret = ipa_pam_session_handler_get_deskprofile_user_info(
+                                                        state,
+                                                        state->be_ctx->domain,
+                                                        pd->user,
+                                                        &state->shortname,
+                                                        &state->domain,
+                                                        &state->user_dir,
+                                                        &state->uid,
+                                                        &state->gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_deskprofile_get_user_info() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        state->pd->pam_status = PAM_SESSION_ERR;
+        goto done;
+    }
+
+    /* As no proper merging mechanism has been implemented yet ...
+     * let's just remove the user directory stored in the disk as it's
+     * going to be created again in case there's any rule fetched. */
+    ret = ipa_deskprofile_rules_remove_user_dir(state->user_dir);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_deskprofile_rules_remove_user_dir() failed.\n");
+        state->pd->pam_status = PAM_SESSION_ERR;
+        goto done;
+    }
+
+    subreq = ipa_fetch_deskprofile_send(state, state->ev, state->be_ctx,
+                                        state->session_ctx, pd->user);
+    if (subreq == NULL) {
+        state->pd->pam_status = PAM_SESSION_ERR;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, ipa_pam_session_handler_done, req);
+    return req;
+
+done:
+    tevent_req_done(req);
+    tevent_req_post(req, params->ev);
+
+    return req;
+}
+
+static void
+ipa_pam_session_handler_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ipa_pam_session_handler_state *state;
+    const char *hostname;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_pam_session_handler_state);
+
+    ret = ipa_fetch_deskprofile_recv(subreq);
+    talloc_free(subreq);
+
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_IMPORTANT_INFO, "No Desktop Profile rules found\n");
+        state->pd->pam_status = PAM_SUCCESS;
+        goto done;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Unable to fetch Desktop Profile rules [%d]: %s\n",
+              ret, sss_strerror(ret));
+        state->pd->pam_status = PAM_SYSTEM_ERR;
+        goto done;
+    }
+
+    hostname = dp_opt_get_string(state->session_ctx->ipa_options, IPA_HOSTNAME);
+    ret = ipa_pam_session_handler_save_deskprofile_rules(state->be_ctx,
+                                                         state->be_ctx->domain,
+                                                         state->pd->user,
+                                                         state->user_dir,
+                                                         hostname,
+                                                         state->uid,
+                                                         state->gid);
+
+    state->pd->pam_status = (ret == EOK) ? PAM_SUCCESS : PAM_SESSION_ERR;
+
+done:
+    /* TODO For backward compatibility we always return EOK to DP now. */
+    tevent_req_done(req);
+}
+
+errno_t
+ipa_pam_session_handler_recv(TALLOC_CTX *mem_ctx,
+                             struct tevent_req *req,
+                             struct pam_data **_data)
+{
+    struct ipa_pam_session_handler_state *state = NULL;
+
+    state = tevent_req_data(req, struct ipa_pam_session_handler_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_data = talloc_steal(mem_ctx, state->pd);
+
+    return EOK;
+}
+
+static errno_t
+ipa_pam_session_handler_get_deskprofile_user_info(TALLOC_CTX *mem_ctx,
+                                                  struct sss_domain_info *domain,
+                                                  const char *username,
+                                                  char **_shortname,
+                                                  char **_domain,
+                                                  char **_user_dir,
+                                                  uid_t *_uid,
+                                                  gid_t *_gid)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_result *res = NULL;
+    char *shortname;
+    char *domain_name;
+    char *user_dir;
+    uid_t uid;
+    gid_t gid;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sss_parse_internal_fqname(tmp_ctx, username,
+                                    &shortname, &domain_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Failed to parse \"%s\" [%d]: %s\n",
+              username, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    user_dir = talloc_asprintf(tmp_ctx, IPA_DESKPROFILE_RULES_USER_DIR"/%s/%s",
+                               domain_name, shortname);
+    if (user_dir == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed!\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_getpwnam(tmp_ctx, domain, username, &res);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_getpwnam() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    if (res->count != 1) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "sysdb_getpwnam() got more users than expected. "
+              "Expected [%d], got [%d]\n", 1, res->count);
+        ret = EINVAL;
+        goto done;
+    }
+
+    uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0);
+    gid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 0);
+    if (uid == 0 || gid == 0) {
+        /* As IPA doesn't handle root users ou groups, we know for sure that's
+         * something wrong in case we get uid = 0 or gid = 0.
+         */
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = EOK;
+
+    *_shortname = talloc_steal(mem_ctx, shortname);
+    *_domain = talloc_steal(mem_ctx, domain_name);
+    *_user_dir = talloc_steal(mem_ctx, user_dir);
+    *_uid = uid;
+    *_gid = gid;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t
+ipa_pam_session_handler_save_deskprofile_rules(
+                                    struct be_ctx *be_ctx,
+                                    struct sss_domain_info *domain,
+                                    const char *username, /* fully-qualified */
+                                    const char *user_dir,
+                                    const char *hostname,
+                                    uid_t uid,
+                                    gid_t gid)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char **attrs_get_cached_rules;
+    size_t rule_count;
+    struct sysdb_attrs **rules;
+    uint16_t priority;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    /* Get Desktop Profile priority from sysdb */
+    ret = deskprofile_get_cached_priority(be_ctx->domain, &priority);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "deskprofile_get_cached_priority() failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+
+    /* Get Desktop Profile rules from sysdb */
+    attrs_get_cached_rules = deskprofile_get_attrs_to_get_cached_rules(tmp_ctx);
+    if (attrs_get_cached_rules == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "deskprofile_get_attrs_get_cached_rules() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+    ret = ipa_common_get_cached_rules(tmp_ctx, be_ctx->domain,
+                                      IPA_DESKPROFILE_RULE,
+                                      DESKPROFILE_RULES_SUBDIR,
+                                      attrs_get_cached_rules,
+                                      &rule_count,
+                                      &rules);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not retrieve Desktop Profile rules from the cache\n");
+        goto done;
+    }
+
+    /* Create the user directory where the rules are going to be stored */
+    ret = ipa_deskprofile_rules_create_user_dir(username, uid, gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Cannot create the user directory [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    /* Save the rules to the disk */
+    for (size_t i = 0; i < rule_count; i++) {
+        ret = ipa_deskprofile_rules_save_rule_to_disk(tmp_ctx,
+                                                      priority,
+                                                      rules[i],
+                                                      domain,
+                                                      hostname,
+                                                      username,
+                                                      uid,
+                                                      gid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Failed to save a Desktop Profile Rule to disk [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            goto done;
+        }
+    }
+
+    /* Notify FleetCommander that our side is done */
+    ret = ipa_pam_session_handler_notify_deskprofile_client(uid,
+                                                            user_dir,
+                                                            priority);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ipa_pam_session_handler_notify_deskprofile_client() "
+              "failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static DBusConnection *
+ipa_deskprofile_client_connect(void)
+{
+    DBusConnection *conn;
+    DBusError error;
+
+    dbus_error_init(&error);
+    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+    if (dbus_error_is_set(&error)) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Unable to connect to the FleetCommanderClient bus [%s]: %s\n",
+              error.name, error.message);
+        conn = NULL;
+        goto done;
+    }
+
+done:
+    dbus_error_free(&error);
+    return conn;
+}
+
+static errno_t
+ipa_pam_session_handler_notify_deskprofile_client(uid_t uid,
+                                                  const char *user_dir,
+                                                  uint16_t prio)
+{
+    DBusConnection *conn = NULL;
+    DBusMessage *msg = NULL;
+    DBusError error;
+    errno_t ret;
+    bool dbus_ret;
+
+    dbus_error_init(&error);
+
+    conn = ipa_deskprofile_client_connect();
+    if (conn == NULL) {
+        ret = EIO;
+        goto done;
+    }
+
+    msg = sbus_create_message(NULL,
+                              SSS_FLEETCOMMANDERCLIENT_BUS,
+                              SSS_FLEETCOMMANDERCLIENT_PATH,
+                              SSS_FLEETCOMMANDERCLIENT_IFACE,
+                              "ProcessSSSDFiles",
+                              DBUS_TYPE_UINT32, &uid,
+                              DBUS_TYPE_STRING, &user_dir,
+                              DBUS_TYPE_UINT16, &prio);
+    if (msg == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create D-Bus Message!\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    dbus_ret = dbus_connection_send(conn, msg, NULL);
+    if (dbus_ret == FALSE) {
+        ret = EIO;
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (msg != NULL) {
+        dbus_message_unref(msg);
+    }
+
+    if (conn != NULL) {
+        dbus_connection_unref(conn);
+    }
+
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_session.h b/src/providers/ipa/ipa_session.h
new file mode 100644
index 000000000..aac99844d
--- /dev/null
+++ b/src/providers/ipa/ipa_session.h
@@ -0,0 +1,52 @@
+/*
+    SSSD
+
+    IPA Backend Module -- Session Management
+
+    Authors:
+        Fabiano Fidêncio <fidencio@redhat.com>
+
+    Copyright (C) 2017 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_SESSION_H_
+#define IPA_SESSION_H_
+
+#include "providers/ldap/ldap_common.h"
+
+struct ipa_session_ctx {
+    struct sdap_id_ctx *sdap_ctx;
+    struct dp_option *ipa_options;
+    time_t last_update;
+
+    struct sdap_attr_map *host_map;
+    struct sdap_attr_map *hostgroup_map;
+    struct sdap_search_base **deskprofile_search_bases;
+    struct sdap_search_base **host_search_bases;
+};
+
+struct tevent_req *
+ipa_pam_session_handler_send(TALLOC_CTX *mem_ctx,
+                             struct ipa_session_ctx *session_ctx,
+                             struct pam_data *pd,
+                             struct dp_req_params *params);
+
+errno_t
+ipa_pam_session_handler_recv(TALLOC_CTX *mem_ctx,
+                             struct tevent_req *req,
+                             struct pam_data **_data);
+
+#endif /* IPA_SESSION_H_ */

From 570ebf1d2877fcf9a9564b2ab7850bf5d5ed3e1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Thu, 3 Aug 2017 00:09:43 +0200
Subject: [PATCH 13/20] HBAC: Fix tevent hierarchy in ipa_hbac_rule_info_send()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The first thing a _send() function should o is call
`tevent_req_create()` in order to create both the state and the request
and then use the state as context for temporary data.

Also, `tevent_req_create()` should be only function returning NULL from
the _send function, while all the other calls should goto immediate and
return the proper error, as they have a valid request.

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 39 +++++++++++++++-----------------------
 1 file changed, 15 insertions(+), 24 deletions(-)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index c860905cc..b8d453519 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -60,35 +60,32 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
     size_t i;
     struct tevent_req *req = NULL;
     struct ipa_hbac_rule_state *state;
-    TALLOC_CTX *tmp_ctx;
     const char *host_dn;
     char *host_dn_clean;
     char *host_group_clean;
     char *rule_filter;
     const char **memberof_list;
 
-    if (ipa_host == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Missing host\n");
+    req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_rule_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
         return NULL;
     }
 
-    tmp_ctx = talloc_new(mem_ctx);
-    if (tmp_ctx == NULL) return NULL;
+    if (ipa_host == NULL) {
+        ret = EINVAL;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing host\n");
+        goto immediate;
+    }
 
     ret = sysdb_attrs_get_string(ipa_host, SYSDB_ORIG_DN, &host_dn);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not identify IPA hostname\n");
-        goto error;
+        goto immediate;
     }
 
-    ret = sss_filter_sanitize(tmp_ctx, host_dn, &host_dn_clean);
-    if (ret != EOK) goto error;
-
-    req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_rule_state);
-    if (req == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
-        goto error;
-    }
+    ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
+    if (ret != EOK) goto immediate;
 
     state->ev = ev;
     state->sh = sh;
@@ -116,7 +113,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
     state->attrs[13] = IPA_HOST_CATEGORY;
     state->attrs[14] = NULL;
 
-    rule_filter = talloc_asprintf(tmp_ctx,
+    rule_filter = talloc_asprintf(state,
                                   "(&(objectclass=%s)"
                                   "(%s=%s)(%s=%s)"
                                   "(|(%s=%s)(%s=%s)",
@@ -132,12 +129,12 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
 
     /* Add all parent groups of ipa_hostname to the filter */
     ret = sysdb_attrs_get_string_array(ipa_host, SYSDB_ORIG_MEMBEROF,
-                                       tmp_ctx, &memberof_list);
+                                       state, &memberof_list);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not identify.\n");
     } if (ret == ENOENT) {
         /* This host is not a member of any hostgroups */
-        memberof_list = talloc_array(tmp_ctx, const char *, 1);
+        memberof_list = talloc_array(state, const char *, 1);
         if (memberof_list == NULL) {
             ret = ENOMEM;
             goto immediate;
@@ -146,7 +143,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
     }
 
     for (i = 0; memberof_list[i]; i++) {
-        ret = sss_filter_sanitize(tmp_ctx,
+        ret = sss_filter_sanitize(state,
                                   memberof_list[i],
                                   &host_group_clean);
         if (ret != EOK) goto immediate;
@@ -176,7 +173,6 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
         goto immediate;
     }
 
-    talloc_free(tmp_ctx);
     return req;
 
 immediate:
@@ -186,12 +182,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
         tevent_req_error(req, ret);
     }
     tevent_req_post(req, ev);
-    talloc_free(tmp_ctx);
     return req;
-
-error:
-    talloc_free(tmp_ctx);
-    return NULL;
 }
 
 static errno_t

From 96d23b301fe04ea49726a9fba041a2fe7a228ade Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Thu, 3 Aug 2017 00:19:56 +0200
Subject: [PATCH 14/20] HBAC: Document ipa_hbac_rule_info_next()'s behaviour
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Let's add a comment on ipa_hbac_rule_info_send() in order to have
cleaner why ret is set to EINVAL when ipa_hbac_rule_info_next() returns
EOK.

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index b8d453519..cb5a716aa 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -166,6 +166,14 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
 
     ret = ipa_hbac_rule_info_next(req, state);
     if (ret == EOK) {
+        /* ipa_hbac_rule_info_next should always have a search base when called
+         * for the first time.
+         *
+         * For the subsequent iterations, not finding any more search bases is
+         * fine though (thus the function returns EOK).
+         *
+         * As, here, it's the first case happening, let's return EINVAL.
+         */
         ret = EINVAL;
     }
 

From 081f21ae2b178628a6b3b8d7247a635ace5df04b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Thu, 3 Aug 2017 00:22:42 +0200
Subject: [PATCH 15/20] HBAC: Remove a cosmetic extra space from an if clause
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index cb5a716aa..404778da3 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -201,7 +201,7 @@ ipa_hbac_rule_info_next(struct tevent_req *req,
     struct sdap_search_base *base;
 
     base = state->search_bases[state->search_base_iter];
-    if (base  == NULL) {
+    if (base == NULL) {
         return EOK;
     }
 

From 91e0fc1ccd347a8753c453059d6bba3f13456d2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Thu, 3 Aug 2017 08:02:32 +0200
Subject: [PATCH 16/20] HBAC: Improve readability of ipa_hbac_rule_info_send()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Move an if condition inside another if condition in order to make the
readability a little bit more clear that those checks are about the same
return code.

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 404778da3..d188971aa 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -165,19 +165,18 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
     state->rules_filter = talloc_steal(state, rule_filter);
 
     ret = ipa_hbac_rule_info_next(req, state);
-    if (ret == EOK) {
-        /* ipa_hbac_rule_info_next should always have a search base when called
-         * for the first time.
-         *
-         * For the subsequent iterations, not finding any more search bases is
-         * fine though (thus the function returns EOK).
-         *
-         * As, here, it's the first case happening, let's return EINVAL.
-         */
-        ret = EINVAL;
-    }
-
     if (ret != EAGAIN) {
+        if (ret == EOK) {
+            /* ipa_hbac_rule_info_next should always have a search base when
+             * called for the first time.
+             *
+             * For the subsequent iterations, not finding any more search bases
+             * is fine though (thus the function returns EOK).
+             *
+             * As, here, it's the first case happening, let's return EINVAL.
+             */
+            ret = EINVAL;
+        }
         goto immediate;
     }
 

From 6f8b89ce3f85430e7a18227aee8e4c962e29fa05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Thu, 3 Aug 2017 00:27:36 +0200
Subject: [PATCH 17/20] HBAC: Enforce coding style on ipa_hbac_rule_info_send()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Use if-else if-else statements instead of using severel different if
statements.

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index d188971aa..5a52a8362 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -132,7 +132,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
                                        state, &memberof_list);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not identify.\n");
-    } if (ret == ENOENT) {
+    } else if (ret == ENOENT) {
         /* This host is not a member of any hostgroups */
         memberof_list = talloc_array(state, const char *, 1);
         if (memberof_list == NULL) {

From eaeb7417fce8fb59f0b1b9e0330159389b543d8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Thu, 3 Aug 2017 00:32:59 +0200
Subject: [PATCH 18/20] HBAC: Enforce coding style ipa_hbac_rule_info_recv()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

By convention, the output variables are prefixed with a underscore.

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 8 ++++----
 src/providers/ipa/ipa_hbac_rules.h | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 5a52a8362..2a30750b6 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -297,16 +297,16 @@ ipa_hbac_rule_info_done(struct tevent_req *subreq)
 errno_t
 ipa_hbac_rule_info_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
-                        size_t *rule_count,
-                        struct sysdb_attrs ***rules)
+                        size_t *_rule_count,
+                        struct sysdb_attrs ***_rules)
 {
     struct ipa_hbac_rule_state *state =
             tevent_req_data(req, struct ipa_hbac_rule_state);
 
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
-    *rule_count = state->rule_count;
-    *rules = talloc_steal(mem_ctx, state->rules);
+    *_rule_count = state->rule_count;
+    *_rules = talloc_steal(mem_ctx, state->rules);
 
     return EOK;
 }
diff --git a/src/providers/ipa/ipa_hbac_rules.h b/src/providers/ipa/ipa_hbac_rules.h
index 732ea483e..d8e5a147c 100644
--- a/src/providers/ipa/ipa_hbac_rules.h
+++ b/src/providers/ipa/ipa_hbac_rules.h
@@ -35,7 +35,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
 errno_t
 ipa_hbac_rule_info_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
-                        size_t *rule_count,
-                        struct sysdb_attrs ***rules);
+                        size_t *_rule_count,
+                        struct sysdb_attrs ***_rules);
 
 #endif /* IPA_HBAC_RULES_H_ */

From f38451435fe01342a8e02c5843f709dc8034968d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 16 Aug 2017 08:16:46 +0200
Subject: [PATCH 19/20] HBAC: Add a debug message in case
 ipa_hbac_rule_info_next() fails
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 2a30750b6..625016151 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -175,6 +175,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
              *
              * As, here, it's the first case happening, let's return EINVAL.
              */
+            DEBUG(SSSDBG_CRIT_FAILURE, "No search base found\n");
             ret = EINVAL;
         }
         goto immediate;

From bce076fc9cea9a16847c5e7133f0639e2d70b2db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 16 Aug 2017 08:19:43 +0200
Subject: [PATCH 20/20] HBAC: Not having rules should not be logged as error
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Let's tone down the debug level to TRACE_FUNC instead of MINOR_FAILURE.

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
---
 src/providers/ipa/ipa_hbac_rules.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 625016151..0634a277e 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -281,7 +281,7 @@ ipa_hbac_rule_info_done(struct tevent_req *subreq)
     } else if (ret != EOK) {
         goto fail;
     } else if (ret == EOK && state->rule_count == 0) {
-        DEBUG(SSSDBG_MINOR_FAILURE, "No rules apply to this host\n");
+        DEBUG(SSSDBG_TRACE_FUNC, "No rules apply to this host\n");
         tevent_req_error(req, ENOENT);
         return;
     }
