>From 7f08c7d43217f636fe8fdba1c6a0c232072d7475 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=A0ruba=C5=99?= <xsruba03@stud.fit.vutbr.cz>
Date: Wed, 20 Aug 2014 12:22:06 +0200
Subject: [PATCH] SUDO: Support the IPA schema

https://fedorahosted.org/sssd/ticket/1108

Sudo rules at IPA server are stored in IPA SUDO schema which is different from
native LDAP SUDO schema. Sudo is capable of processing only sudo rules in native
LDAP schema so the rules at IPA are exported into native LDAP schema. This is
done by the compat plugin and exported rules are stored in ou=SUDOers,$DC
container. Current implementation of IPA SUDO provider is just a simple wrapper
for LDAP SUDO provider which looks up rules in that container. So
there is no native support of IPA SUDO schema at SSSD side. This patch adds the
support for IPA schema.

Scope off the changes:
----------------------

Modified files:

src/providers/ipa/ipa_sudo.{c,h}
 - inicialization of periodical refreshes added + ipa sudo handler

src/providers/ldap/sdap.h
 - ipa sudo rule and ipa sudo commands attributes added

src/providers/ldap/ldap_opts.h
 - new maps for IPA SUDO rules and IPA SUDO commands added

src/providers/ldap/ldap_options.h
 - initialization of new IPA sudo maps

src/providers/ldap/sdap_async_sudo.c and src/providers/ldap/sdap_sudo.h
 - these functions and structures are used by both IPA and LDAP sudo provider so
   they are not static anymore
  - sdap_sudo_store_sudoers()
  - sdap_sudo_purge_sudoers()
  - sdap_sudo_set_usn()
  - struct sdap_sudo_ctx
  - struct sdap_sudo_refresh_state

Makefile.am

New files:

src/providers/ipa/ipa_async_sudo.{c,h}
 - asynchronous module that wraps functionality of LDAP SUDO Provider

src/providers/ipa/ipa_async_sudo_cmds.c{c,h}
 - asynchronous module which handles IPA SUDO commands

src/providers/ipa/ipa_async_sudo_hostgroups.{c,h}
 - asynchronous module which performs refresh of hostgroups that the IPA client
	 is member of

src/providers/ipa/ipa_sudo_export.{c,h}
 - this module handles export of sudo rules from IPA SUDO scheme into native
	 LDAP SUDO scheme

src/providers/ipa/ipa_sudo_refreshes.{c,h}
 - build LDAP and SYSDB filters
 - prepares parameters for specific kind of a refresh (full, smart, rules)
---
 Makefile.am                                   |   37 +-
 src/providers/ipa/ipa_async_sudo.c            |  322 ++++++++
 src/providers/ipa/ipa_async_sudo.h            |   41 +
 src/providers/ipa/ipa_async_sudo_cmds.c       |  311 ++++++++
 src/providers/ipa/ipa_async_sudo_cmds.h       |   39 +
 src/providers/ipa/ipa_async_sudo_hostgroups.c |  240 ++++++
 src/providers/ipa/ipa_async_sudo_hostgroups.h |   37 +
 src/providers/ipa/ipa_sudo.c                  |  288 ++++++-
 src/providers/ipa/ipa_sudo.h                  |   52 ++
 src/providers/ipa/ipa_sudo_export.c           | 1044 +++++++++++++++++++++++++
 src/providers/ipa/ipa_sudo_export.h           |   99 +++
 src/providers/ipa/ipa_sudo_refreshes.c        |  766 ++++++++++++++++++
 src/providers/ipa/ipa_sudo_refreshes.h        |   54 ++
 src/providers/ldap/ldap_options.c             |   20 +
 src/providers/ldap/ldap_opts.h                |   31 +
 src/providers/ldap/sdap.h                     |   35 +
 src/providers/ldap/sdap_async_sudo.c          |  170 ++--
 src/providers/ldap/sdap_sudo.c                |   10 +-
 src/providers/ldap/sdap_sudo.h                |   52 +-
 19 files changed, 3561 insertions(+), 87 deletions(-)
 create mode 100644 src/providers/ipa/ipa_async_sudo.c
 create mode 100644 src/providers/ipa/ipa_async_sudo.h
 create mode 100644 src/providers/ipa/ipa_async_sudo_cmds.c
 create mode 100644 src/providers/ipa/ipa_async_sudo_cmds.h
 create mode 100644 src/providers/ipa/ipa_async_sudo_hostgroups.c
 create mode 100644 src/providers/ipa/ipa_async_sudo_hostgroups.h
 create mode 100644 src/providers/ipa/ipa_sudo.h
 create mode 100644 src/providers/ipa/ipa_sudo_export.c
 create mode 100644 src/providers/ipa/ipa_sudo_export.h
 create mode 100644 src/providers/ipa/ipa_sudo_refreshes.c
 create mode 100644 src/providers/ipa/ipa_sudo_refreshes.h

diff --git a/Makefile.am b/Makefile.am
index 67077110588d9c746342f8cb69f942ebccf7fed4..00a7717bb7cbb39356c250c2cf756a2c6bbedc84 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -188,6 +188,7 @@ if HAVE_CMOCKA
         nestedgroups-tests \
         test_sss_idmap \
         test_ipa_idmap \
+        test_ipa_sudo_export \
         test_utils \
         ad_common_tests \
         dp_opt_tests \
@@ -1647,6 +1648,35 @@ sbus_internal_tests_LDADD = \
     libsss_debug.la \
     libsss_test_common.la
 
+test_ipa_sudo_export_SOURCES = \
+    src/tests/cmocka/test_ipa_sudo_export.c \
+    src/providers/ipa/ipa_async_sudo.c \
+    src/providers/ipa/ipa_async_sudo_cmds.c \
+    src/providers/ldap/sdap_async_sudo.c \
+    src/providers/ipa/ipa_sudo_export.c \
+    src/providers/ldap/sdap_sudo_cache.c \
+    src/util/debug.c
+test_ipa_sudo_export_CFLAGS = \
+    $(AM_CFLAGS)
+test_ipa_sudo_export_LDFLAGS = \
+    -Wl,-wrap,debug_fn \
+    -Wl,-wrap,be_is_offline \
+    -Wl,-wrap,sdap_id_op_create \
+    -Wl,-wrap,sdap_id_op_connect_send \
+    -Wl,-wrap,sdap_id_op_connect_recv \
+    -Wl,-wrap,sdap_id_op_handle \
+    -Wl,-wrap,sdap_get_generic_send \
+    -Wl,-wrap,sdap_get_generic_recv \
+    -Wl,-wrap,dp_opt_get_int \
+    -Wl,-wrap,_dp_opt_get_int \
+    -Wl,-wrap,build_attrs_from_map \
+    -Wl,-wrap,sdap_get_id_specific_filter
+test_ipa_sudo_export_LDADD = \
+    $(CMOCKA_LIBS) \
+    libsss_debug.la \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_test_common.la
+
 test_find_uid_SOURCES = \
     src/tests/cmocka/test_find_uid.c \
     src/util/find_uid.c \
@@ -2288,7 +2318,12 @@ endif
 
 if BUILD_SUDO
 libsss_ipa_la_SOURCES += \
-    src/providers/ipa/ipa_sudo.c
+    src/providers/ipa/ipa_sudo.c \
+    src/providers/ipa/ipa_sudo_refreshes.c \
+    src/providers/ipa/ipa_sudo_export.c \
+    src/providers/ipa/ipa_async_sudo.c \
+    src/providers/ipa/ipa_async_sudo_cmds.c \
+    src/providers/ipa/ipa_async_sudo_hostgroups.c
 endif
 
 if BUILD_SSH
diff --git a/src/providers/ipa/ipa_async_sudo.c b/src/providers/ipa/ipa_async_sudo.c
new file mode 100644
index 0000000000000000000000000000000000000000..ffb8b95e909f9b554a9e94dca96c710442cb7189
--- /dev/null
+++ b/src/providers/ipa/ipa_async_sudo.c
@@ -0,0 +1,322 @@
+/*
+    SSSD
+
+    IPA SUDO Provider
+
+    Authors:
+        MIchal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 MIchal Šrubař
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/dp_backend.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap.h"
+#include "providers/ldap/sdap_async.h"
+#include "providers/ldap/sdap_sudo.h"
+#include "providers/ldap/sdap_sudo_cache.h"
+#include "providers/ipa/ipa_async_sudo_cmds.h"
+#include "db/sysdb_sudo.h"
+
+static void ipa_sudo_sudoers_process(struct tevent_req *subreq);
+static void ipa_sudo_get_cmds_done(struct tevent_req *subreq);
+static void ipa_sudo_load_sudoers_finish(struct tevent_req *req, 
+                                         struct sdap_sudo_refresh_state *state,
+                                         struct sysdb_attrs **rules,
+                                         size_t count);
+
+struct tevent_req *ipa_sudo_refresh_send(TALLOC_CTX *mem_ctx,
+                                          struct be_ctx *be_ctx,
+                                          struct sdap_options *opts,
+                                          struct sdap_id_conn_cache *conn_cache,
+                                          const char *ldap_filter,
+                                          const char *sysdb_filter)
+{
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+
+    /* use the same state structure as LDAP SUDO provider because we use LDAP
+     * provider to download sudo rules from IPA server */
+    struct sdap_sudo_refresh_state *state;
+    int ret = EOK;
+
+    req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_refresh_state);
+    if (!req) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    /* if we don't have a search filter, this request is meaningless */
+    if (ldap_filter == NULL) {
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    state->be_ctx = be_ctx;
+    state->opts = opts;
+    state->sdap_op = NULL;
+    state->sdap_conn_cache = conn_cache;
+    state->sysdb = be_ctx->domain->sysdb;
+    state->domain = be_ctx->domain;
+    state->ldap_filter = talloc_strdup(state, ldap_filter);
+    state->sysdb_filter = talloc_strdup(state, sysdb_filter);
+    state->dp_error = DP_ERR_OK;
+    state->error = EOK;
+    state->highest_usn = NULL;
+    state->ldap_rules = NULL;
+    state->ldap_rules_count = 0;
+
+    /* if we don't have a LDAP search filter then this request is meaningless 
+     * although sysdb filter can be NULL for smart refresh */
+    if (state->ldap_filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup() failed\n"));
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    /* sysdb filter can be NULL at SMART REFRESH */
+    if (sysdb_filter != NULL && state->sysdb_filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup() failed\n"));
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Giving control to LDAP SUDO provider to "
+                              "download IPA sudo rules\n"));
+
+    /* LDAP SUDO Provider won't be saving anything to sysdb so it doesn't need
+     * the sysdb filter */
+    subreq = sdap_sudo_refresh_send(state,
+                                    state->be_ctx,
+                                    state->opts,
+                                    state->sdap_conn_cache,
+                                    state->ldap_filter, "");
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    /* We'll receive sudo rules in IPA schema. We need to export those rules 
+     * into native LDAP sudo schema before we can store them into sysdb. */
+    tevent_req_set_callback(subreq, ipa_sudo_sudoers_process, req);
+
+    /* asynchronous processing */
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, be_ctx->ev);
+
+    return req;
+}
+
+int ipa_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
+                           struct tevent_req *req,
+                           int *dp_error,
+                           int *error,
+                           char **usn,
+                           size_t *num_rules,
+                           struct sysdb_attrs ***rules)
+{
+    struct sdap_sudo_refresh_state *state;
+
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *dp_error = state->dp_error;
+    *error = state->error;
+
+    if (usn != NULL && state->highest_usn != NULL) {
+        *usn = talloc_steal(mem_ctx, state->highest_usn);
+    }
+
+    if (num_rules != NULL) {
+        *num_rules = state->num_rules;
+    }
+
+    if (rules != NULL) {
+        *rules = talloc_steal(mem_ctx, state->ldap_rules);
+    }
+
+    return EOK;
+}
+
+/* subreq -> req from sdap_sudo_refresh_send */
+static void ipa_sudo_sudoers_process(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_sudo_refresh_state *state;
+    struct sysdb_attrs **ipa_rules;
+    size_t ipa_rules_count;
+    int ret = EOK;
+
+    /* callback data is req from ipa_sudo_refresh_send */
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
+    ret = sdap_sudo_refresh_recv(state, subreq, &state->dp_error, &state->error, 
+                                 &state->highest_usn, 
+                                 &ipa_rules_count, &ipa_rules);
+
+    talloc_zfree(subreq);
+    if (ret != EOK || state->dp_error != DP_ERR_OK || state->error != EOK) {
+        tevent_req_done(req);
+        return;
+    }
+
+    if (ipa_rules == NULL && ipa_rules_count == 0) {
+        ipa_sudo_load_sudoers_finish(req, state, NULL, 0);
+        return;
+    }
+
+    subreq = ipa_sudo_get_cmds_send(state,
+                                    ipa_rules, 
+                                    ipa_rules_count, 
+                                    state->be_ctx, 
+                                    state->sdap_conn_cache,
+                                    state->opts);
+    if (subreq == NULL) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_get_cmds_done, req);
+}
+
+static void ipa_sudo_get_cmds_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_sudo_refresh_state *state;
+    struct sysdb_attrs **attrs = NULL;
+    size_t count;
+    int ret = EOK;
+    int i;
+
+    /* req from ipa_sudo_refresh_send */
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
+    /* steal EXPORTED sudoers and free IPA sudo commands req */
+    ret = ipa_sudo_get_cmds_recv(subreq, state, &count, &attrs);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /* FIXME:
+     * Multiple search bases are handlel automatically by LDAP SUDO Provider but
+     * is it even possible to have multiple search bases with IPA?
+     */
+
+    /* add exported rules to result */
+    if (count > 0) {
+        state->ldap_rules = talloc_realloc(state, state->ldap_rules,
+                                           struct sysdb_attrs *,
+                                           state->ldap_rules_count + count);
+        if (state->ldap_rules == NULL) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        for (i = 0; i < count; i++) {
+            state->ldap_rules[state->ldap_rules_count + i] = talloc_steal(
+                                                   state->ldap_rules, attrs[i]);
+        }
+
+        state->ldap_rules_count += count;
+    }
+
+    /* now we need to purge the sysdb and store the exported sudoers */
+    ipa_sudo_load_sudoers_finish(req, state, state->ldap_rules, 
+                                 state->ldap_rules_count);
+}
+
+/* req from ipa_sudo_refresh_send() */
+static void ipa_sudo_load_sudoers_finish(struct tevent_req *req, 
+                                         struct sdap_sudo_refresh_state *state,
+                                         struct sysdb_attrs **rules,
+                                         size_t count)
+{
+    bool in_transaction = false;
+    errno_t sret = EOK;
+    int ret = EOK;
+    time_t now;
+
+    /* start transaction */
+    ret = sysdb_transaction_start(state->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
+        goto done;
+    }
+    in_transaction = true;
+
+    /* purge cache */
+    ret = sdap_sudo_purge_sudoers(state->domain, state->sysdb_filter,
+                                  state->opts->ipa_sudorule_map, count, rules);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* store rules */
+    now = time(NULL);
+    ret = sdap_sudo_store_sudoers(state, state->domain,
+                                  state->opts, count, rules,
+                                  state->domain->sudo_timeout, now,
+                                  &state->highest_usn);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* commit transaction */
+    ret = sysdb_transaction_commit(state->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction\n"));
+        goto done;
+    }
+    in_transaction = false;
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Sudoers is successfully stored in cache\n"));
+
+    state->num_rules = count;
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(state->sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Could not cancel transaction\n"));
+        }
+    }
+
+    /* finish req from ipa_sudo_refresh_send */
+    state->error = ret;
+    if (ret == EOK) {
+        state->dp_error = DP_ERR_OK;
+        tevent_req_done(req);
+    } else {
+        state->dp_error = DP_ERR_FATAL;
+        tevent_req_error(req, ret);
+    }
+}
diff --git a/src/providers/ipa/ipa_async_sudo.h b/src/providers/ipa/ipa_async_sudo.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9b62d260c3b14aea68d18d9586828898597959e
--- /dev/null
+++ b/src/providers/ipa/ipa_async_sudo.h
@@ -0,0 +1,41 @@
+/*
+    SSSD
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Srubar
+
+    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_ASYNC_SUDO_H_
+#define _IPA_ASYNC_SUDO_H_
+
+struct tevent_req *ipa_sudo_refresh_send(TALLOC_CTX *mem_ctx,
+                                          struct be_ctx *be_ctx,
+                                          struct sdap_options *opts,
+                                          struct sdap_id_conn_cache *conn_cache,
+                                          const char *ldap_filter,
+                                          const char *sysdb_filter);
+int ipa_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
+                           struct tevent_req *req,
+                           int *dp_error,
+                           int *error,
+                           char **usn,
+                           size_t *num_rules,
+                           struct sysdb_attrs ***rules);
+
+#endif  // _IPA_ASYNC_SUDO_H_
diff --git a/src/providers/ipa/ipa_async_sudo_cmds.c b/src/providers/ipa/ipa_async_sudo_cmds.c
new file mode 100644
index 0000000000000000000000000000000000000000..a75a2e2d7726ddf6cc6c404daedaba1f67706e74
--- /dev/null
+++ b/src/providers/ipa/ipa_async_sudo_cmds.c
@@ -0,0 +1,311 @@
+/*
+    SSSD
+
+    Async IPA SUDO module which downloads sudo commands for IPA sudo rules.
+
+    Authors:
+
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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 "db/sysdb_sudo.h"
+#include "providers/ldap/sdap_async.h"
+#include "providers/ldap/sdap.h"
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_sudo_export.h"
+#include "providers/ipa/ipa_sudo.h"
+
+static errno_t ipa_sudo_get_cmds_retry(struct tevent_req *req);
+static void ipa_sudo_get_cmds_connect_done(struct tevent_req *subreq);
+static void ipa_sudo_cmds_process(struct tevent_req *subreq);
+
+struct ipa_sudo_get_cmds_state {
+
+    struct be_ctx *be_ctx;
+    struct sdap_id_op *sdap_op;
+    struct sysdb_ctx *sysdb;
+    struct tevent_context *ev;
+    struct sdap_id_conn_cache *conn_cache;
+    struct sdap_options *opts;
+
+    const char *filter;     /* LDAP filter for IPA sudo commands */
+    const char *basedn;
+    const char **attrs;
+    int scope;
+    int timeout;
+
+    int dp_error;
+    int error;
+
+    struct sudo_rules *rules;
+};
+
+struct tevent_req *ipa_sudo_get_cmds_send(TALLOC_CTX *mem,
+                                          struct sysdb_attrs **ipa_rules,
+                                          int ipa_rules_count,
+                                          struct be_ctx *be_ctx,
+                                          struct sdap_id_conn_cache *conn_cache,
+                                          struct sdap_options *opts)
+{
+    struct ipa_sudo_get_cmds_state *state;
+    struct tevent_req *req;
+    errno_t ret = EOK;
+    errno_t cmds_ret = EOK;
+
+    req = tevent_req_create(mem, &state, struct ipa_sudo_get_cmds_state);
+    if (!req) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    state->be_ctx = be_ctx;
+    state->sdap_op = NULL;
+    state->opts = opts;
+    state->sysdb = be_ctx->domain->sysdb;
+    state->ev = be_ctx->ev;
+    state->conn_cache = conn_cache;
+    state->filter = NULL;
+    state->basedn = talloc_strdup(state, IPA_SUDO_CMDS_BASEDN);
+    state->attrs = NULL;
+    state->scope = LDAP_SCOPE_SUBTREE;
+    state->timeout = dp_opt_get_int(opts->basic, SDAP_SEARCH_TIMEOUT);
+    state->dp_error = DP_ERR_OK;
+    state->error = EOK;
+
+    state->rules = talloc_zero(state, struct sudo_rules);
+    if (state->rules == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_zero() failed\n");
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    state->rules->ipa_rules = talloc_steal(state->rules, ipa_rules);
+    state->rules->ipa_rules_count = ipa_rules_count;
+
+    if (state->basedn == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_strdup() failed\n");
+        ret = ENOMEM;
+        goto immediately;
+    }
+ 
+    DEBUG(SSSDBG_TRACE_FUNC, "Getting commands for downloaded IPA SUDO rules\n");
+
+    cmds_ret = build_cmds_filter(state, state->sysdb, ipa_rules, 
+                                 ipa_rules_count, &(state->filter));
+    if (cmds_ret != ENOENT && cmds_ret != EOK) {
+        /* an error has occured */
+        ret = cmds_ret;
+        goto immediately;
+    }
+
+    /* EXPORT sudo rules but skip commands */
+    ret = export_sudoers(state, state->sysdb,
+                         state->rules->ipa_rules, 
+                         state->rules->ipa_rules_count, 
+                         &(state->rules->sudoers),
+                         &(state->rules->sudoers_count),
+                         &(state->rules->cmds_index),
+                         req);
+    if (ret != EOK) {
+        goto immediately;
+    } else if (ret == EOK && cmds_ret == ENOENT) {
+        /* ipa sudoers exported and there is no need to download any ipa 
+         * sudo commands */
+        goto immediately;
+    }
+
+    ret = ipa_sudo_get_cmds_retry(req);
+    if (ret == EAGAIN) {
+        return req;
+    }
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, state->ev);
+
+    return req;
+}
+
+int ipa_sudo_get_cmds_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           size_t *reply_count,
+                           struct sysdb_attrs ***reply)
+{
+    struct ipa_sudo_get_cmds_state *state;
+
+    state = tevent_req_data(req, struct ipa_sudo_get_cmds_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *reply_count = state->rules->sudoers_count;
+    *reply = talloc_steal(mem_ctx, state->rules->sudoers);
+
+    return EOK;
+}
+
+static errno_t ipa_sudo_get_cmds_retry(struct tevent_req *req)
+{
+    struct ipa_sudo_get_cmds_state *state;
+    struct tevent_req *subreq;
+    errno_t ret = EOK;
+
+    state = tevent_req_data(req, struct ipa_sudo_get_cmds_state);
+
+    if (be_is_offline(state->be_ctx)) {
+        state->dp_error = DP_ERR_OFFLINE;
+        state->error = EAGAIN;
+        return EOK;
+    }
+
+    if (state->sdap_op == NULL) {
+
+        state->sdap_op = sdap_id_op_create(state, state->conn_cache);
+        if (state->sdap_op == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create() failed\n");
+            state->dp_error = DP_ERR_FATAL;
+            state->error = EIO;
+            return EIO;
+        }
+    }
+
+    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));
+        talloc_zfree(state->sdap_op);
+        state->dp_error = DP_ERR_FATAL;
+        state->error = ret;
+        return ret;
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_get_cmds_connect_done, req);
+
+    return EAGAIN;
+}
+
+static void ipa_sudo_get_cmds_connect_done(struct tevent_req *subreq)
+{
+    struct ipa_sudo_get_cmds_state *state;
+    struct tevent_req *req;
+    int dp_error;
+    int ret;
+
+    /* req from ipa_sudo_get_cmds_send */
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_get_cmds_state);
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+
+    if (dp_error == DP_ERR_OFFLINE) {
+        talloc_zfree(state->sdap_op);
+        state->dp_error = DP_ERR_OFFLINE;
+        state->error = EAGAIN;
+        tevent_req_done(req);
+        return;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "IPA SUDO cmds connection failed - %s\n", 
+                                    strerror(ret));
+        goto fail;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "IPA SUDO cmds connection successful\n");
+
+    struct sdap_attr_map *map = state->opts->ipa_sudocmds_map;
+
+    /* create attrs from map */
+    ret = build_attrs_from_map(state, map, SDAP_OPTS_SUDO_CMD, NULL, &state->attrs, NULL);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Searching for IPA SUDO commands\n");
+
+    subreq = sdap_get_generic_send(state,
+                                   state->ev,
+                                   state->opts,
+                                   sdap_id_op_handle(state->sdap_op),
+                                   state->basedn,
+                                   state->scope,
+                                   state->filter,
+                                   state->attrs,
+                                   map,
+                                   SDAP_OPTS_SUDO_CMD,
+                                   state->timeout,
+                                   true);
+    if (subreq == NULL) {
+        goto fail;
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_cmds_process, req);
+
+fail:
+    state->error = ret;
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    }
+}
+
+static void ipa_sudo_cmds_process(struct tevent_req *subreq)
+{
+    struct ipa_sudo_get_cmds_state *state;
+    struct tevent_req *req;
+    int ret;
+
+    /* req from ipa_sudo_get_cmds_send */
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_get_cmds_state);
+ 
+    DEBUG(SSSDBG_TRACE_FUNC, "Receiving commands for IPA SUDO rules with "
+                             "base [%s]\n", state->basedn);
+
+    /* get IPA sudo commands */
+    ret = sdap_get_generic_recv(subreq, state, 
+                                &state->rules->ipa_cmds_count, 
+                                &state->rules->ipa_cmds);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    ret = export_sudoers_cmds(state, 
+                              state->rules->sudoers, 
+                              state->rules->sudoers_count,
+                              state->rules->cmds_index, 
+                              state->rules->ipa_cmds, 
+                              state->rules->ipa_cmds_count);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "All IPA sudo rules successfully exported into "
+                             "the native LDAP SUDO scheme\n");
+
+fail:
+    if (ret == EOK) {
+        /* ipa_sudo_get_cmds_send */
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+}
diff --git a/src/providers/ipa/ipa_async_sudo_cmds.h b/src/providers/ipa/ipa_async_sudo_cmds.h
new file mode 100644
index 0000000000000000000000000000000000000000..20db4237a6a7a0fa41dc160aa0424a81051277ca
--- /dev/null
+++ b/src/providers/ipa/ipa_async_sudo_cmds.h
@@ -0,0 +1,39 @@
+/*
+    SSSD
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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_ASYNC_SUDO_CMDS_H_
+#define _IPA_ASYNC_SUDO_CMDS_H_
+
+struct tevent_req *
+ipa_sudo_get_cmds_send(TALLOC_CTX *mem,
+                       struct sysdb_attrs **ipa_rules,
+                       int ipa_rules_count,
+                       struct be_ctx *be_ctx,
+                       struct sdap_id_conn_cache *conn_cache,
+                       struct sdap_options *opts);
+int ipa_sudo_get_cmds_recv(struct tevent_req *req,
+                               TALLOC_CTX *mem_ctx,
+                               size_t *reply_count,
+                               struct sysdb_attrs ***reply);
+
+#endif  // _IPA_ASYNC_SUDO_CMDS_H_
diff --git a/src/providers/ipa/ipa_async_sudo_hostgroups.c b/src/providers/ipa/ipa_async_sudo_hostgroups.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ad625d73bcb363fa00c9f84efc762ce7afc8287
--- /dev/null
+++ b/src/providers/ipa/ipa_async_sudo_hostgroups.c
@@ -0,0 +1,240 @@
+/*
+    SSSD
+
+    Get list of hostgroups that the IPA client is member of.
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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_common.h"
+#include "providers/ipa/ipa_hosts.h"
+#include "providers/ipa/ipa_access.h"
+#include "providers/ldap/sdap_sudo.h"
+
+struct ipa_sudo_get_hostgroups_state {
+
+    struct be_ctx *be_ctx;
+    struct sdap_id_op *sdap_op;
+    struct sdap_id_conn_cache *conn_cache;
+    struct sdap_options *opts;
+
+    const char *hostname;
+    struct sdap_attr_map *host_map;
+    struct sdap_attr_map *hostgroup_map;
+    struct sdap_search_base **host_search_bases;
+ 
+    int dp_error;
+    int error;
+
+    /* Hosts */
+    size_t host_count;
+    struct sysdb_attrs **hosts;
+    size_t hostgroup_count;
+    struct sysdb_attrs **hostgroups;
+};
+
+static errno_t ipa_sudo_get_hostgroups_connect(struct tevent_req *req);
+static void ipa_sudo_get_hostgroups_connect_done(struct tevent_req *subreq);
+static void ipa_sudo_get_hostgroups_done(struct tevent_req *subreq);
+
+struct tevent_req *ipa_sudo_get_hostgroups_send(TALLOC_CTX *mem, 
+                                                struct sdap_sudo_ctx *sudo_ctx)
+{
+    struct ipa_sudo_get_hostgroups_state *state;
+    struct ipa_access_ctx *access_ctx;
+    struct tevent_req *req;
+    int ret = EOK;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Refreshing information about hostgroups\n");
+
+    access_ctx = talloc_get_type(sudo_ctx->be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
+                                 struct ipa_access_ctx);
+    if (access_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("talloc_get_type() failed\n"));
+        return NULL;
+    }
+
+    req = tevent_req_create(mem, &state, struct ipa_sudo_get_hostgroups_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    state->be_ctx = sudo_ctx->be_ctx;
+    state->conn_cache = access_ctx->sdap_ctx->conn->conn_cache;
+    state->opts = access_ctx->sdap_ctx->opts;
+    state->hostname = talloc_strdup(state, sudo_ctx->ipa_hostname);
+    state->host_map = access_ctx->host_map;
+    state->hostgroup_map = access_ctx->hostgroup_map;
+    state->host_search_bases = access_ctx->host_search_bases;
+    state->dp_error = DP_ERR_OK;
+    state->error = EOK;
+
+    if (state->hostname == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("talloc_strdup() failed\n"));
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ret = ipa_sudo_get_hostgroups_connect(req);
+    if (ret == EAGAIN) {
+        /* the backend went offline */
+        return req;
+    }
+
+immediately:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, state->be_ctx->ev);
+    }
+
+    return req;
+}
+
+static errno_t ipa_sudo_get_hostgroups_connect(struct tevent_req *req)
+{
+    struct ipa_sudo_get_hostgroups_state *state;
+    struct tevent_req *subreq;
+    errno_t ret = EOK;
+
+    state = tevent_req_data(req, struct ipa_sudo_get_hostgroups_state);
+
+    if (be_is_offline(state->be_ctx)) {
+        state->dp_error = DP_ERR_OFFLINE;
+        state->error = EAGAIN;
+        return EOK;
+    }
+
+    if (state->sdap_op == NULL) {
+        state->sdap_op = sdap_id_op_create(state, 
+                state->conn_cache);
+        if (state->sdap_op == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_create() failed\n");
+            state->dp_error = DP_ERR_FATAL;
+            state->error = EIO;
+            return EIO;
+        }
+    }
+
+    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));
+        talloc_zfree(state->sdap_op);
+        state->dp_error = DP_ERR_FATAL;
+        state->error = ret;
+        return ret;
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_get_hostgroups_connect_done, req);
+
+    return ret;
+}
+
+static void ipa_sudo_get_hostgroups_connect_done(struct tevent_req *subreq)
+{
+    struct ipa_sudo_get_hostgroups_state *state;
+    struct tevent_req *req;
+    int dp_error =DP_ERR_OK;
+    int ret = EOK;
+
+    /* req from ipa_sudo_get_hostgroups_send */
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_get_hostgroups_state);
+ 
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+                
+    if (dp_error == DP_ERR_OFFLINE) {
+        talloc_zfree(state->sdap_op);
+        state->dp_error = DP_ERR_OFFLINE;
+        state->error = EAGAIN;
+        tevent_req_done(req);
+        return;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "IPA sudo hostgroups connection failed "
+                                   "- %s\n", strerror(ret));
+        state->error = ret;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "IPA sudo hostgroups connection successful\n");
+
+    subreq = ipa_host_info_send(state, 
+                                state->be_ctx->ev,
+                                sdap_id_op_handle(state->sdap_op),
+                                state->opts,
+                                state->hostname,
+                                state->host_map,
+                                state->hostgroup_map,
+                                state->host_search_bases);
+    if (subreq == NULL) {
+        DEBUG(1, ("Could not get host info\n"));
+        tevent_req_error(req, ret);
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_get_hostgroups_done, req);
+}
+
+static void ipa_sudo_get_hostgroups_done(struct tevent_req *subreq)
+{
+    struct ipa_sudo_get_hostgroups_state *state;
+    struct tevent_req *req;
+    errno_t ret = EOK;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_get_hostgroups_state);
+ 
+    ret = ipa_host_info_recv(subreq, state,
+                             &state->host_count,
+                             &state->hosts,
+                             &state->hostgroup_count,
+                             &state->hostgroups);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /* getting hostgroups is done now */
+    tevent_req_done(req);
+}
+
+int ipa_sudo_get_hostgroups_recv(struct tevent_req *req,
+                                 TALLOC_CTX *mem_ctx,
+                                 int *dp_error,
+                                 int *error,
+                                 struct sysdb_attrs ***hostgroups,
+                                 size_t *hostgroup_count)
+{
+    struct ipa_sudo_get_hostgroups_state *state;
+
+    state = tevent_req_data(req, struct ipa_sudo_get_hostgroups_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *dp_error = state->dp_error;
+    *error = state->error;
+
+    *hostgroup_count = state->hostgroup_count;
+    *hostgroups = state->hostgroups;
+
+    return EOK;
+}
diff --git a/src/providers/ipa/ipa_async_sudo_hostgroups.h b/src/providers/ipa/ipa_async_sudo_hostgroups.h
new file mode 100644
index 0000000000000000000000000000000000000000..5badb49c0fa2791cf3ac2b97ae1896a7560abce4
--- /dev/null
+++ b/src/providers/ipa/ipa_async_sudo_hostgroups.h
@@ -0,0 +1,37 @@
+/*
+    SSSD
+
+    Authors:
+        MIchal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 MIchal Šrubař
+
+    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_ASYNC_SUDO_HOSTGROUPS_
+#define _IPA_ASYNC_SUDO_HOSTGROUPS_
+
+struct tevent_req *
+ipa_sudo_get_hostgroups_send(TALLOC_CTX *mem, 
+                             struct sdap_sudo_ctx *sudo_ctx);
+int ipa_sudo_get_hostgroups_recv(struct tevent_req *req,
+                                 TALLOC_CTX *mem_ctx,
+                                 int *dp_error,
+                                 int *error,
+                                 struct sysdb_attrs ***hostgroup,
+                                 size_t *hostgrups_count);
+
+#endif  // _IPA_ASYNC_SUDO_HOSTGROUPS_
diff --git a/src/providers/ipa/ipa_sudo.c b/src/providers/ipa/ipa_sudo.c
index 4863aa55904c47ff7d19e3fdb364c06bad5f5678..d1558e7d62ad0d5a0d9c9c6701658dc934e5f910 100644
--- a/src/providers/ipa/ipa_sudo.c
+++ b/src/providers/ipa/ipa_sudo.c
@@ -1,12 +1,14 @@
 /*
     SSSD
 
-    IPA Provider Initialization functions
+    IPA SUDO Provider Initialization functions
 
     Authors:
         Lukas Slebodnik <lslebodn@redhat.com>
+        MIchal Šrubař <xsruba03@stud.fit.vutbr.cz>
 
     Copyright (C) 2013 Red Hat
+    Copyright (C) 2014 MIchal Šrubař
 
     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
@@ -24,32 +26,288 @@
 
 #include "providers/ipa/ipa_common.h"
 #include "providers/ldap/sdap_sudo.h"
+#include "providers/ipa/ipa_sudo_refreshes.h"
+#include "providers/dp_ptask.h"
+#include "db/sysdb_sudo.h"
+
+void ipa_sudo_handler(struct be_req *be_req);
+static void ipa_sudo_reply(struct tevent_req *req);
+static void ipa_sudo_shutdown(struct be_req *req);
+
+static int ipa_sudo_setup_periodical_refreshes(struct sdap_sudo_ctx *sudo_ctx);
+
+struct bet_ops ipa_sudo_ops = {
+    .handler = ipa_sudo_handler,
+    .finalize = ipa_sudo_shutdown
+};
 
 int ipa_sudo_init(struct be_ctx *be_ctx,
-                  struct ipa_id_ctx *id_ctx,
+                  struct ipa_id_ctx *ipa_id_ctx,
                   struct bet_ops **ops,
                   void **pvt_data)
 {
-    int ret;
-    struct ipa_options *ipa_options;
-    struct sdap_options *ldap_options;
+    struct sdap_id_ctx *id_ctx = ipa_id_ctx->sdap_id_ctx;
+    struct sdap_sudo_ctx *sudo_ctx = NULL;
+    const char *hostname = NULL;
+    char *dot = NULL;
+    int ret = EOK;
 
-    DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing sudo IPA back end\n");
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing IPA sudo back end\n");
 
-    /*
-     * SDAP_SUDO_SEARCH_BASE has already been initialized in
-     * function ipa_get_id_options
-     */
-    ret = sdap_sudo_init(be_ctx, id_ctx->sdap_id_ctx, ops, pvt_data);
+    sudo_ctx = talloc_zero(be_ctx, struct sdap_sudo_ctx);
+    if (sudo_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc() failed\n");
+        return ENOMEM;
+    }
+
+    sudo_ctx->id_ctx = id_ctx;
+    sudo_ctx->be_ctx = be_ctx;
+    sudo_ctx->ipa_hostname = NULL;
+    sudo_ctx->ipa_hostgroups = NULL;
+    /* no full refresh done yet so we don't have current usn values available */
+    sudo_ctx->full_refresh_done = false;
+
+    *ops = &ipa_sudo_ops;
+    *pvt_data = sudo_ctx;
+
+    ret = ldap_get_sudo_options(id_ctx, be_ctx->cdb,
+                                be_ctx->conf_path, id_ctx->opts,
+                                &sudo_ctx->use_host_filter,
+                                &sudo_ctx->include_regexp,
+                                &sudo_ctx->include_netgroups);
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize LDAP SUDO [%d]: %s\n",
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudo options [%d]: %s\n",
                                   ret, strerror(ret));
         return ret;
     }
 
-    ipa_options = id_ctx->ipa_options;
-    ldap_options = id_ctx->sdap_id_ctx->opts;
+    /* if IPA hostname isn't set in sssd.conf (ipa_hostname option) then it'll
+     * be get automatically by gethostname() in ipa_get_options() and set into
+     * ipa_options->basic as IPA_HOSTNAME
+     */
+    hostname = dp_opt_get_string(ipa_id_ctx->ipa_options->basic, IPA_HOSTNAME);
+    if (hostname != NULL) {
+
+        dot = strchr(hostname, '.');
+        if (dot != NULL) {
+
+            DEBUG(SSSDBG_TRACE_INTERNAL, "Found IPA hostname: %s\n", hostname);
+
+            sudo_ctx->ipa_hostname = talloc_strdup(sudo_ctx, hostname);
+            if (sudo_ctx->ipa_hostname == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+                return ENOMEM;
+            }
+        }
+    }
+
+    if (hostname == NULL || dot == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Machine hostname ins't FQDN, sudo "
+                                   "won't work correctly");
+        sudo_ctx->use_host_filter = false;
+    }
+
+    return ipa_sudo_setup_periodical_refreshes(sudo_ctx);
+}
+
+static int ipa_sudo_setup_periodical_refreshes(struct sdap_sudo_ctx *sudo_ctx)
+{
+    struct sdap_id_ctx *id_ctx = sudo_ctx->id_ctx;
+    time_t smart_default;
+    time_t smart_interval;
+    time_t full_interval;
+    time_t last_full;
+    time_t delay;
+    int ret = EOK;
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Setting up periodical refreshes of sudo "
+                                 "rules using ptask scheduler\n");
+
+    /* get configured values */
+    smart_interval = dp_opt_get_int(id_ctx->opts->basic,
+                                    SDAP_SUDO_SMART_REFRESH_INTERVAL);
+    full_interval = dp_opt_get_int(id_ctx->opts->basic,
+                                   SDAP_SUDO_FULL_REFRESH_INTERVAL);
+
+    if (smart_interval == 0 && full_interval == 0) {
+        smart_default = id_ctx->opts->basic[SDAP_SUDO_SMART_REFRESH_INTERVAL].def_val.number;
+        DEBUG(SSSDBG_MINOR_FAILURE, "At least one periodical update has to be "
+              "enabled. Setting smart refresh interval to default value (%ld).\n",
+              smart_default);
+
+        ret = dp_opt_set_int(id_ctx->opts->basic,
+                             SDAP_SUDO_SMART_REFRESH_INTERVAL,
+                             smart_default);
+        if (ret != EOK) {
+            return ret;
+        }
+    }
+
+    if (full_interval <= smart_interval) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Full refresh interval has to be greater"
+              "than smart refresh interval. Periodical full refresh will be "
+              "disabled.\n");
+        ret = dp_opt_set_int(id_ctx->opts->basic,
+                             SDAP_SUDO_FULL_REFRESH_INTERVAL,
+                             0);
+        if (ret != EOK) {
+            return ret;
+        }
+    }
+
+    ret = sysdb_sudo_get_last_full_refresh(id_ctx->be->domain, &last_full);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get the time of the "
+                                   "last full refresh\n");
+        return ret;
+    }
+
+    if (last_full == 0) {
+        /* If this is the first startup, we need to kick off refresh
+         * immediately, to close a window where clients requesting sudo
+         * information won't get an immediate reply with no entries
+         */
+        delay = 0;
+    } else {
+        /* At least one update has previously run, so clients will get cached
+         * data. We will delay the refresh so we don't slow down the startup
+         * process if this is happening during system boot.
+         */
+        delay = 10;
+    }
+
+    /* Schedule full refresh
+     * If the backend is offline then this kind of refresh is disabled and when
+     * we got online again ptask will fire it immediately.
+     *
+     * FIXME: timeouts and delays
+     */
+    ret = be_ptask_create(sudo_ctx,
+                          sudo_ctx->be_ctx,
+                          full_interval,                /* period */
+                          delay,                        /* first delay */
+                          1,                            /* enabled_delay */
+                          0,                            /* random_offset */
+                          60,                           /* timeout */
+                          BE_PTASK_OFFLINE_DISABLE,
+                          0,                            /* backoff - disabled */
+                          ipa_sudo_full_refresh_send,
+                          ipa_sudo_full_refresh_ptask_recv,
+                          sudo_ctx,
+                          "full refresh of IPA sudo rules",
+                          NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize periodic full "
+                                    "refresh of IPA sudo rules\n");
+    }
+
+    /* Schedule smart refresh
+     * If backend is offline then this kind of refresh is just skipped.
+     */
+    ret = be_ptask_create(sudo_ctx,
+                          sudo_ctx->be_ctx,
+                          smart_interval,
+                          smart_interval,
+                          1,
+                          0,
+                          60,
+                          BE_PTASK_OFFLINE_SKIP,
+                          0,
+                          ipa_sudo_smart_refresh_send,
+                          ipa_sudo_smart_refresh_recv,
+                          sudo_ctx,
+                          "smart refresh of IPA sudo rules",
+                          NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize periodic smart "
+                                    "refresh of IPA sudo rules\n");
+    }
 
-    ipa_options->id->sudorule_map = ldap_options->sudorule_map;
     return EOK;
 }
+
+void ipa_sudo_handler(struct be_req *be_req)
+{
+    struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
+    struct tevent_req *req = NULL;
+    struct be_sudo_req *sudo_req = NULL;
+    struct sdap_sudo_ctx *sudo_ctx = NULL;
+    struct sdap_id_ctx *id_ctx = NULL;
+    int ret = EOK;
+
+    sudo_ctx = talloc_get_type(be_ctx->bet_info[BET_SUDO].pvt_bet_data,
+                               struct sdap_sudo_ctx);
+    id_ctx = sudo_ctx->id_ctx;
+    sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req);
+
+    switch (sudo_req->type) {
+    case BE_REQ_SUDO_FULL:
+        DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of IPA sudo rules\n");
+        req = ipa_sudo_full_refresh_send(sudo_ctx, id_ctx->be->ev, id_ctx->be,
+                                         NULL, sudo_ctx);
+        break;
+    case BE_REQ_SUDO_RULES:
+        DEBUG(SSSDBG_TRACE_FUNC, "Issuing a refresh of specific "
+                                 "IPA sudo rules\n");
+        req = ipa_sudo_rules_refresh_send(be_req, sudo_ctx, id_ctx->be,
+                                          id_ctx->opts,
+                                          id_ctx->conn->conn_cache,
+                                          sudo_req->rules);
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n",sudo_req->type);
+        ret = EINVAL;
+        goto fail;
+    }
+
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request: %d\n",
+                                    sudo_req->type);
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    tevent_req_set_callback(req, ipa_sudo_reply, be_req);
+    return;
+
+fail:
+    sdap_handler_done(be_req, DP_ERR_FATAL, ret, NULL);
+}
+
+static void ipa_sudo_reply(struct tevent_req *req)
+{
+    struct be_req *be_req = NULL;
+    struct be_sudo_req *sudo_req = NULL;
+    int dp_error = DP_ERR_OK;
+    int error = EOK;
+    int ret = EOK;
+
+    be_req = tevent_req_callback_data(req, struct be_req);
+    sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req);
+
+    switch (sudo_req->type) {
+    case BE_REQ_SUDO_FULL:
+        ret = ipa_sudo_full_refresh_recv(req, &dp_error, &error);
+        break;
+    case BE_REQ_SUDO_RULES:
+        ret = ipa_sudo_rules_refresh_recv(req, &dp_error, &error);
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n",
+                                    sudo_req->type);
+        ret = EINVAL;
+    }
+
+    talloc_zfree(req);
+    if (ret != EOK) {
+        sdap_handler_done(be_req, DP_ERR_FATAL, ret, strerror(ret));
+        return;
+    }
+
+    sdap_handler_done(be_req, dp_error, error, strerror(error));
+}
+
+static void ipa_sudo_shutdown(struct be_req *req)
+{
+    sdap_handler_done(req, DP_ERR_OK, EOK, NULL);
+}
diff --git a/src/providers/ipa/ipa_sudo.h b/src/providers/ipa/ipa_sudo.h
new file mode 100644
index 0000000000000000000000000000000000000000..4cc105b75973e5095affcb799b01f05923f27594
--- /dev/null
+++ b/src/providers/ipa/ipa_sudo.h
@@ -0,0 +1,52 @@
+/*
+    SSSD
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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_SUDO_H_
+#define _IPA_SUDO_H_
+
+#define IPA_SUDO_CMDS_BASEDN            "cn=sudocmds,cn=sudo,dc=example,dc=cz"
+#define IPA_SUDO_CONTAINER_CMDS         "cn=sudocmds,cn=sudo"
+#define IPA_SUDO_CONTAINER_CMD_GRPS     "cn=sudocmdgroups,cn=sudo"
+
+#define IPA_SUDO_ATTR_ID                "ipaUniqueID"
+#define IPA_SUDO_ATTR_MEMBEROF          "memberOf"
+#define IPA_SUDO_ATTR_CMD               "sudoCmd"
+#define IPA_SUDO_ATTR_ALLOW_CMD         "memberAllowCmd"
+#define IPA_SUDO_ATTR_DENY_CMD          "memberDenyCmd"
+#define IPA_SUDO_ATTR_CMD_ALL           "cmdCategory"
+
+#define IPA_SUDO_MEMBER_USER            "memberUser"
+#define IPA_SUDO_MEMBER_HOST            "memberHost"
+#define IPA_SUDO_RUN_AS                 "ipaSudoRunAs"
+#define IPA_SUDO_RUN_AS_GROUP           "ipaSudoRunAsGroup"
+ 
+#define IPA_HOST_FILTER         "(memberHost=fqdn=%s,cn=computers,cn=accounts,%s)"
+#define IPA_HOST_GROUP_FILTER   "(memberHost=cn=%s,cn=hostgroups,cn=accounts,%s)"
+
+#define IPA_SUDO_RULE_FILTER    "(objectClass=%s)(ipaEnabledFlag=TRUE)"
+#define IPA_SUDO_SMART_FILTER   "&(&(objectclass=%s)(ipaEnabledFlag=TRUE)(%s>=%s)(!(%s=%s)))(|"
+#define IPA_SUDO_RULES_FILTER   "&(&(objectClass=%s)(ipaEnabledFlag=TRUE)(|%s))(|"
+#define IPA_SUDO_FULL_FILTER    "&(objectClass=%s)(ipaEnabledFlag=TRUE)(|(cn=defaults)"
+#define IPA_SUDO_CMD_FILTER     "(&(objectClass=%s)(|"
+
+#endif	// _IPA_SUDO_H_
diff --git a/src/providers/ipa/ipa_sudo_export.c b/src/providers/ipa/ipa_sudo_export.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8c2830f2e260b7a087f30544556daf4d63f7072
--- /dev/null
+++ b/src/providers/ipa/ipa_sudo_export.c
@@ -0,0 +1,1044 @@
+/*
+    SSSD
+
+    Export of IPA SUDO rules into native LDAP scheme.
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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 <stdio.h>
+#include <ldb.h>
+#include <string.h>
+#include <talloc.h>
+
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_sudo.h"
+#include "providers/ipa/ipa_sudo_export.h"
+#include "db/sysdb_sudo.h"
+#include "util/util.h"
+
+enum attr_type {
+    USER = 0,
+    USER_GROUP,
+    RUN_AS_GROUP,
+    HOST,
+    HOST_GROUP,
+    COMMAND,
+    CMDS_GROUP,
+    UPPER_CASE,
+    COPY,
+};
+
+struct ipa_sudo_export {
+    enum attr_type type;
+    const char *orig_name;
+    const char *orig_value;
+};
+
+/*
+ * Get a third value out of a given RDN
+ * ipaUniqueID=6f74dc10b,cn=sudocmds,cn=sudo,$DC    => 6f74dc10b
+ * uid=admin,cn=users,cn=accounts,dc=example,dc=cz  => admin
+ */
+static errno_t get_third_rdn_value(TALLOC_CTX *mem_ctx, 
+                            struct sysdb_ctx *sysdb,
+                            const char *dn_str,
+                            const char *first_attr,
+                            const char *second_attr,
+                            const char *second_val,
+                            const char *third_attr,
+                            const char *third_val,
+                            char **value)
+{
+    struct ldb_dn *dn = NULL;
+    const struct ldb_val *val = NULL;
+    const char *rdn = NULL;
+    errno_t ret = EOK;
+
+    dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), dn_str);
+    if (dn == NULL) {
+
+        goto done;
+    }
+
+    /* first rdn, second rdn, third rdn and least one domain component */
+    if (ldb_dn_get_comp_num(dn) < 4) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    /* rdn must be name of the first component */
+    rdn = ldb_dn_get_rdn_name(dn);
+    if (rdn == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (strcasecmp(first_attr, rdn) != 0) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    /* second component must be 'second->attr=second->val' */
+    rdn = ldb_dn_get_component_name(dn, 1);
+    if (strcasecmp(second_attr, rdn) != 0) {
+        ret = ENOENT;
+        goto done;
+    }
+    
+    val = ldb_dn_get_component_val(dn, 1);
+    if (strncasecmp(second_val, (const char *) val->data,
+                    val->length) != 0) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    /* third component must be 'third->attr=third->val' */
+    rdn = ldb_dn_get_component_name(dn, 2);
+    if (strcasecmp(third_attr, rdn) != 0) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    val = ldb_dn_get_component_val(dn, 2);
+    if (strncasecmp(third_val, (const char *) val->data,
+                    val->length) != 0) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    val = ldb_dn_get_rdn_val(dn);
+    *value = talloc_strndup(mem_ctx, (const char *)val->data, val->length);
+    if (*value == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+done:
+    talloc_free(dn);
+    return ret;
+}
+
+/* Take values of all memberAllowCmd or memberDenyCmd attributes (the function
+ * is called twice, once for allowed cmds and once for denied) of a single
+ * rule and add this values into the LDAP filter that will be used to look up 
+ * IPA sudo commands.
+ *
+ * If the value of member{Allow,Deny}Cmd is DN of a single command than use the
+ * IPAUNIQUEID value. Otherwise the value is DN of a group of commands. In this
+ * case use entire DN.
+ *
+ * For example:
+ * memberAllowCmd: ipaUniqueID=9e03c1d0,cn=sudorules,cn=sudo,$DC 
+ *  -> add (memberOf=9e03c1d0)
+ * memberAllowCmd: cn=user,cn=sudocmdgroups,cn=sudo,$DC 
+ *  -> add (memberOf=cn=user,cn=sudocmdgroups,cn=sudo,$DC
+ *
+ */
+static errno_t get_cmd_filter(TALLOC_CTX *mem, 
+                                  struct sysdb_ctx *sysdb,
+                                  const char **values, 
+                                  char **filter)
+{
+    TALLOC_CTX *tmp = NULL;
+    char *ipauniqueid = NULL;
+    const char *val = NULL;
+    const char *type = NULL;
+    int ret = EOK;
+
+    char *cmds_filter = *filter;
+
+    /* for all values (ipaUniquedID or DN of a cmds group) */
+    for (; *values != NULL; values = values+1) {
+
+        /* DN of a command => we need to get value of ipaUniqueID */
+        if (strstr(*values, IPA_SUDO_CONTAINER_CMDS) != NULL) {
+
+            ret = get_third_rdn_value(tmp, sysdb, *values, IPA_SUDO_ATTR_ID, 
+                            "cn", "sudocmds", "cn", "sudo", &ipauniqueid);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_MINOR_FAILURE, "Couldn't parse out the "
+                                             "ipaUniqueID out of the DN\n");
+                ret = ENOMEM;
+                goto fail;
+            }
+            val = ipauniqueid; 
+            type = IPA_SUDO_ATTR_ID;
+        } 
+        /* DN of commands group */
+        else if (strstr(*values, IPA_SUDO_CONTAINER_CMD_GRPS) != NULL) { 
+            val = *values;
+            type = IPA_SUDO_ATTR_MEMBEROF;
+        }
+
+        /* if cmds_filter is NULL then there no attrs in the filter yet but if
+         * the command already is in filter then don't add it again. */
+        if (cmds_filter == NULL || strstr(cmds_filter, val) == NULL) {
+            if ((cmds_filter = talloc_asprintf_append_buffer(cmds_filter, "(%s=%s)", 
+                                type, val)) == NULL) {
+                DEBUG(SSSDBG_MINOR_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+                ret = ENOMEM;
+                goto fail;
+            }
+        }
+    }
+
+    *filter = talloc_steal(mem, cmds_filter);
+
+fail:
+    talloc_free(tmp);
+    return ret;
+}
+
+/* 
+ * Build LDAP filter that will match only neceary sudo commands for
+ * all downloaded ipa sudo rules.
+ *
+ * If it fails, we can't get ipa sudo commands => we don't have complete
+ * sudoers so we can't store them into sysdb!
+ */
+errno_t build_cmds_filter(TALLOC_CTX *mem,
+                          struct sysdb_ctx *sysdb,
+                          struct sysdb_attrs **rules, 
+                          int count, 
+                          const char **cmd_filter)
+{
+    TALLOC_CTX *tmp = NULL;
+    const char **attr_vals;
+    char *cmds_filter = NULL;
+    char *filter;
+    errno_t ret = EOK;
+    int i;
+
+    /* no ipa sudo rules -> nothing to build the new filter from */
+    if (rules == NULL && count == 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "no IPA sudo rules\n");
+        return EINVAL;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Building LDAP filter out of IPA sudo rules to "
+                             "get sudo commands for those rules\n");
+
+    filter = talloc_asprintf(tmp, IPA_SUDO_CMD_FILTER, "ipasudocmd");
+    if (filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprint() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    /* for each downloaded ipa sudo rule */
+    for (i = 0; i < count; i++) {
+
+        /* get values of a memberAllowCmd attr if any */
+        if (sysdb_attrs_get_string_array(rules[i], IPA_SUDO_ATTR_ALLOW_CMD, 
+                                         tmp, &attr_vals) == EOK) {
+            ret = get_cmd_filter(tmp, sysdb, attr_vals, &cmds_filter);
+            if (ret != EOK) {
+                goto fail;
+            }
+            
+            talloc_zfree(attr_vals);
+        }
+
+        /* get values of a memberDenyCmd attr if any  */
+        if (sysdb_attrs_get_string_array(rules[i], 
+                    IPA_SUDO_ATTR_DENY_CMD, tmp, &attr_vals) == EOK) {
+            ret = get_cmd_filter(tmp, sysdb, attr_vals, &cmds_filter);
+            if (ret != EOK) {
+                goto fail;
+            }
+
+            talloc_zfree(attr_vals);
+        }
+    }
+ 
+    /* join object class with ipa sudo cmds to get final filter */
+    filter = talloc_asprintf_append_buffer(filter, "%s))", cmds_filter);
+    if (filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    *cmd_filter = talloc_steal(mem, filter);
+
+    /* no ipa commands needed by these ipa sudo rules */
+    if (cmds_filter == NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No sudo commands needed by downloaded IPA "
+                                 "sudo rules\n");
+        ret = ENOENT;
+    }
+
+fail:
+    talloc_free(tmp);
+    return ret;
+}
+
+/* Add a denied or allowed command of a rule into command index. */
+errno_t ipa_sudo_index_commands(TALLOC_CTX *mem, 
+                                struct ipa_sudoer_cmds *cmds, 
+                                const char *name, 
+                                const char *command)
+{
+    errno_t ret = EOK;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Building command index for: %s\n", command);
+
+    if (strcasecmp(name, IPA_SUDO_ATTR_ALLOW_CMD) == 0) {
+
+        /* make a space for one more command */
+        cmds->allowed = talloc_realloc(mem, cmds->allowed, const char *, 
+                                       cmds->allowed_num+1);
+        if (cmds->allowed == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        /* add new cmd or DN of a group of commands */
+        cmds->allowed[cmds->allowed_num] = talloc_strdup(mem, command);
+        if (cmds->allowed[cmds->allowed_num] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        cmds->allowed_num++;
+    }
+    else if (strcasecmp(name, IPA_SUDO_ATTR_DENY_CMD) == 0) {
+
+        /* make a space for one more command */
+        cmds->denied = talloc_realloc(mem, cmds->denied, const char *, cmds->denied_num+1);
+        if (cmds->denied == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        /* add new cmd or DN to group of commands */
+        cmds->denied[cmds->denied_num] = talloc_strdup(mem, command);
+        if (cmds->denied[cmds->denied_num] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        cmds->denied_num++;
+    }
+    else {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown IPA SUDO command attribute\n");
+        ret = ENOENT;
+        goto fail;
+    }
+
+fail:
+    return ret;
+}
+
+static errno_t get_upper_letter_value(TALLOC_CTX *mem_ctx, 
+                                      const char *val,
+                                      char **new_value)
+{
+    errno_t ret = EOK;
+
+    /* value should always be 'all' */
+    if (strcmp(val, "all") != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Trying to export other than *category attribute\n");
+        return ENOENT;
+    }
+
+    *new_value = talloc_strdup(mem_ctx, "ALL");
+    if (*new_value == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        ret = ENOMEM;
+    }
+
+    return ret;
+}
+
+static errno_t export_attr_name(TALLOC_CTX *mem,
+                                const char *ipa_name,
+                                const char **sysdb_name)
+{
+    errno_t ret = EOK;
+
+    /* FIXME: 
+     * use map or think through something smarter? Or let it be this way
+     * because it's more readable?
+     */
+
+    /* attrs which names need to be exported */
+    if (strcasecmp(ipa_name, "memberUser") == 0 ) {
+        *sysdb_name = talloc_strdup(mem, "sudoUser");
+    }
+    else if (strcasecmp(ipa_name, "externalUser") == 0 ) {
+        *sysdb_name = talloc_strdup(mem, "sudoUser");
+    }
+    else if (strcasecmp(ipa_name, "memberHost") == 0 ) {
+        *sysdb_name = talloc_strdup(mem, "sudoHost");
+    }
+    else if (strcasecmp(ipa_name, "externalHost") == 0 ) {
+        *sysdb_name = talloc_strdup(mem, "sudoHost");
+    }
+    else if (strcasecmp(ipa_name, "memberAllowCmd") == 0 ||
+             strcasecmp(ipa_name, "memberDenyCmd") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoCommand");
+    } 
+    else if (strcasecmp(ipa_name, "ipaSudoRunAs") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoRunAsUser");
+    }
+    else if (strcasecmp(ipa_name, "ipaSudoRunAsExtUser") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoRunAsUser");
+    }
+    else if (strcasecmp(ipa_name, "ipaSudoRunAsGroup") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoRunAsGroup");
+    }
+    else if (strcasecmp(ipa_name, "ipaSudoRunAsExtGroup") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoRunAsGroup");
+    }
+    else if (strcasecmp(ipa_name, "userCategory") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoUser");
+    }
+    else if (strcasecmp(ipa_name, "hostCategory") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoHost");
+    }
+    else if (strcasecmp(ipa_name, "cmdCategory") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoCommand");
+    }
+    else if (strcasecmp(ipa_name, "ipaSudoRunAsUserCategory") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoRunAsUser");
+    }
+    else if (strcasecmp(ipa_name, "ipaSudoRunAsGroupCategory") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoRunAsGroup");
+    }
+    else if (strcasecmp(ipa_name, "ipaSudoOpt") == 0) {
+        *sysdb_name = talloc_strdup(mem, "sudoOption");
+    } /* these attrs are still legal */
+    else if (strcasecmp(ipa_name, "originalDN") == 0 ||
+             strcasecmp(ipa_name, "cn") == 0 ||
+             strcasecmp(ipa_name, "sudoUser") == 0 ||
+             strcasecmp(ipa_name, "sudoHost") == 0 ||
+             strcasecmp(ipa_name, "sudoCommand") == 0 ||
+             strcasecmp(ipa_name, "sudoOption") == 0 ||
+             strcasecmp(ipa_name, "sudoRunAsUser") == 0 ||
+             strcasecmp(ipa_name, "sudoRunAsGroup") == 0 ||
+             strcasecmp(ipa_name, "entryUSN") == 0) {
+        
+        *sysdb_name = talloc_strdup(mem, ipa_name);
+    } else {    /* ipa should NOT sent attr with other name */
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown attr name: %s\n", ipa_name);
+        return ENOENT;
+    }
+    
+    if (sysdb_name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        return ENOMEM;
+    }
+
+    return ret;
+}
+
+static errno_t export_attr_value(TALLOC_CTX *mem,
+                                 struct ipa_sudo_export *properties,
+                                 struct sysdb_ctx *sysdb,
+                                 char **new_value)
+{
+    errno_t ret = EOK;
+    char *value;
+
+    switch (properties->type) {
+        case USER:
+            ret = get_third_rdn_value(mem, sysdb, properties->orig_value,
+                            "uid", "cn", "users", "cn", "accounts", new_value);
+            break;
+        case USER_GROUP:
+            ret = get_third_rdn_value(mem, sysdb, properties->orig_value,
+                            "cn", "cn", "groups", "cn", "accounts", &value);
+            *new_value = talloc_asprintf_append(*new_value, "%c%s", 
+                                                USER_GROUP_PREFIX, value);
+            break;
+        case RUN_AS_GROUP:
+            ret = get_third_rdn_value(mem, sysdb, properties->orig_value,
+                            "cn", "cn", "groups", "cn", "accounts", new_value);
+            break;
+        case HOST:
+            ret = get_third_rdn_value(mem, sysdb, properties->orig_value,
+                            "fqdn", "cn", "computers", "cn", "accounts", new_value);
+            break;
+        case HOST_GROUP:
+            ret = get_third_rdn_value(mem, sysdb, properties->orig_value,
+                            "cn", "cn", "hostgroups", "cn", "accounts", &value);
+            *new_value = talloc_asprintf_append(*new_value, "%c%s", 
+                                                HOST_GROUP_PREFIX, value);
+            break;
+        case COMMAND:
+            ret = get_third_rdn_value(mem, sysdb, properties->orig_value,
+                            "ipaUniqueID", "cn", "sudocmds", "cn", "sudo", new_value);
+            break;
+        case UPPER_CASE:
+            ret = get_upper_letter_value(mem, properties->orig_value, new_value);
+            break;
+        case CMDS_GROUP:
+            *new_value = talloc_strdup(mem, properties->orig_value);
+            break;
+        case COPY:
+            *new_value = talloc_strdup(mem, properties->orig_value);
+            break;
+    }
+
+    if (new_value == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc() failed\n");
+        return ENOMEM;
+    }
+
+    return ret;
+}
+ 
+
+/* FIXME: 
+ * Use ipa_sudorule_map and macros for these constants? It's much more 
+ * readable and clear this way in my opinion.
+ */
+static errno_t export_set_properties(TALLOC_CTX *mem,
+                                     const char *attr_name, 
+                                     const char *attr_val,
+                                     struct ipa_sudo_export **properties)
+{
+    struct ipa_sudo_export *prop;
+    errno_t ret = EOK;
+
+    prop = talloc_zero(mem, struct ipa_sudo_export);
+    if (prop == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    /* ipa users and user groups attributes */
+    if (strcasecmp(attr_name, "memberUser") == 0 ||
+        strcasecmp(attr_name, "ipaSudoRunAs") == 0 ||
+        strcasecmp(attr_name, "ipaSudoRunAsGroup") == 0) {
+
+        if (strstr(attr_val, "cn=users,cn=accounts") != NULL) {
+            prop->type = USER;
+        } else if (strstr(attr_val, "cn=groups,cn=accounts") != NULL) {
+            prop->type = RUN_AS_GROUP;
+        }
+    } /* ipa command attributes */
+    else if (strcasecmp(attr_name, "memberAllowCmd") == 0 ||
+             strcasecmp(attr_name, "memberDenyCmd") == 0) {
+
+        if (strstr(attr_val, "cn=sudocmds,cn=sudo") != NULL) {
+            prop->type = COMMAND;
+        } else if (strstr(attr_val, "cn=sudocmdgroups,cn=sudo") != NULL) {
+            prop->type = CMDS_GROUP;
+        }
+    } /* ipa hosts and host groups attributes */
+    else if (strcasecmp(attr_name, "memberHost") == 0 ) {
+
+        if (strstr(attr_val, "cn=computers,cn=accounts") != NULL) {
+            prop->type = HOST;
+        } else if (strstr(attr_val, "cn=hostgroups,cn=accounts") != NULL) {
+            prop->type = HOST_GROUP;
+        }
+    } /* sudo is case sensitive so we need to export all to ALL */
+    else if (strcasecmp(attr_name, "userCategory") == 0 ||
+               strcasecmp(attr_name, "hostCategory") == 0 ||
+               strcasecmp(attr_name, "cmdCategory") == 0 ||
+               strcasecmp(attr_name, "ipaSudoRunAsUserCategory") == 0 ||
+               strcasecmp(attr_name, "ipaSudoRunAsGroupCategory") == 0) {
+        prop->type = UPPER_CASE;
+    } /* attrs which values will be copied */
+    else if (strcasecmp(attr_name, "originalDN") == 0 ||
+             strcasecmp(attr_name, "cn") == 0 ||
+             strcasecmp(attr_name, "sudoUser") == 0 ||
+             strcasecmp(attr_name, "externalUser") == 0 ||
+             strcasecmp(attr_name, "sudoHost") == 0 ||
+             strcasecmp(attr_name, "externalHost") == 0 ||
+             strcasecmp(attr_name, "sudoCommand") == 0 ||
+             strcasecmp(attr_name, "ipaSudoOpt") == 0 ||
+             strcasecmp(attr_name, "sudoOption") == 0 ||
+             strcasecmp(attr_name, "sudoRunAsUser") == 0 ||
+             strcasecmp(attr_name, "ipaSudoRunAsExtUser") == 0 ||
+             strcasecmp(attr_name, "sudoRunAsGroup") == 0 ||
+             strcasecmp(attr_name, "ipaSudoRunAsExtGroup") == 0 ||
+             strcasecmp(attr_name, "entryUSN") == 0) {
+        prop->type = COPY;
+    }
+    else {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Don't know how to export this attr: %s\n", 
+                                     attr_name);
+        ret = ENOENT;
+        goto fail;
+    }
+
+    /* make a copy of original name and value of the attribute */
+    prop->orig_name = talloc_strdup(prop, attr_name);
+    if (prop->orig_name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+    prop->orig_value = talloc_strdup(prop, attr_val);
+    if (prop->orig_value == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    *properties = prop;
+
+fail:
+    return ret;
+}
+
+static errno_t export_attr_values(TALLOC_CTX *mem_sudoer,
+                                  TALLOC_CTX *mem_cmd,
+                                  struct sysdb_ctx *sysdb,
+                                  struct ldb_message_element *e,
+                                  struct ipa_sudoer_cmds **cmd_index,
+                                  struct sysdb_attrs **sudoers,
+                                  const char *new_name,
+                                  struct tevent_req *req)
+{
+    struct ldb_message_element *new_el = NULL;
+    struct ipa_sudo_export *prop = NULL;
+    char *new_value = NULL;
+    errno_t ret = EOK;
+    int k;
+
+    /* EXPORT all values of the attribute */
+    for (k = 0; k < e->num_values; k++) {
+
+        ret = export_set_properties(mem_sudoer, e->name, 
+                                    (const char *)e->values[k].data, &prop);
+        if (ret != EOK) {
+            goto fail;
+        }
+
+        ret = export_attr_value(mem_sudoer, prop, sysdb, &new_value);
+        if (ret != EOK) {
+            goto fail;
+        }
+
+        /* For commands we build cmd_index but do not copy it's values into
+         * final sudoers yet. Because we need to export the value first but we
+         * don't have downloaded the IPA SUDO commands yet.
+         */
+        if (strcasecmp(e->name, IPA_SUDO_ATTR_ALLOW_CMD) == 0 ||
+            strcasecmp(e->name, IPA_SUDO_ATTR_DENY_CMD) == 0) {
+
+            ret = ipa_sudo_index_commands(mem_cmd, *cmd_index, e->name, new_value);
+            if (ret != EOK) {
+                goto fail;
+            }
+
+            continue;
+        }
+
+        /* create new attribute or get the existing one based on the
+         * new name of the attribute */
+        ret = sysdb_attrs_get_el_ext(*sudoers, new_name, true, &new_el);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+            goto fail;
+        }
+
+        /* copy exported value to the values of the new attribute */
+        ret = sysdb_attrs_add_string(*sudoers, new_name, new_value);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+            goto fail;
+        }
+
+        talloc_zfree(prop);
+        talloc_zfree(new_value);
+    }
+
+fail:
+    return ret;
+}
+
+/* 
+ * Export IPA specific attributes, copy attributes that doesn't need to be
+ * exported and create command index (remeber commands for each rule so when we
+ * get the commands we don't have to iterate through all rules attrs again).
+ *
+ * Skip the commands attributes for now because we don't have the commands
+ * donwloaded yet.
+ */
+errno_t export_sudoers(TALLOC_CTX *mem, 
+                       struct sysdb_ctx *sysdb,
+                       struct sysdb_attrs **ipa_rules, 
+                       size_t ipa_rules_count, 
+                       struct sysdb_attrs ***exported_rules,
+                       size_t *sudoers_count,
+                       struct ipa_sudoer_cmds ***index,
+                       struct tevent_req *req)
+{
+    struct ldb_message_element *e = NULL;
+    struct sysdb_attrs **sudoers;
+    struct ipa_sudoer_cmds **cmds_index;
+    const char *new_name = NULL;
+    errno_t ret = EOK;
+    int i, j;
+
+    /* no rules to export */
+    if (ipa_rules == NULL || ipa_rules_count == 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "no IPA sudo rules to export\n");
+        return EINVAL;
+    }
+
+    /* an array of exported sudoers (without commands) */
+    *sudoers_count = 0;
+    sudoers = talloc_zero_array(mem, struct sysdb_attrs *, ipa_rules_count);
+    if (sudoers == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    cmds_index = talloc_zero_array(mem, struct ipa_sudoer_cmds *, ipa_rules_count);
+    if (cmds_index == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    /* for each IPA sudo rule aplicable to this host */
+    for (i = 0; i < ipa_rules_count; i++) {
+
+        DEBUG(SSSDBG_TRACE_FUNC, "Exporting IPA SUDO rule cn=%s\n",
+                                  (char *)ipa_rules[i]->a[1].values[0].data);
+
+        /* new sudo rule */
+        sudoers[i] = sysdb_new_attrs(sudoers);
+        /* new index of allowed and denied commands for this specific rule*/
+        cmds_index[i] = talloc_zero(cmds_index, struct ipa_sudoer_cmds);
+
+        /* for each attribute of the rule */
+        for (j = 0; j < ipa_rules[i]->num; j++) {
+
+            /* get element -> one attribute of the rule */
+            e = &(ipa_rules[i]->a[j]);
+
+            /* EXPORT the name of the attribute */
+            ret = export_attr_name(sudoers[i], e->name, &new_name);
+            if (ret != EOK) {
+                goto fail;
+            }
+
+            /* EXPORT all values of the attribute */
+            ret = export_attr_values(sudoers[i], cmds_index[i], sysdb, e, 
+                                     &(cmds_index[i]), &(sudoers[i]), 
+                                     new_name, req);
+                                              
+            if (ret != EOK) {
+                goto fail;
+            }
+
+            talloc_zfree(new_name);
+
+        } /* for each attribute of the rule */
+
+        (*sudoers_count)++;
+
+    } /* for each rule aplicable to this host */
+
+    if (ipa_rules_count != *sudoers_count || ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unsuccessful export of IPA SUDO rules\n");
+        ret = EIO;
+        goto fail;
+    }
+
+    *exported_rules = talloc_steal(mem, sudoers);
+    *index = cmds_index;
+
+fail:
+    return ret;
+}
+
+/* If attr_name is ipaUniqueID then it'l return value of sudoCmd attribute on a
+ * first call and NULL on second call.
+ *
+ * If attr_name is DN of a commands group then it'll return sudoCmd on each call
+ * and NULL if there are no more commands.
+ */
+static const char *get_sudoCmd_value(TALLOC_CTX *mem,
+                                     struct sysdb_attrs **ipa_cmds,
+                                     int ipa_cmds_count, 
+                                     const char *attr_name,
+                                     const char *attr_value,
+                                     bool cmd_group,
+                                     int *error)
+{
+    const char **values = NULL;
+    const char **val;
+    const char *sudo_cmd = NULL;
+    const char *tmp = NULL;
+
+    static int i;
+    int ret;
+
+    *error = EOK;
+
+    /* for each IPA sudo cmd (continue where we stopped the last time) */
+    for ( ; i < ipa_cmds_count; i++) {
+
+        ret = sysdb_attrs_get_string_array(ipa_cmds[i], attr_name, mem, &values);
+        if (ret != EOK && ret != ENOENT) {
+            *error = ret;
+            return NULL;
+        }
+
+        for (val = values; val != NULL && *val != NULL; val++) {
+            
+            if (strcasecmp(*val, attr_value) == 0) {
+                /* searched ipa command found, returning value of sudoCmd */
+                ret = sysdb_attrs_get_string(ipa_cmds[i], IPA_SUDO_ATTR_CMD, &tmp);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_string() failed\n");
+                    *error = ret;
+                    return NULL;
+                }
+
+                i++;    /* don't start in the same entry next time */
+
+                sudo_cmd = talloc_strdup(mem, tmp);
+                if (sudo_cmd == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+                    *error = ENOMEM;
+                    return NULL;
+                }
+
+                return sudo_cmd;
+            }
+        }
+
+        talloc_zfree(values);
+    }
+
+    /* no more sudoCmd(s) for this group of ipa commands */
+    i = 0;
+    return NULL;
+}
+
+/* Takes single sudo rule and exports all its allowed OR denied (it's called
+ * twice) IPA sudo commands.
+ */
+static int ipa_sudo_assign_command(struct sysdb_attrs *sudoers, 
+                                   struct sysdb_attrs **ipa_cmds, 
+                                   int ipa_cmds_count,
+                                   const char **cmds, 
+                                   int count, bool prefix)
+{
+
+    TALLOC_CTX *tmp_ctx;
+    const char *attr_name = NULL;
+    const char *sudo_cmd = NULL;
+    char *p_sudo_cmd = NULL;
+    bool cmd_group = false;
+    const char **exported_cmds = NULL;
+    bool cmd_exists = false;
+    errno_t ret = EOK;
+    int j;
+
+    if ((tmp_ctx = talloc_new(NULL)) == NULL) {
+        return ENOMEM;
+    }
+
+    if (cmds == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "No IPA command index\n");
+        ret = ENOENT;
+        goto fail;
+    }
+
+    /* for each allowed or denied IPA sudo command */
+    for (j = 0; j < count; j++) {
+
+        /* look up a single command or a group? */
+        if (strstr(cmds[j], IPA_SUDO_CONTAINER_CMD_GRPS) == NULL) {
+            attr_name = talloc_strdup(tmp_ctx, IPA_SUDO_ATTR_ID);
+            cmd_group = false;
+            DEBUG(SSSDBG_TRACE_FUNC, "Exporting cmd ipauniqueid=%s\n", cmds[j]);
+        } else {
+            attr_name = talloc_strdup(tmp_ctx, IPA_SUDO_ATTR_MEMBEROF);
+            cmd_group = true;
+            DEBUG(SSSDBG_TRACE_FUNC, "Exporting cmd group: %s\n", cmds[j]);
+        }
+
+        if (attr_name == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        while ((sudo_cmd = get_sudoCmd_value(tmp_ctx, ipa_cmds, ipa_cmds_count, 
+                                        attr_name, cmds[j], cmd_group, &ret)) != NULL)
+        {
+            /* if the attribute 'sudoCommand' in not already in the rule then
+             * it'll be created automatically in sysdb_attrs_add_string().
+             * Problem is that the sysdb_func() doesn't not check if the value
+             * it adding isn't already in the attribute. There can be a case
+             * where rule has same command, e.g '/sbin/blkid', as allowed and
+             * also as allowed in command group. Then there would be two same
+             * values in 'sudoCommand' attribute:
+             *
+             * sudoCommands:    /sbin/blkid
+             * sudoCommands:    /sbin/blkid
+             *
+             * and that would fail when LDAP SUDO Provider would try to save it
+             * into sysdb. That's the reason why we has to check if the exported
+             * commands isn't already exported.
+             */
+            ret = sysdb_attrs_get_string_array(sudoers, SYSDB_SUDO_CACHE_AT_COMMAND, 
+                                               tmp_ctx, &exported_cmds);
+            if (ret == EOK) {
+                while (*exported_cmds != NULL) {
+                    if (strcmp(*exported_cmds, sudo_cmd) == 0) {
+                        cmd_exists = true;
+                        break;
+                    }
+
+                    exported_cmds++;    /* go to another value */
+                }
+
+                if (cmd_exists) {
+                    /* sudo command is already in sucoCommands attribute, don't
+                     * add it again */
+                    cmd_exists = false;
+                    continue;
+                }
+            } else if (ret != EOK && ret != ENOENT) {
+                goto fail;
+            }
+
+            if (prefix) {   /* denied cmds has to have ! prefix */
+                p_sudo_cmd = talloc_asprintf_append(p_sudo_cmd, "%c%s", 
+                                                DENIED_CMD_PREFIX, sudo_cmd);
+                if (p_sudo_cmd == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+                    ret = ENOMEM;
+                    goto fail;
+                }
+
+                ret = sysdb_attrs_add_string(sudoers, SYSDB_SUDO_CACHE_AT_COMMAND, 
+                                             p_sudo_cmd);
+                if (ret != EOK) {
+                    goto fail;
+                }
+                DEBUG(SSSDBG_TRACE_FUNC, "Exported denied sudo cmd: %s\n", p_sudo_cmd);
+
+            } else {
+                ret = sysdb_attrs_add_string(sudoers, SYSDB_SUDO_CACHE_AT_COMMAND, 
+                                             sudo_cmd);
+                if (ret != EOK) {
+                    goto fail;
+                }
+                DEBUG(SSSDBG_TRACE_FUNC, "Exported allowed sudo cmd: %s\n", sudo_cmd);
+            }
+
+            talloc_zfree(p_sudo_cmd);
+            talloc_zfree(sudo_cmd);
+        } /* while end */
+
+        if (ret != EOK) {
+            goto fail;
+        }
+
+        talloc_zfree(attr_name);
+    }
+
+fail:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+/* EXPORT IPA SUDO COMMANDS
+ * There are two arrays. First with already exported sudoers but without
+ * commands (sudoers) and ther there is index. Both arrays has same length and
+ * they are indexed by same index:
+ *
+ * ---------------
+ * | sudoers[0]   |  ...
+ * | (sudo rule1) |
+ *  --------------
+ *       |
+ *  ----------------------------------------------------
+ * | index[0]                                           |
+ * | - number of allowe denided cmds for sudo rule1     |   ...
+ * | - ipauniqueid or DN of a cmd group for each IPA    |
+ * |   SUDO cmd of the sudo rule1                       |
+ *  ----------------------------------------------------
+ *
+ * Apropriate IPA sudo commands are downloaded. To finish the export we just
+ * need to assign the correct sudo commands to the rules.
+ */
+int export_sudoers_cmds(TALLOC_CTX *mem,
+                        struct sysdb_attrs **sudoers,
+                        int sudoers_count,
+                        struct ipa_sudoer_cmds **index, 
+                        struct sysdb_attrs **ipa_cmds,
+                        int ipa_cmds_count)
+{
+    errno_t ret = EOK;
+    const char *rule_name;
+    int i;  /* i can be index to both sudoers and index arrays */
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Exporting IPA sudo commands\n");
+
+    /* for each sudoer */
+    for (i = 0; i < sudoers_count; i++) {
+
+        ret = sysdb_attrs_get_string(sudoers[i], SYSDB_SUDO_CACHE_AT_CN, &rule_name);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_string() failed\n");
+            goto fail;
+        } else {
+            DEBUG(SSSDBG_TRACE_FUNC, "Sudo rule cn=%s has %d allowed and %d "
+                                     "denied cmds which needs to be exported\n",
+                                      rule_name, index[i]->allowed_num, 
+                                      index[i]->denied_num);
+        }
+
+        /* are there any allowed IPA sudo commands for this sudoer that need to
+         * be exported? */
+        if (index[i]->allowed_num != 0) {
+            ret = ipa_sudo_assign_command(sudoers[i], ipa_cmds, ipa_cmds_count, 
+                                          index[i]->allowed, 
+                                          index[i]->allowed_num, false);
+            if (ret != EOK) {
+                goto fail;
+            }
+        }
+
+        /* same for denied commands */
+        if (index[i]->denied_num != 0) {
+            ret = ipa_sudo_assign_command(sudoers[i], ipa_cmds, ipa_cmds_count, 
+                                          index[i]->denied, 
+                                          index[i]->denied_num, true);
+            if (ret != EOK) {
+                goto fail;
+            }
+        }
+    }
+fail:
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_sudo_export.h b/src/providers/ipa/ipa_sudo_export.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3377f356203b2240ef13cccf66aa7eb779e2281
--- /dev/null
+++ b/src/providers/ipa/ipa_sudo_export.h
@@ -0,0 +1,99 @@
+/*
+    SSSD
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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_SUDO_EXPORT_H_
+#define _IPA_SUDO_EXPORT_H_
+
+struct ipa_sudoer_cmds {
+    /* if there are no cmds for the rule then allowed and denied points to NULL
+     * and allowd_num and denied_num is zero
+     */
+    const char **allowed;
+    int allowed_num;
+    const char **denied;
+    int denied_num;
+};
+
+struct sudo_rules {
+    /* sudo rules in native LDAP format ready to be saved into sysdb */
+    struct sysdb_attrs **sudoers;
+    size_t sudoers_count;
+
+    /* sudo rules in IPA format */
+    struct sysdb_attrs **ipa_rules;
+    size_t ipa_rules_count;
+
+    /* IPA SUDO commands for thoso rules */
+    struct sysdb_attrs **ipa_cmds;
+    size_t ipa_cmds_count;
+
+    /* commands index is an array created from ipa sudo rules which length 
+     * is the same as number of ipa_rules 
+     *
+     * +----------------------------------------+-------------------------+
+     * |cn=users_op,cn=sudocmdgroups,cn=sudo,$DC|fdfcaf84-...-080027eec4b0|
+     * +----------------------------------------+-------------------------+
+     *                                 /|\
+     *     rule[0]       rule[1]        |
+     * +-------------+---------------+  |
+     * | allowed     | allowed ------|---
+     * | allowed_num | allowed_num=2 | ...
+     * | denied      | denied        |
+     * | denied_num  | denied_num    |
+     * +-------------+---------------+
+     */
+    struct ipa_sudoer_cmds **cmds_index;
+};
+
+errno_t export_sudoers(TALLOC_CTX *mem, 
+                       struct sysdb_ctx *sysdb,
+                       struct sysdb_attrs **ipa_rules, 
+                       size_t rules_count, 
+                       struct sysdb_attrs ***exported_rules,
+                       size_t *sudoers_count,
+                       struct ipa_sudoer_cmds ***index,
+                       struct tevent_req *req);
+int export_sudoers_cmds(TALLOC_CTX *mem,
+                        struct sysdb_attrs **sudoers,
+                        int sudoers_count,
+                        struct ipa_sudoer_cmds **index, 
+                        struct sysdb_attrs **ipa_cmds,
+                        int ipa_cmds_count);
+
+errno_t build_cmds_filter(TALLOC_CTX *mem,
+                          struct sysdb_ctx *sysdb,
+                          struct sysdb_attrs **rules, 
+                          int count, 
+                          const char **cmd_filter);
+
+/* Add a denied or allowed command of a rule into command index. */
+errno_t ipa_sudo_index_commands(TALLOC_CTX *mem, 
+                                struct ipa_sudoer_cmds *cmds, 
+                                const char *name, 
+                                const char *value);
+
+#define DENIED_CMD_PREFIX       '!'
+#define USER_GROUP_PREFIX       '%'
+#define HOST_GROUP_PREFIX       '+'
+
+#endif  // _IPA_SUDO_EXPORT_H_
diff --git a/src/providers/ipa/ipa_sudo_refreshes.c b/src/providers/ipa/ipa_sudo_refreshes.c
new file mode 100644
index 0000000000000000000000000000000000000000..07b8f31bfb82b080f21a169a966456e8c1533305
--- /dev/null
+++ b/src/providers/ipa/ipa_sudo_refreshes.c
@@ -0,0 +1,766 @@
+/*
+    SSSD
+    
+    This module prepares parameters for refresh of IPA sudo rules. Parameters
+    as LDAP or SYSDB filters are set based on a kind of refresh. There are 
+    currently three kinds of refreshes:
+      1) FULL Refersh
+      2) SMART Refersh
+      3) RULES Refersh
+
+    Authors:
+        Michal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 Michal Šrubař
+
+    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_common.h"
+#include "providers/ldap/sdap_sudo.h"
+#include "providers/ldap/sdap.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ipa/ipa_sudo.h"
+#include "providers/ipa/ipa_async_sudo.h"
+#include "providers/ipa/ipa_async_sudo_hostgroups.h"
+#include "providers/dp_backend.h"
+#include "db/sysdb_sudo.h"
+
+struct ipa_sudo_full_refresh_state {
+    struct sdap_sudo_ctx *sudo_ctx;
+    struct sdap_id_ctx *id_ctx;
+    struct sysdb_ctx *sysdb;
+    struct sss_domain_info *domain;
+    int dp_error;
+    int error;
+};
+
+struct ipa_sudo_smart_refresh_state {
+    struct tevent_req *subreq;
+    struct sdap_id_ctx *id_ctx;
+    struct sysdb_ctx *sysdb;
+};
+
+struct ipa_sudo_rules_refresh_state {
+    struct sdap_id_ctx *id_ctx;
+    size_t num_rules;
+    int dp_error;
+    int error;
+};
+
+static void ipa_sudo_get_hostinfo_finish(struct tevent_req *subreq);
+static void ipa_sudo_full_refresh_step(struct tevent_req *subreq);
+static void ipa_sudo_full_refresh_done(struct tevent_req *subreq);
+static void ipa_sudo_smart_refresh_done(struct tevent_req *subreq);
+static void ipa_sudo_rules_refresh_done(struct tevent_req *subreq);
+
+/* returns IPA LDAP host filter in the following format:
+ * (hostCategory=ALL)(externalHost=...)(...)
+ */
+static char *ipa_sudo_build_host_filter(TALLOC_CTX *mem_ctx,
+                                        struct sdap_attr_map *map,
+                                        char *basedn,
+                                        char *hostnames,
+                                        char **ip_addr,
+                                        char **hostgroups)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *filter = NULL;
+    int i = 0;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        return NULL;
+    }
+
+    /* ALL */
+    filter = talloc_asprintf_append_buffer(filter, "(%s=ALL)",
+                                           map[SDAP_AT_IPA_SUDO_HOST_CAT].name);
+    if (filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+        goto done;
+    }
+
+    /* we specify the IPA host by FQDN */
+    filter = talloc_asprintf_append_buffer(filter, IPA_HOST_FILTER,
+                                           hostnames, basedn);
+    if (filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+        goto done;
+    }
+
+    /* external host */
+    filter = talloc_asprintf_append_buffer(filter, "(%s=%s)",
+                                           map[SDAP_AT_IPA_SUDO_EXT_HOST].name,
+                                           hostnames);
+    if (filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+        goto done;
+    }
+
+    /* host groups */
+    if (hostgroups != NULL) {
+        for (i = 0; hostgroups[i] != NULL; i++) {
+
+            filter = talloc_asprintf_append_buffer(filter, IPA_HOST_GROUP_FILTER,
+                                                   hostgroups[i], basedn);
+            if (filter == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+                goto done;
+            }
+        }
+    }
+
+    /* ip addresses and networks */
+    if (ip_addr != NULL) {
+        for (i = 0; ip_addr[i] != NULL; i++) {
+
+            /* only external host can be specified by IP */
+            filter = talloc_asprintf_append_buffer(filter, "(%s=%s)",
+                                                   map[SDAP_AT_IPA_SUDO_EXT_HOST].name,
+                                                   ip_addr[i]);
+            if (filter == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append_buffer() failed\n");
+                goto done;
+            }
+        }
+    }
+
+    talloc_steal(mem_ctx, filter);
+
+done:
+    talloc_free(tmp_ctx);
+    return filter;
+}
+
+static char *ipa_sudo_get_filter(TALLOC_CTX *mem_ctx,
+                                 struct sdap_attr_map *map,
+                                 struct sdap_sudo_ctx *sudo_ctx,
+                                 const char *rule_filter)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *host_filter = NULL;
+    char *filter = NULL;
+
+    if (!sudo_ctx->use_host_filter) {
+        return talloc_strdup(mem_ctx, rule_filter);
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        return NULL;
+    }
+
+    host_filter = ipa_sudo_build_host_filter(tmp_ctx, map,
+                                             sudo_ctx->id_ctx->opts->sdom->basedn,
+                                             sudo_ctx->ipa_hostname,
+                                             sudo_ctx->ip_addr,
+                                             sudo_ctx->ipa_hostgroups);
+    if (host_filter == NULL) {
+        goto done;
+    }
+
+    filter = talloc_asprintf(tmp_ctx, "(%s%s)",
+                                 rule_filter, host_filter);
+ 
+    /* FIXME
+    filter = sdap_get_id_specific_filter(tmp_ctx, rule_filter, host_filter);
+    if (filter == NULL) {
+        goto done;
+    }
+    */
+
+    talloc_steal(mem_ctx, filter);
+
+done:
+    talloc_free(tmp_ctx);
+    return filter;
+}
+
+/* issue a full refresh of sudo rules */
+struct tevent_req *ipa_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct be_ctx *be_ctx,
+                                              struct be_ptask *be_ptask,
+                                              void *pvt)
+{
+    struct ipa_sudo_full_refresh_state *state = NULL;
+    struct sdap_sudo_ctx *sudo_ctx = NULL;
+    struct sdap_id_ctx *id_ctx = NULL;
+    struct tevent_req *subreq = NULL;
+    struct tevent_req *req = NULL;
+
+    sudo_ctx = talloc_get_type(pvt, struct sdap_sudo_ctx);
+    id_ctx = sudo_ctx->id_ctx;
+
+    req = tevent_req_create(mem_ctx, &state, struct ipa_sudo_full_refresh_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    sudo_ctx->full_refresh_in_progress = true;
+
+    state->sudo_ctx = sudo_ctx;
+    state->id_ctx = id_ctx;
+    state->sysdb = id_ctx->be->domain->sysdb;
+    state->domain = id_ctx->be->domain;
+
+    /* get the hostgroups first */
+    if (sudo_ctx->ipa_hostname == NULL) {
+        /* if the hostname of the client isn't FQDN then we can't get sudo 
+         * rules aplicable to a hostgroup but we can still use the other rules
+         */
+        DEBUG(SSSDBG_MINOR_FAILURE, "Hostname isn't FQDN - unable to get "
+                                    "hostgroups - (sudo won't work correctly\n");
+        ipa_sudo_full_refresh_step(req);
+        return req;
+    }
+
+    subreq = ipa_sudo_get_hostgroups_send(sudo_ctx, sudo_ctx);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get hostgroups - (sudo won't "
+                                    "work correctly)\n");
+        ipa_sudo_full_refresh_step(req);
+        return req;
+    }
+ 
+    tevent_req_set_callback(subreq, ipa_sudo_get_hostinfo_finish, req);
+    return req;
+}
+
+static void ipa_sudo_get_hostinfo_finish(struct tevent_req *subreq)
+{
+    struct ipa_sudo_full_refresh_state *state;
+    struct sdap_sudo_ctx *sudo_ctx;
+    struct sysdb_attrs **hostgroups;
+    struct tevent_req *req;
+    const char *group_name;
+    size_t hostgroup_count;
+    errno_t ret = EOK;
+    int dp_error = DP_ERR_OK;
+    int error = EOK;
+    int i;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_full_refresh_state);
+
+    sudo_ctx = state->sudo_ctx;
+
+    ret = ipa_sudo_get_hostgroups_recv(subreq, sudo_ctx, &dp_error, &error,
+                                       &hostgroups, &hostgroup_count);
+    talloc_zfree(subreq);
+    if (ret != EOK || state->dp_error != DP_ERR_OK || state->error != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to retrieve hostgroups information - "
+                                    "(sudo won't work correctly)\n");
+        goto done;
+    }
+
+    sudo_ctx->ipa_hostgroups = talloc_zero_array(sudo_ctx, char *, hostgroup_count+1);
+    if (sudo_ctx->ipa_hostgroups == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_zero_array() failed\n");
+        goto done;
+    }
+
+    for (i = 0; i < hostgroup_count; i++) {
+
+        ret = sysdb_attrs_get_string(hostgroups[i], "name", &group_name);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get common name of a "
+                        "hostgroup. Trying another hostgroup record.\n");
+            continue;
+        }
+
+        sudo_ctx->ipa_hostgroups[i] = talloc_strdup(sudo_ctx->ipa_hostgroups, 
+                                                group_name);
+        if (sudo_ctx->ipa_hostgroups[i] == NULL) {
+            DEBUG(SSSDBG_FATAL_FAILURE, "talloc_strdup() failed\n");
+        }
+    }
+
+    sudo_ctx->ipa_hostgroups[hostgroup_count] = NULL;
+
+done:
+    ipa_sudo_full_refresh_step(req);
+}
+
+static void ipa_sudo_full_refresh_step(struct tevent_req *req)
+{
+    struct ipa_sudo_full_refresh_state *state;
+    struct tevent_req *subreq;
+    struct sdap_sudo_ctx *sudo_ctx;
+    struct sdap_id_ctx *id_ctx = NULL;
+    char *ldap_full_filter = NULL;
+    char *sysdb_filter = NULL;
+    char *ldap_filter = NULL;
+    int ret = EOK;
+ 
+    state = tevent_req_data(req, struct ipa_sudo_full_refresh_state);
+    sudo_ctx = state->sudo_ctx;
+    id_ctx = state->id_ctx;
+
+     /* filter will match all enabled and aplicable sudo rules to this host at IPA */
+    ldap_filter = talloc_asprintf(state, IPA_SUDO_FULL_FILTER,
+                    sudo_ctx->id_ctx->opts->ipa_sudorule_map[SDAP_OC_SUDORULE].name);
+    if (ldap_filter == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_asprintf() failed\n");
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ldap_full_filter = ipa_sudo_get_filter(state, id_ctx->opts->ipa_sudorule_map,
+                                           sudo_ctx, ldap_filter);
+    if (ldap_full_filter == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    /* close the filter */
+    ldap_full_filter = talloc_strdup_append_buffer(ldap_full_filter, ")");
+    if (ldap_full_filter == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_strdup_append_buffer() failed\n");
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    /* Remove all rules from cache */
+    sysdb_filter = talloc_asprintf(state, "(%s=%s)",
+                                   SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC);
+    if (sysdb_filter == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_asprintf() failed\n");
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of IPA sudo rules\n");
+
+    subreq = ipa_sudo_refresh_send(state, id_ctx->be, id_ctx->opts,
+                                   id_ctx->conn->conn_cache,
+                                   ldap_full_filter, sysdb_filter);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_full_refresh_done, req);
+
+    /* free filters */
+    talloc_free(ldap_filter);
+    talloc_free(ldap_full_filter);
+    talloc_free(sysdb_filter);
+
+    return;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, id_ctx->be->ev);
+}
+
+static void ipa_sudo_full_refresh_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = NULL;
+    struct ipa_sudo_full_refresh_state *state = NULL;
+    char *highest_usn = NULL;
+    int ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_full_refresh_state);
+
+    ret = ipa_sudo_refresh_recv(state, subreq, &state->dp_error,
+                                &state->error, &highest_usn, NULL, NULL);
+    talloc_zfree(subreq);
+    if (ret != EOK || state->dp_error != DP_ERR_OK || state->error != EOK) {
+        goto done;
+    }
+
+    state->sudo_ctx->full_refresh_done = true;
+
+    /* save the time in the sysdb */
+    ret = sysdb_sudo_set_last_full_refresh(state->domain, time(NULL));
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to save time of "
+                                    "a successful full refresh\n");
+        /* this is only a minor error that does not affect the functionality,
+         * therefore there is no need to report it with tevent_req_error()
+         * which would cause problems in the consumers */
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Successful full refresh of IPA sudo rules\n");
+
+    /* set highest usn */
+    if (highest_usn != NULL) {
+        sdap_sudo_set_usn(state->id_ctx->srv_opts, highest_usn);
+    }
+
+done:
+    state->sudo_ctx->full_refresh_in_progress = false;
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+/* Full refresh can be perform by ptask or as request from SUDO responder. 
+ * Prototype of a ptask recv func is different than recv func in sudo reply 
+ * so full refresh request has to have two recv functions */
+int ipa_sudo_full_refresh_recv(struct tevent_req *req,
+                               int *dp_error,
+                               int *error)
+{
+    struct ipa_sudo_full_refresh_state *state = NULL;
+    state = tevent_req_data(req, struct ipa_sudo_full_refresh_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *dp_error = state->dp_error;
+    *error = state->error;
+
+    return EOK;
+}
+
+int ipa_sudo_full_refresh_ptask_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+/* issue a smart refresh of IPA SUDO rules */
+struct tevent_req *ipa_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct be_ctx *be_ctx,
+                                   struct be_ptask *be_ptask,
+                                   void *pvt)
+{
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct sdap_id_ctx *id_ctx = NULL;
+    struct sdap_sudo_ctx *sudo_ctx = NULL;
+    struct sdap_attr_map *map = NULL;
+    struct sdap_server_opts *srv_opts = NULL;
+    struct ipa_sudo_smart_refresh_state *state = NULL;
+    char *ldap_filter = NULL;
+    char *ldap_smart_filter = NULL;
+    const char *usn;
+    int ret = EOK;
+
+    sudo_ctx = talloc_get_type(pvt, struct sdap_sudo_ctx);
+    id_ctx = sudo_ctx->id_ctx;
+    map = id_ctx->opts->ipa_sudorule_map;
+    srv_opts = id_ctx->srv_opts;
+ 
+    req = tevent_req_create(mem_ctx, &state, struct ipa_sudo_smart_refresh_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    if (!sudo_ctx->full_refresh_done
+            && (srv_opts == NULL || srv_opts->max_sudo_value == 0)) {
+        /* Perform full refresh first */
+        DEBUG(SSSDBG_TRACE_FUNC, "USN value is unknown, "
+                                 "waiting for a full refresh!\n");
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    state->id_ctx = id_ctx;
+    state->sysdb = id_ctx->be->domain->sysdb;
+
+    /* Download all rules from LDAP that are newer than usn */
+    usn = srv_opts->max_sudo_value;
+    if (usn != NULL) {
+        ldap_filter = talloc_asprintf(state, IPA_SUDO_SMART_FILTER,
+                                      map[SDAP_OC_IPA_SUDORULE].name,
+                                      map[SDAP_AT_IPA_SUDO_USN].name, usn,
+                                      map[SDAP_AT_IPA_SUDO_USN].name, usn);
+    } else {
+        /* no valid USN value known, match any sudo rule */
+        ldap_filter = talloc_asprintf(state, IPA_SUDO_FULL_FILTER,
+                                      map[SDAP_OC_SUDORULE].name);
+    }
+    if (ldap_filter == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ldap_smart_filter = ipa_sudo_get_filter(state, map, sudo_ctx, ldap_filter);
+    if (ldap_smart_filter == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    /* close the filter */
+    ldap_smart_filter = talloc_asprintf_append_buffer(ldap_smart_filter, ")");
+    if (ldap_smart_filter == NULL) {
+        goto immediately;
+    }
+
+    /* Do not remove any rules that are already in the sysdb
+     * sysdb_filter = NULL;
+     */
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Issuing a smart refresh of IPA sudo rules "
+                             "(USN > %s)\n", (usn == NULL ? "0" : usn));
+
+    subreq = ipa_sudo_refresh_send(state, id_ctx->be, id_ctx->opts,
+                                    id_ctx->conn->conn_cache,
+                                    ldap_smart_filter, NULL);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    state->subreq = subreq;
+    tevent_req_set_callback(subreq, ipa_sudo_smart_refresh_done, req);
+
+    /* free filters */
+    talloc_free(ldap_filter);
+    talloc_free(ldap_smart_filter);
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, id_ctx->be->ev);
+
+    return req;
+}
+
+static void ipa_sudo_smart_refresh_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = NULL;
+    struct ipa_sudo_smart_refresh_state *state = NULL;
+    char *highest_usn = NULL;
+    int dp_error;
+    int error;
+    int ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_smart_refresh_state);
+
+    ret = ipa_sudo_refresh_recv(state, subreq, &dp_error, &error,
+                                 &highest_usn, NULL, NULL);
+    if (ret != EOK || dp_error != DP_ERR_OK || error != EOK) {
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Successful smart refresh of IPA sudo rules\n");
+
+    /* set highest usn */
+    if (highest_usn != NULL) {
+        sdap_sudo_set_usn(state->id_ctx->srv_opts, highest_usn);
+    }
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+int ipa_sudo_smart_refresh_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+/* issue a refresh of specific sudo rules */
+struct tevent_req *ipa_sudo_rules_refresh_send(TALLOC_CTX *mem_ctx,
+                                               struct sdap_sudo_ctx *sudo_ctx,
+                                               struct be_ctx *be_ctx,
+                                               struct sdap_options *opts,
+                                               struct sdap_id_conn_cache *conn_cache,
+                                               char **rules)
+{
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct ipa_sudo_rules_refresh_state *state = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *ldap_filter = NULL;
+    char *ldap_rules_filter = NULL;
+    char *sysdb_filter = NULL;
+    char *safe_rule = NULL;
+    int ret = EOK;
+    int i;
+
+    if (rules == NULL) {
+        return NULL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        return NULL;
+    }
+
+    req = tevent_req_create(mem_ctx, &state, struct ipa_sudo_rules_refresh_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    ldap_filter = talloc_zero(tmp_ctx, char);
+    sysdb_filter = talloc_zero(tmp_ctx, char);
+
+    /* Download only selected rules from IPA and remove all selected rules from
+     * cache */
+    for (i = 0; rules[i] != NULL; i++) {
+        ret = sss_filter_sanitize(tmp_ctx, rules[i], &safe_rule);
+        if (ret != EOK) {
+            ret = ENOMEM;
+            goto immediately;
+        }
+
+        ldap_filter = talloc_asprintf_append_buffer(ldap_filter, "(%s=%s)",
+                                     opts->ipa_sudorule_map[SDAP_AT_SUDO_NAME].name,
+                                     safe_rule);
+        if (ldap_filter == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf_append_buffer() failed\n"));
+            ret = ENOMEM;
+            goto immediately;
+        }
+
+        sysdb_filter = talloc_asprintf_append_buffer(sysdb_filter, "(%s=%s)",
+                                                     SYSDB_SUDO_CACHE_AT_CN,
+                                                     safe_rule);
+        if (sysdb_filter == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf_append_buffer() failed\n"));
+            ret = ENOMEM;
+            goto immediately;
+        }
+    }
+
+    state->id_ctx = sudo_ctx->id_ctx;
+    state->num_rules = i;
+
+    ldap_filter = talloc_asprintf(tmp_ctx, IPA_SUDO_RULES_FILTER,
+                                  opts->ipa_sudorule_map[SDAP_OC_IPA_SUDORULE].name,
+                                  ldap_filter);
+    if (ldap_filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf() failed\n"));
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ldap_rules_filter = ipa_sudo_get_filter(tmp_ctx, opts->ipa_sudorule_map,
+                                           sudo_ctx, ldap_filter);
+    if (ldap_rules_filter == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    /* close the filter */
+    ldap_rules_filter = talloc_asprintf_append_buffer(ldap_rules_filter, ")");
+    if (ldap_rules_filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf_append_buffer() failed\n"));
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+
+    sysdb_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|%s))",
+                                   SYSDB_OBJECTCLASS, SYSDB_SUDO_CACHE_OC,
+                                   sysdb_filter);
+    if (sysdb_filter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf() failed\n"));
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    subreq = ipa_sudo_refresh_send(req, be_ctx, opts, conn_cache,
+                                    ldap_rules_filter, sysdb_filter);
+
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, ipa_sudo_rules_refresh_done, req);
+
+immediately:
+    talloc_free(tmp_ctx);
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, be_ctx->ev);
+    }
+
+    return req;
+}
+
+static void ipa_sudo_rules_refresh_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = NULL;
+    struct ipa_sudo_rules_refresh_state *state = NULL;
+    char *highest_usn = NULL;
+    size_t downloaded_rules_num;
+    int ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_sudo_rules_refresh_state);
+
+    ret = ipa_sudo_refresh_recv(state, subreq, &state->dp_error, &state->error,
+                                 &highest_usn, &downloaded_rules_num, NULL);
+    talloc_zfree(subreq);
+    if (ret != EOK || state->dp_error != DP_ERR_OK || state->error != EOK) {
+        goto done;
+    }
+
+    /* set highest usn */
+    if (highest_usn != NULL) {
+        sdap_sudo_set_usn(state->id_ctx->srv_opts, highest_usn);
+    }
+
+    if (downloaded_rules_num != state->num_rules) {
+        state->error = ENOENT;
+    }
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+int ipa_sudo_rules_refresh_recv(struct tevent_req *req,
+                                int *dp_error,
+                                int *error)
+{
+    struct ipa_sudo_rules_refresh_state *state = NULL;
+    state = tevent_req_data(req, struct ipa_sudo_rules_refresh_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *dp_error = state->dp_error;
+    *error = state->error;
+
+    return EOK;
+}
diff --git a/src/providers/ipa/ipa_sudo_refreshes.h b/src/providers/ipa/ipa_sudo_refreshes.h
new file mode 100644
index 0000000000000000000000000000000000000000..eaacda823e7ffe94aac34669bbf83c44528cd637
--- /dev/null
+++ b/src/providers/ipa/ipa_sudo_refreshes.h
@@ -0,0 +1,54 @@
+/*
+    SSSD
+
+    Authors:
+        MIchal Šrubař <xsruba03@stud.fit.vutbr.cz>
+
+    Copyright (C) 2014 MIchal Šrubař
+
+    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_SUDO_REFRESHES_H_
+#define _IPA_SUDO_REFRESHES_H_
+
+struct tevent_req *ipa_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct be_ctx *be_ctx,
+                                              struct be_ptask *be_ptask,
+                                              void *pvt);
+int ipa_sudo_full_refresh_ptask_recv(struct tevent_req *req);
+int ipa_sudo_full_refresh_recv(struct tevent_req *req,
+                               int *dp_error,
+                               int *error);
+
+struct tevent_req *ipa_sudo_rules_refresh_send(TALLOC_CTX *mem_ctx,
+                                               struct sdap_sudo_ctx *sudo_ctx,
+                                               struct be_ctx *be_ctx,
+                                               struct sdap_options *opts,
+                                               struct sdap_id_conn_cache *conn_cache,
+                                               char **rules);
+int ipa_sudo_rules_refresh_recv(struct tevent_req *req,
+                                int *dp_error,
+                                int *error);
+
+struct tevent_req *ipa_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct be_ctx *be_ctx,
+                                   struct be_ptask *be_ptask,
+                                   void *pvt);
+int ipa_sudo_smart_refresh_recv(struct tevent_req *req);
+
+#endif  // _IPA_SUDO_REFRESHES_H_
diff --git a/src/providers/ldap/ldap_options.c b/src/providers/ldap/ldap_options.c
index eb00aab32a55c6841ebbf32d30f3a48722e8b165..14e89825c1bc3cd609d85a68a1e962451baa7903 100644
--- a/src/providers/ldap/ldap_options.c
+++ b/src/providers/ldap/ldap_options.c
@@ -394,6 +394,26 @@ int ldap_get_sudo_options(TALLOC_CTX *memctx,
         return ret;
     }
 
+    /* attrs map for IPA SUDO Provider */
+    ret = sdap_get_map(opts, cdb, conf_path,
+                       ipa_sudorule_map,
+                       SDAP_OPTS_IPA_SUDO,
+                       &opts->ipa_sudorule_map);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Could not get IPA SUDO attribute map\n");
+        return ret;
+    }
+
+    /* attrs map for IPA SUDO commands */
+    ret = sdap_get_map(opts, cdb, conf_path,
+                       ipa_sudocmds_map,
+                       SDAP_OPTS_SUDO_CMD,
+                       &opts->ipa_sudocmds_map);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Could not get IPA SUDO commnads attribute map\n");
+        return ret;
+    }
+
     /* host filter */
     *use_host_filter = dp_opt_get_bool(opts->basic, SDAP_SUDO_USE_HOST_FILTER);
     *include_netgroups = dp_opt_get_bool(opts->basic, SDAP_SUDO_INCLUDE_NETGROUPS);
diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h
index 13a84ec1e979864e06d5690ec29967548b21aac1..c1584ae21df81f31974ad4738d76572fce4da2d3 100644
--- a/src/providers/ldap/ldap_opts.h
+++ b/src/providers/ldap/ldap_opts.h
@@ -321,6 +321,37 @@ struct sdap_attr_map native_sudorule_map[] = {
     SDAP_ATTR_MAP_TERMINATOR
 };
 
+struct sdap_attr_map ipa_sudorule_map[] = {
+    { "ipa_sudorule_object_class", "ipasudorule", "ipasudorule", NULL },
+    { "ldap_sudorule_name", "cn", SYSDB_SUDO_CACHE_AT_CN, NULL },
+    { "ldap_sudorule_usercategory", "userCategory", "userCategory", NULL },
+    { "ldap_sudorule_memberuser", "memberUser", "memberUser", NULL },
+    { "ldap_sudorule_externaluser", "externalUser", SYSDB_SUDO_CACHE_AT_USER, NULL },
+    { "ldap_sudorule_hostcategory", "hostCategory", "hostCategory", NULL },
+    { "ldap_sudorule_memberhost", "memberHost", "memberHost", NULL },
+    { "ldap_sudorule_externalhost", "externalHost", SYSDB_SUDO_CACHE_AT_HOST, NULL },
+    { "ldap_sudorule_cmdcategory", "cmdCategory", "cmdCategory", NULL },
+    { "ldap_sudorule_memberallowcmd", "memberAllowCmd", "memberAllowCmd", NULL },
+    { "ldap_sudorule_memberdenycmd", "memberDenyCmd", "memberDenyCmd", NULL },
+    { "ldap_sudorule_ipasudoopt", "ipaSudoOpt", SYSDB_SUDO_CACHE_AT_OPTION, NULL },
+    { "ldap_sudorule_ipasudorunasusercategory", "ipaSudoRunAsUserCategory", "ipaSudoRunAsUserCategory", NULL },
+    { "ldap_sudorule_ipasudorunas", "ipaSudoRunAs", "ipaSudoRunAs", NULL },
+    { "ldap_sudorule_ipasudorunasextuser", "ipaSudoRunAsExtUser", SYSDB_SUDO_CACHE_AT_RUNASUSER, NULL },
+    { "ldap_sudorule_ipasudorunasgroupcategory", "ipaSudoRunAsGroupCategory", "ipaSudoRunAsGroupCategory", NULL },
+    { "ldap_sudorule_ipasudorunasgroup", "ipaSudoRunAsGroup", "ipaSudoRunAsGroup", NULL },
+    { "ldap_sudorule_ipasudorunasextgroup", "ipaSudoRunAsExtGroup", SYSDB_SUDO_CACHE_AT_RUNASGROUP, NULL },
+    { "ldap_sudorule_entry_usn", "entryUSN", SYSDB_USN, NULL },
+    SDAP_ATTR_MAP_TERMINATOR
+};
+
+struct sdap_attr_map ipa_sudocmds_map[] = {
+    { "ipa_sudocmd_object_class", "ipasudocmd", "ipasudocmd", NULL },
+    { "ipa_sudocmd_ipauniqueid", "ipaUniqueID", "ipaUniqueID", NULL },
+    { "ipa_sudocmd_command", "sudoCmd", "sudoCmd", NULL },
+    { "ipa_sudocmd_memberof", "memberOf", "memberOf", NULL },
+    SDAP_ATTR_MAP_TERMINATOR
+};
+
 struct sdap_attr_map service_map[] = {
     { "ldap_service_object_class", "ipService", SYSDB_SVC_CLASS, NULL },
     { "ldap_service_name", "cn", SYSDB_NAME, NULL },
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 6bab0e1c1ce520ba248cc6634349493d82b31233..0f3745a03ae083b3b034a776ea57d4a69c763178 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -330,6 +330,39 @@ enum sdap_sudorule_attrs {
     SDAP_OPTS_SUDO  /* attrs counter */
 };
 
+enum sdap_ipa_sudorule_attrs {
+    SDAP_OC_IPA_SUDORULE = 0,
+    SDAP_AT_IPA_SUDO_NAME,
+    SDAP_AT_IPA_SUDO_USER_CAT,
+    SDAP_AT_IPA_SUDO_MEM_USER,
+    SDAP_AT_IPA_SUDO_EXT_USER,
+    SDAP_AT_IPA_SUDO_HOST_CAT,
+    SDAP_AT_IPA_SUDO_MEM_HOST,
+    SDAP_AT_IPA_SUDO_EXT_HOST,
+    SDAP_AT_IPA_SUDO_CMD_CAT,
+    SDAP_AT_IPA_SUDO_MEM_ALLOW_CMD,
+    SDAP_AT_IPA_SUDO_MEM_DENY_CMD,
+    SDAP_AT_IPA_SUDO_OPT,
+    SDAP_AT_IPA_SUDO_RUN_AS_USER_CAT,
+    SDAP_AT_IPA_SUDO_RUN_AS,
+    SDAP_AT_IPA_SUDO_RUN_AS_EXT_USER,
+    SDAP_AT_IPA_SUDO_RUN_AS_GROUP_CAT,
+    SDAP_AT_IPA_SUDO_RUN_AS_GROUP,
+    SDAP_AT_IPA_SUDO_RUN_AS_EXT_GROUP,
+    SDAP_AT_IPA_SUDO_USN,
+
+    SDAP_OPTS_IPA_SUDO  /* attrs counter */
+};
+
+enum sdap_ipa_sudocmds_attrs {
+    SDAP_OC_SUDO_CMD = 0,
+    SDAP_OC_SUDO_CMD_CMD,
+    SDAP_OC_SUDO_CMD_MEMBEROF,
+    SDAP_OC_SUDO_CMD_IPAUNIQUEID,
+
+    SDAP_OPTS_SUDO_CMD  /* attrs counter */
+};
+
 enum sdap_service_attrs {
     SDAP_OC_SERVICE = 0,
     SDAP_AT_SERVICE_NAME,
@@ -422,6 +455,8 @@ struct sdap_options {
 
     /* FIXME - should this go to a special struct to avoid mixing with name-service-switch maps? */
     struct sdap_attr_map *sudorule_map;
+    struct sdap_attr_map *ipa_sudorule_map;         /* map for IPA SUDO scheme */
+    struct sdap_attr_map *ipa_sudocmds_map;         /* map for IPA SUDO commands */
     struct sdap_attr_map *autofs_mobject_map;
     struct sdap_attr_map *autofs_entry_map;
 
diff --git a/src/providers/ldap/sdap_async_sudo.c b/src/providers/ldap/sdap_async_sudo.c
index b4899cbaa4c5821a79f1b228a88a4d6055c1558c..6d4ae9f57ac85d3136703781a3ff075810b7e034 100644
--- a/src/providers/ldap/sdap_async_sudo.c
+++ b/src/providers/ldap/sdap_async_sudo.c
@@ -34,23 +34,6 @@
 #include "providers/ldap/sdap_sudo_cache.h"
 #include "db/sysdb_sudo.h"
 
-struct sdap_sudo_refresh_state {
-    struct be_ctx *be_ctx;
-    struct sdap_options *opts;
-    struct sdap_id_op *sdap_op;
-    struct sdap_id_conn_cache *sdap_conn_cache;
-    struct sysdb_ctx *sysdb;
-    struct sss_domain_info *domain;
-
-    const char *ldap_filter;    /* search */
-    const char *sysdb_filter;   /* delete */
-
-    int dp_error;
-    int error;
-    char *highest_usn;
-    size_t num_rules;
-};
-
 struct sdap_sudo_load_sudoers_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
@@ -79,27 +62,9 @@ static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req);
 
 static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq);
 
-static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
-                                       TALLOC_CTX *mem_ctx,
-                                       size_t *rules_count,
-                                       struct sysdb_attrs ***rules);
-
-static void sdap_sudo_refresh_load_done(struct tevent_req *subreq);
-
-static int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
-                                   const char *filter,
-                                   struct sdap_attr_map *map,
-                                   size_t rules_count,
-                                   struct sysdb_attrs **rules);
-
-static int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
-                                   struct sss_domain_info *domain,
-                                   struct sdap_options *opts,
-                                   size_t rules_count,
-                                   struct sysdb_attrs **rules,
-                                   int cache_timeout,
-                                   time_t now,
-                                   char **_usn);
+static void sdap_sudo_refresh_load_done_ex(struct tevent_req *subreq);
+static void sdap_sudo_refresh_load_done_ldap(struct tevent_req *subreq);
+static void sdap_sudo_refresh_load_done_ipa(struct tevent_req *subreq);
 
 struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
                                           struct be_ctx *be_ctx,
@@ -167,7 +132,8 @@ int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
                            int *dp_error,
                            int *error,
                            char **usn,
-                           size_t *num_rules)
+                           size_t *num_rules,
+                           struct sysdb_attrs ***rules)
 {
     struct sdap_sudo_refresh_state *state;
 
@@ -186,6 +152,10 @@ int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
         *num_rules = state->num_rules;
     }
 
+    if (rules != NULL) {
+        *rules = talloc_steal(mem_ctx, state->ldap_rules);
+    }
+
     return EOK;
 }
 
@@ -264,7 +234,7 @@ static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
         goto fail;
     }
 
-    tevent_req_set_callback(subreq, sdap_sudo_refresh_load_done, req);
+    tevent_req_set_callback(subreq, sdap_sudo_refresh_load_done_ex, req);
 
     return;
 
@@ -279,9 +249,6 @@ static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
                                                        struct sdap_options *opts,
                                                        struct sdap_handle *sh,
                                                        const char *ldap_filter)
-
-
-
 {
     struct tevent_req *req;
     struct sdap_sudo_load_sudoers_state *state;
@@ -310,8 +277,16 @@ static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
     }
 
     /* create attrs from map */
-    ret = build_attrs_from_map(state, opts->sudorule_map, SDAP_OPTS_SUDO,
+    if (state->opts->schema_type == SDAP_SCHEMA_IPA_V1) {
+        /* req from ipa_sudo_refresh_send() */
+        ret = build_attrs_from_map(state, opts->ipa_sudorule_map,
+                                   SDAP_OPTS_IPA_SUDO, NULL, &state->attrs, NULL);
+    } else {
+        /* req from sdap_sudo_refresh_send() */
+        ret = build_attrs_from_map(state, opts->sudorule_map, SDAP_OPTS_SUDO,
                                NULL, &state->attrs, NULL);
+    }
+
     if (ret != EOK) {
         goto fail;
     }
@@ -337,6 +312,8 @@ static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req)
     struct sdap_sudo_load_sudoers_state *state;
     struct sdap_search_base *search_base;
     struct tevent_req *subreq;
+    struct sdap_attr_map *map;
+    int attr_count;
     char *filter;
 
     state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
@@ -359,6 +336,17 @@ static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req)
           "Searching for sudo rules with base [%s]\n",
            search_base->basedn);
 
+    if (state->opts->schema_type == SDAP_SCHEMA_IPA_V1) {
+        /* req from ipa_sudo_refresh_send() */
+        attr_count = SDAP_OPTS_IPA_SUDO;
+        map = state->opts->ipa_sudorule_map;
+    }
+    else {
+        /* req from sdap_sudo_refresh_send() */
+        attr_count = SDAP_OPTS_SUDO;
+        map = state->opts->sudorule_map;
+    }
+
     subreq = sdap_get_generic_send(state,
                                    state->ev,
                                    state->opts,
@@ -367,8 +355,8 @@ static errno_t sdap_sudo_load_sudoers_next_base(struct tevent_req *req)
                                    search_base->scope,
                                    filter,
                                    state->attrs,
-                                   state->opts->sudorule_map,
-                                   SDAP_OPTS_SUDO,
+                                   map,
+                                   attr_count,
                                    state->timeout,
                                    true);
     if (subreq == NULL) {
@@ -390,6 +378,7 @@ static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq)
     int ret;
     int i;
 
+    /* req from sdap_sudo_load_sudoers_send */
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct sdap_sudo_load_sudoers_state);
     search_base = state->search_bases[state->base_iter];
@@ -438,10 +427,10 @@ static void sdap_sudo_load_sudoers_process(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
-static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
-                                       TALLOC_CTX *mem_ctx,
-                                       size_t *rules_count,
-                                       struct sysdb_attrs ***rules)
+int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
+                                TALLOC_CTX *mem_ctx,
+                                size_t *rules_count,
+                                struct sysdb_attrs ***rules)
 {
     struct sdap_sudo_load_sudoers_state *state;
 
@@ -455,7 +444,59 @@ static int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
     return EOK;
 }
 
-static void sdap_sudo_refresh_load_done(struct tevent_req *subreq)
+static void sdap_sudo_refresh_load_done_ex(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_sudo_refresh_state *state;
+
+    /* req from sdap_sudo_refresh_send() */
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
+    if (state->opts->schema_type == SDAP_SCHEMA_IPA_V1) {
+        /* req from ipa_sudo_refresh_send() */
+        sdap_sudo_refresh_load_done_ipa(subreq);
+    }
+    else {
+        /* req from sdap_sudo_refresh_send() */
+        sdap_sudo_refresh_load_done_ldap(subreq);
+    }
+}
+
+static void sdap_sudo_refresh_load_done_ipa(struct tevent_req *subreq)
+{
+    struct sdap_sudo_refresh_state *state;
+    struct tevent_req *req;
+    int ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
+    ret = sdap_sudo_load_sudoers_recv(subreq, state, &state->ldap_rules_count,
+                                      &state->ldap_rules);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Received %zu rules\n", state->ldap_rules_count);
+    DEBUG(SSSDBG_TRACE_FUNC, "Giving control back to IPA SUDO provider\n");
+
+    state->num_rules = state->ldap_rules_count;
+
+done:
+    /* req from ipa_sudo_refresh_send continue in ipa_sudo_sudoers_process */
+    state->error = ret;
+    if (ret == EOK) {
+        state->dp_error = DP_ERR_OK;
+        tevent_req_done(req);
+    } else {
+        state->dp_error = DP_ERR_FATAL;
+        tevent_req_error(req, ret);
+    }
+}
+
+static void sdap_sudo_refresh_load_done_ldap(struct tevent_req *subreq)
 {
     struct tevent_req *req; /* req from sdap_sudo_refresh_send() */
     struct sdap_sudo_refresh_state *state;
@@ -523,6 +564,7 @@ done:
         }
     }
 
+    /* finish sdap_sudo_refresh_send() */
     state->error = ret;
     if (ret == EOK) {
         state->dp_error = DP_ERR_OK;
@@ -533,11 +575,11 @@ done:
     }
 }
 
-static int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
-                                   const char *filter,
-                                   struct sdap_attr_map *map,
-                                   size_t rules_count,
-                                   struct sysdb_attrs **rules)
+int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
+                            const char *filter,
+                            struct sdap_attr_map *map,
+                            size_t rules_count,
+                            struct sysdb_attrs **rules)
 {
     const char *name;
     int i;
@@ -586,14 +628,14 @@ done:
     return ret;
 }
 
-static int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
-                                   struct sss_domain_info *domain,
-                                   struct sdap_options *opts,
-                                   size_t rules_count,
-                                   struct sysdb_attrs **rules,
-                                   int cache_timeout,
-                                   time_t now,
-                                   char **_usn)
+int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domain,
+                            struct sdap_options *opts,
+                            size_t rules_count,
+                            struct sysdb_attrs **rules,
+                            int cache_timeout,
+                            time_t now,
+                            char **_usn)
 {
     errno_t ret;
 
diff --git a/src/providers/ldap/sdap_sudo.c b/src/providers/ldap/sdap_sudo.c
index edef44208b69af454da0fc906e608b91d5634e90..1e3bf5fa216789962f135d8dacdef17a9b6cdbd5 100644
--- a/src/providers/ldap/sdap_sudo.c
+++ b/src/providers/ldap/sdap_sudo.c
@@ -288,7 +288,7 @@ static int sdap_sudo_setup_periodical_refresh(struct sdap_sudo_ctx *sudo_ctx)
     return EOK;
 }
 
-static void sdap_sudo_set_usn(struct sdap_server_opts *srv_opts, char *usn)
+void sdap_sudo_set_usn(struct sdap_server_opts *srv_opts, char *usn)
 {
     unsigned int usn_number;
     char *endptr = NULL;
@@ -621,7 +621,7 @@ static void sdap_sudo_full_refresh_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct sdap_sudo_full_refresh_state);
 
     ret = sdap_sudo_refresh_recv(state, subreq, &state->dp_error,
-                                 &state->error, &highest_usn, NULL);
+                                 &state->error, &highest_usn, NULL, NULL);
     talloc_zfree(subreq);
     if (ret != EOK || state->dp_error != DP_ERR_OK || state->error != EOK) {
         goto done;
@@ -795,7 +795,7 @@ static void sdap_sudo_rules_refresh_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct sdap_sudo_rules_refresh_state);
 
     ret = sdap_sudo_refresh_recv(state, subreq, &state->dp_error, &state->error,
-                                 &highest_usn, &downloaded_rules_num);
+                                 &highest_usn, &downloaded_rules_num, NULL);
     talloc_zfree(subreq);
     if (ret != EOK || state->dp_error != DP_ERR_OK || state->error != EOK) {
         goto done;
@@ -938,7 +938,7 @@ static void sdap_sudo_smart_refresh_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct sdap_sudo_smart_refresh_state);
 
     ret = sdap_sudo_refresh_recv(state, subreq, &dp_error, &error,
-                                 &highest_usn, NULL);
+                                 &highest_usn, NULL, NULL);
     if (ret != EOK || dp_error != DP_ERR_OK || error != EOK) {
         goto done;
     }
@@ -969,7 +969,7 @@ static int sdap_sudo_smart_refresh_recv(struct tevent_req *req,
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return sdap_sudo_refresh_recv(state, state->subreq, dp_error, error,
-                                  NULL, NULL);
+                                  NULL, NULL, NULL);
 }
 
 static void sdap_sudo_full_refresh_online_cb(void *pvt)
diff --git a/src/providers/ldap/sdap_sudo.h b/src/providers/ldap/sdap_sudo.h
index e2764b90c91470ed64e4c07a792206fa034646a6..00ee72e4bc35d37fdfaf9969736395210ee4a43e 100644
--- a/src/providers/ldap/sdap_sudo.h
+++ b/src/providers/ldap/sdap_sudo.h
@@ -21,11 +21,17 @@
 #ifndef _SDAP_SUDO_H_
 #define _SDAP_SUDO_H_
 
+/* these two structures are used by both LDAP and IPA SUDO Providers */
 struct sdap_sudo_ctx {
+    struct be_ctx *be_ctx;
     struct sdap_id_ctx *id_ctx;
 
     char **hostnames;
     char **ip_addr;
+
+    char *ipa_hostname;
+    char **ipa_hostgroups;
+
     bool include_netgroups;
     bool include_regexp;
     bool use_host_filter;
@@ -37,6 +43,26 @@ struct sdap_sudo_ctx {
     struct tevent_req *first_refresh_timer;
 };
 
+struct sdap_sudo_refresh_state {
+    struct be_ctx *be_ctx;
+    struct sdap_options *opts;
+    struct sdap_id_op *sdap_op;
+    struct sdap_id_conn_cache *sdap_conn_cache;
+    struct sysdb_ctx *sysdb;
+    struct sss_domain_info *domain;
+
+    const char *ldap_filter;    /* search */
+    const char *sysdb_filter;   /* delete */
+
+    struct sysdb_attrs **ldap_rules; /* search result will be stored here */
+    size_t ldap_rules_count;
+
+    int dp_error;
+    int error;
+    char *highest_usn;
+    size_t num_rules;
+};
+
 enum sdap_sudo_refresh_type {
     SDAP_SUDO_REFRESH_FULL,
     SDAP_SUDO_REFRESH_SMART,
@@ -49,6 +75,7 @@ int sdap_sudo_init(struct be_ctx *be_ctx,
                    struct sdap_id_ctx *id_ctx,
                    struct bet_ops **ops,
                    void **pvt_data);
+void sdap_sudo_set_usn(struct sdap_server_opts *srv_opts, char *usn);
 
 /* sdap async interface */
 struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
@@ -58,14 +85,35 @@ struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
                                           const char *ldap_filter,
                                           const char *sysdb_filter);
 
+int sdap_sudo_load_sudoers_recv(struct tevent_req *req,
+                                       TALLOC_CTX *mem_ctx,
+                                       size_t *rules_count,
+                                       struct sysdb_attrs ***rules);
+
 int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
                            struct tevent_req *req,
                            int *dp_error,
                            int *error,
                            char **usn,
-                           size_t *num_rules);
+                           size_t *num_rules,
+                           struct sysdb_attrs ***rules);
 
-/* timer */
+
+/* sysdb */ 
+int sdap_sudo_purge_sudoers(struct sss_domain_info *dom,
+                            const char *filter,
+                            struct sdap_attr_map *map,
+                            size_t rules_count,
+                            struct sysdb_attrs **rules);
+
+int sdap_sudo_store_sudoers(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domain,
+                            struct sdap_options *opts,
+                            size_t rules_count,
+                            struct sysdb_attrs **rules,
+                            int cache_timeout,
+                            time_t now,
+                            char **_usn);
 
 typedef struct tevent_req * (*sdap_sudo_timer_fn_t)(TALLOC_CTX *mem_ctx,
                                                     struct sdap_sudo_ctx *sudo_ctx);
-- 
1.9.3
