From 0710a75dac67209ad421c2239877aa73760f8635 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Mon, 12 Mar 2012 10:07:35 +0100
Subject: [PATCH 2/2] sudo: send uid, username and domainname

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

Test client was changed accordingly. The new usage is:

sss_sudo_cli username [uid]

If uid is not set, getpwnam(username) is called. It will retrieve
both default options and rules.
---
 src/db/sysdb_sudo.c                        |    4 +-
 src/responder/sudo/sudosrv_cmd.c           |  227 ++++++++++-----------------
 src/responder/sudo/sudosrv_get_sudorules.c |  238 +++++++++++-----------------
 src/responder/sudo/sudosrv_private.h       |   74 ++++-----
 src/responder/sudo/sudosrv_query.c         |  231 ++++++++++++++++++++++-----
 src/sss_client/sudo/sss_sudo.c             |  106 ++++++++-----
 src/sss_client/sudo/sss_sudo.h             |   40 ++++--
 src/sss_client/sudo/sss_sudo_private.h     |    1 +
 src/sss_client/sudo/sss_sudo_response.c    |   12 ++
 src/sss_client/sudo_testcli/sudo_testcli.c |   64 ++++++--
 10 files changed, 558 insertions(+), 439 deletions(-)

diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
index f579ba8..e18e2a1 100644
--- a/src/db/sysdb_sudo.c
+++ b/src/db/sysdb_sudo.c
@@ -332,8 +332,10 @@ sysdb_get_sudo_user_info(TALLOC_CTX *mem_ctx, const char *username,
     }
 
     ret = EOK;
-    *_uid = uid;
     *groupnames = talloc_steal(mem_ctx, sysdb_groupnames);
+    if (_uid != NULL) {
+        *_uid = uid;
+    }
 done:
     talloc_free(tmp_ctx);
     return EOK;
diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
index e3dd384..2295c44 100644
--- a/src/responder/sudo/sudosrv_cmd.c
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -78,8 +78,14 @@ static errno_t sudosrv_cmd_send_error(TALLOC_CTX *mem_ctx,
     size_t response_len = 0;
     int ret = EOK;
 
-    ret = sudosrv_response_append_uint32(mem_ctx, error,
-                                         &response_body, &response_len);
+    if (error == EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Everything is fine but we are "
+              "returning error?\n"));
+        return EFAULT;
+    }
+
+    ret = sudosrv_build_response(mem_ctx, error, NULL, 0, NULL,
+                                 &response_body, &response_len);
     if (ret != EOK) {
         return ret;
     }
@@ -87,25 +93,25 @@ static errno_t sudosrv_cmd_send_error(TALLOC_CTX *mem_ctx,
     return sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len);
 }
 
-errno_t sudosrv_cmd_done(struct sudo_dom_ctx *dctx, int ret)
+errno_t sudosrv_cmd_done(struct sudo_cmd_ctx *cmd_ctx, int ret)
 {
     uint8_t *response_body = NULL;
     size_t response_len = 0;
-    size_t num_rules = dctx->res_count;
-    struct sysdb_attrs **rules = dctx->res;
+    size_t num_rules = cmd_ctx->num_rules;
+    struct sysdb_attrs **rules = cmd_ctx->rules;
 
     switch (ret) {
     case EOK:
         /*
-         * Parent of dctx->res is in-memory cache, we must not talloc_free it!
+         * Parent of cmd_ctx->rules is in-memory cache, we must not talloc_free it!
          */
-        if (!dctx->cmd_ctx->sudo_ctx->timed) {
-            num_rules = dctx->res_count;
-            rules = dctx->res;
+        if (!cmd_ctx->sudo_ctx->timed) {
+            num_rules = cmd_ctx->num_rules;
+            rules = cmd_ctx->rules;
         } else {
             /* filter rules by time */
-            ret = sysdb_sudo_filter_rules_by_time(dctx, dctx->res_count,
-                                                  dctx->res, 0,
+            ret = sysdb_sudo_filter_rules_by_time(cmd_ctx, cmd_ctx->num_rules,
+                                                  cmd_ctx->rules, 0,
                                                   &num_rules, &rules);
             if (ret != EOK) {
                 return EFAULT;
@@ -113,14 +119,14 @@ errno_t sudosrv_cmd_done(struct sudo_dom_ctx *dctx, int ret)
         }
 
         /* send result */
-        ret = sudosrv_get_sudorules_build_response(dctx->cmd_ctx, SSS_SUDO_ERROR_OK,
-                                                   num_rules, rules,
-                                                   &response_body, &response_len);
+        ret = sudosrv_build_response(cmd_ctx, SSS_SUDO_ERROR_OK,
+                                     cmd_ctx->domain->name, num_rules, rules,
+                                     &response_body, &response_len);
         if (ret != EOK) {
             return EFAULT;
         }
 
-        ret = sudosrv_cmd_send_reply(dctx->cmd_ctx, response_body, response_len);
+        ret = sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len);
         break;
 
     case EAGAIN:
@@ -139,55 +145,47 @@ errno_t sudosrv_cmd_done(struct sudo_dom_ctx *dctx, int ret)
 
     default:
         /* send error */
-        ret = sudosrv_cmd_send_error(dctx->cmd_ctx, dctx->cmd_ctx, ret);
+        ret = sudosrv_cmd_send_error(cmd_ctx, cmd_ctx, ret);
         break;
     }
 
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Fatal error, killing connection!\n"));
-        talloc_free(dctx->cmd_ctx->cli_ctx);
+        talloc_free(cmd_ctx->cli_ctx);
         return EFAULT;
     }
 
     return EOK;
 }
 
-static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
+static errno_t sudosrv_cmd(enum sss_dp_sudo_type type, struct cli_ctx *cli_ctx)
 {
-    char *rawname = NULL;
-    char *domname = NULL;
+    struct sudo_cmd_ctx *cmd_ctx = NULL;
+    struct sudo_dom_ctx *dom_ctx = NULL;
     uint8_t *query_body = NULL;
     size_t query_len = 0;
-    int ret = EOK;
-    struct sudo_cmd_ctx *cmd_ctx = NULL;
-    struct sudo_dom_ctx *dctx = NULL;
+    errno_t ret;
+
+    /* create cmd_ctx */
 
     cmd_ctx = talloc_zero(cli_ctx, struct sudo_cmd_ctx);
-    if (!cmd_ctx) {
+    if (cmd_ctx == NULL) {
         /* kill the connection here as we have no context for reply */
         DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?\n"));
         return ENOMEM;
     }
-    cmd_ctx->cli_ctx = cli_ctx;
-    cmd_ctx->type = SSS_DP_SUDO_USER;
 
-    /* get responder ctx */
+    cmd_ctx->domain = NULL;
+    cmd_ctx->cli_ctx = cli_ctx;
+    cmd_ctx->type = type;
     cmd_ctx->sudo_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sudo_ctx);
-    if (!cmd_ctx->sudo_ctx) {
+    if (cmd_ctx->sudo_ctx == NULL) {
         DEBUG(SSSDBG_FATAL_FAILURE, ("sudo_ctx not set, killing connection!\n"));
         return EFAULT;
     }
 
-    /* create domain ctx */
-    dctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
-    if (!dctx) {
-        return sudosrv_cmd_send_error(cmd_ctx, cmd_ctx, ENOMEM);
-    }
-    dctx->cmd_ctx = cmd_ctx;
-    dctx->orig_username = NULL;
-    dctx->cased_username = NULL;
+    /* parse query */
 
-    /* get query */
     sss_packet_get_body(cli_ctx->creq->in, &query_body, &query_len);
     if (query_len <= 0 || query_body == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Query is empty\n"));
@@ -195,128 +193,65 @@ static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
         goto done;
     }
 
-    /* If the body isn't valid UTF-8, fail */
-    if (!sss_utf8_check(query_body, query_len - 1)) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("Supplied data is not valid UTF-8 string\n"));
-        ret = EINVAL;
-        goto done;
-    }
-
-    /* parse query */
-    rawname = sudosrv_get_sudorules_parse_query(cmd_ctx,
-                                                (const char*)query_body,
-                                                 query_len);
-    if (rawname == NULL) {
-        ret = ENOMEM;
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              ("Unable to parse query: %s\n", strerror(ret)));
+    ret = sudosrv_parse_query(cmd_ctx, cli_ctx->rctx->domains,
+                              cli_ctx->rctx->names, query_body, query_len,
+                              &cmd_ctx->uid, &cmd_ctx->username, &cmd_ctx->domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query: %s\n", strerror(ret)));
         goto done;
     }
 
-    domname = NULL;
-    ret = sss_parse_name(cmd_ctx, cli_ctx->rctx->names, rawname,
-                         &domname, &cmd_ctx->username);
-    if (ret != EOK) {
-        DEBUG(2, ("Invalid name received [%s]\n", rawname));
-        ret = ENOENT;
-        goto done;
+    cmd_ctx->check_next = cmd_ctx->domain == NULL;
+
+    switch (cmd_ctx->type) {
+        case SSS_DP_SUDO_DEFAULTS:
+            DEBUG(SSSDBG_FUNC_DATA, ("Requesting default options "
+                  "for [%s] from [%s]\n", cmd_ctx->username,
+                  cmd_ctx->domain ? cmd_ctx->domain->name : "<ALL>"));
+            break;
+        case SSS_DP_SUDO_USER:
+            DEBUG(SSSDBG_FUNC_DATA, ("Requesting rules "
+                  "for [%s] from [%s]\n", cmd_ctx->username,
+                  cmd_ctx->domain ? cmd_ctx->domain->name : "<ALL>"));
+            break;
     }
 
-    DEBUG(SSSDBG_FUNC_DATA, ("Requesting sudo rules for [%s] from [%s]\n",
-          cmd_ctx->username, domname ? domname : "<ALL>"));
+    /* create domain ctx */
 
-    if (domname) {
-        dctx->domain = responder_get_domain(cli_ctx->rctx->domains, domname);
-        if (!dctx->domain) {
-            ret = ENOENT;
-            goto done;
-        }
-    } else {
-        /* this is a multidomain search */
-        dctx->domain = cli_ctx->rctx->domains;
-        cmd_ctx->check_next = true;
+    dom_ctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
+    if (dom_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
     }
-
-    /* try to find rules in in-memory cache */
-    ret = sudosrv_cache_lookup(cmd_ctx->sudo_ctx->cache, dctx,
-                               cmd_ctx->check_next, cmd_ctx->username,
-                               &dctx->res_count, &dctx->res);
-    if (ret == EOK) {
-        /* cache hit */
-        DEBUG(SSSDBG_FUNC_DATA, ("Returning rules for [%s@%s] "
-              "from in-memory cache\n", cmd_ctx->username, dctx->domain->name));
-    } else if (ret == ENOENT) {
-        /* cache expired or missed */
-        ret = sudosrv_get_sudorules(dctx);
-    } /* else error */
+    dom_ctx->cmd_ctx = cmd_ctx;
+    dom_ctx->domain = cmd_ctx->domain != NULL ? cmd_ctx->domain
+                                              : cli_ctx->rctx->domains;
+
+//       /* try to find rules in in-memory cache */
+//       ret = sudosrv_cache_lookup(cmd_ctx->sudo_ctx->cache, dctx,
+//                                  cmd_ctx->check_next, cmd_ctx->username,
+//                                  &dctx->res_count, &dctx->res);
+//       if (ret == EOK) {
+//           /* cache hit */
+//           DEBUG(SSSDBG_FUNC_DATA, ("Returning rules for [%s@%s] "
+//                 "from in-memory cache\n", cmd_ctx->username, dctx->domain->name));
+//       } else if (ret == ENOENT) {
+           /* cache expired or missed */
+           ret = sudosrv_get_sudorules(dom_ctx);
+//       } /* else error */
 
 done:
-    return sudosrv_cmd_done(dctx, ret);
+    return sudosrv_cmd_done(cmd_ctx, ret);
 }
 
-static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
+static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
 {
-    int ret = EOK;
-    struct sudo_cmd_ctx *cmd_ctx = NULL;
-    struct sudo_dom_ctx *dctx = NULL;
-
-    cmd_ctx = talloc_zero(cli_ctx, struct sudo_cmd_ctx);
-    if (!cmd_ctx) {
-        /* kill the connection here as we have no context for reply */
-        DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?\n"));
-        return ENOMEM;
-    }
-    cmd_ctx->cli_ctx = cli_ctx;
-    cmd_ctx->type = SSS_DP_SUDO_DEFAULTS;
-    cmd_ctx->username = NULL;
-    cmd_ctx->check_next = false;
-
-    /* get responder ctx */
-    cmd_ctx->sudo_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sudo_ctx);
-    if (!cmd_ctx->sudo_ctx) {
-        DEBUG(SSSDBG_FATAL_FAILURE, ("sudo_ctx not set, killing connection!\n"));
-        return EFAULT;
-    }
-
-    /* create domain ctx */
-    dctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
-    if (!dctx) {
-        return sudosrv_cmd_send_error(cmd_ctx, cmd_ctx, ENOMEM);
-    }
-    dctx->cmd_ctx = cmd_ctx;
-    dctx->orig_username = NULL;
-    dctx->cased_username = NULL;
-
-    DEBUG(SSSDBG_FUNC_DATA, ("Requesting cn=defaults\n"));
-
-    /* sudo currently does not support domain selection
-     * so find first available domain
-     * TODO - support domain selection */
-    dctx->domain = cli_ctx->rctx->domains;
-    while (dctx->domain && dctx->domain->fqnames) {
-        dctx->domain = dctx->domain->next;
-    }
-    if (!dctx->domain) {
-        DEBUG(SSSDBG_MINOR_FAILURE, ("No valid domain found\n"));
-        ret = ENOENT;
-        goto done;
-    }
-
-    /* try to find rules in in-memory cache */
-    ret = sudosrv_cache_lookup(cmd_ctx->sudo_ctx->cache, dctx,
-                               cmd_ctx->check_next, cmd_ctx->username,
-                               &dctx->res_count, &dctx->res);
-    if (ret == EOK) {
-        /* cache hit */
-        DEBUG(SSSDBG_FUNC_DATA, ("Returning defaults settings for [%s] "
-                                 "from in-memory cache\n", dctx->domain->name));
-    } else if (ret == ENOENT) {
-        /* cache expired or missed */
-        ret = sudosrv_get_rules(dctx);
-    } /* else error */
+    return sudosrv_cmd(SSS_DP_SUDO_USER, cli_ctx);
+}
 
-done:
-    return sudosrv_cmd_done(dctx, ret);
+static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
+{
+    return sudosrv_cmd(SSS_DP_SUDO_DEFAULTS, cli_ctx);
 }
 
 struct cli_protocol_version *register_cli_protocol_version(void)
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
index abdf3e7..cf34014 100644
--- a/src/responder/sudo/sudosrv_get_sudorules.c
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -48,7 +48,7 @@ errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx)
     }
 
     /* OK, got the user from cache. Try to get the rules. */
-    ret = sudosrv_get_rules(dctx);
+    ret = sudosrv_get_rules(dctx->cmd_ctx);
     if (ret == EAGAIN) {
         DEBUG(SSSDBG_TRACE_INTERNAL,
               ("Looking up the sudo rules from Data Provider\n"));
@@ -79,6 +79,7 @@ static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx)
     struct dp_callback_ctx *cb_ctx;
     const char *original_name = NULL;
     char *name = NULL;
+    uid_t uid = 0;
     errno_t ret;
 
     tmp_ctx = talloc_new(NULL);
@@ -186,6 +187,21 @@ static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx)
             goto done;
         }
 
+        /* check uid */
+        uid = ldb_msg_find_attr_as_int(user->msgs[0], SYSDB_UIDNUM, 0);
+        if (uid != cmd_ctx->uid) {
+            /* if a multidomain search, try with next */
+            if (cmd_ctx->check_next) {
+                dctx->check_provider = true;
+                dom = dom->next;
+                if (dom) continue;
+            }
+
+            DEBUG(SSSDBG_MINOR_FAILURE, ("UID does not match\n"));
+            ret = ENOENT;
+            goto done;
+        }
+
         /* user is stored in cache, remember cased and original name */
         original_name = ldb_msg_find_attr_as_string(user->msgs[0],
                                                     SYSDB_NAME, NULL);
@@ -195,14 +211,17 @@ static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx)
             goto done;
         }
 
-        dctx->cased_username = talloc_move(dctx, &name);
-        dctx->orig_username = talloc_strdup(dctx, original_name);
-        if (dctx->orig_username== NULL) {
+        cmd_ctx->cased_username = talloc_move(cmd_ctx, &name);
+        cmd_ctx->orig_username = talloc_strdup(cmd_ctx, original_name);
+        if (cmd_ctx->orig_username == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
             ret = ENOMEM;
             goto done;
         }
 
+        /* and set domain */
+        cmd_ctx->domain = dom;
+
         DEBUG(SSSDBG_TRACE_FUNC, ("Returning info for user [%s@%s]\n",
               cmd_ctx->username, dctx->domain->name));
         ret = EOK;
@@ -262,63 +281,72 @@ static void sudosrv_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
         DEBUG(SSSDBG_OP_FAILURE,
               ("Could not look up the user [%d]: %s\n",
               ret, strerror(ret)));
-        sudosrv_cmd_done(dctx, EIO);
+        sudosrv_cmd_done(dctx->cmd_ctx, ret);
         return;
     }
 
     DEBUG(SSSDBG_TRACE_INTERNAL, ("Looking up sudo rules..\n"));
-    ret = sudosrv_get_rules(dctx);
+    ret = sudosrv_get_rules(dctx->cmd_ctx);
     if (ret == EAGAIN) {
         goto done;
     } else if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               ("Error getting sudo rules [%d]: %s\n",
               ret, strerror(ret)));
-        sudosrv_cmd_done(dctx, EIO);
+        sudosrv_cmd_done(dctx->cmd_ctx, EIO);
         return;
     }
 
 done:
-    sudosrv_cmd_done(dctx, ret);
+    sudosrv_cmd_done(dctx->cmd_ctx, ret);
 }
 
-static errno_t sudosrv_get_sudorules_from_cache(struct sudo_dom_ctx *dctx);
+static errno_t sudosrv_get_sudorules_from_cache(struct sudo_cmd_ctx *cmd_ctx);
 static void
 sudosrv_get_sudorules_dp_callback(uint16_t err_maj, uint32_t err_min,
                                   const char *err_msg, void *ptr);
 static void
 sudosrv_dp_req_done(struct tevent_req *req);
 
-errno_t sudosrv_get_rules(struct sudo_dom_ctx *dctx)
+errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
 {
     struct tevent_req *dpreq;
-    struct sudo_cmd_ctx *cmd_ctx = dctx->cmd_ctx;
     struct dp_callback_ctx *cb_ctx = NULL;
 
-    DEBUG(SSSDBG_TRACE_FUNC, ("getting rules for %s\n",
-          cmd_ctx->username ? cmd_ctx->username : "default options"));
+    switch (cmd_ctx->type) {
+        case SSS_DP_SUDO_DEFAULTS:
+            DEBUG(SSSDBG_TRACE_FUNC, ("Retrieving default options "
+                  "for [%s] from [%s]\n", cmd_ctx->orig_username,
+                  cmd_ctx->domain->name));
+            break;
+        case SSS_DP_SUDO_USER:
+            DEBUG(SSSDBG_TRACE_FUNC, ("Retrieving rules "
+                  "for [%s] from [%s]\n", cmd_ctx->orig_username,
+                  cmd_ctx->domain->name));
+            break;
+    }
 
     dpreq = sss_dp_get_sudoers_send(cmd_ctx->cli_ctx,
                                     cmd_ctx->cli_ctx->rctx,
-                                    dctx->domain, false,
+                                    cmd_ctx->domain, false,
                                     cmd_ctx->type,
-                                    dctx->orig_username);
+                                    cmd_ctx->orig_username);
     if (dpreq == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Cannot issue DP request.\n"));
         return EIO;
     }
 
-    cb_ctx = talloc_zero(dctx, struct dp_callback_ctx);
+    cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx);
     if (!cb_ctx) {
         talloc_zfree(dpreq);
         return ENOMEM;
     }
 
     cb_ctx->callback = sudosrv_get_sudorules_dp_callback;
-    cb_ctx->ptr = dctx;
-    cb_ctx->cctx = dctx->cmd_ctx->cli_ctx;
-    cb_ctx->mem_ctx = dctx;
+    cb_ctx->ptr = cmd_ctx;
+    cb_ctx->cctx = cmd_ctx->cli_ctx;
+    cb_ctx->mem_ctx = cmd_ctx;
 
     tevent_req_set_callback(dpreq, sudosrv_dp_req_done, cb_ctx);
     return EAGAIN;
@@ -329,8 +357,7 @@ sudosrv_dp_req_done(struct tevent_req *req)
 {
     struct dp_callback_ctx *cb_ctx =
         tevent_req_callback_data(req, struct dp_callback_ctx);
-    struct sudo_dom_ctx *dctx =
-        talloc_get_type(cb_ctx->ptr, struct sudo_dom_ctx);
+    struct cli_ctx *cli_ctx = talloc_get_type(cb_ctx->cctx, struct cli_ctx);
 
     errno_t ret;
     dbus_uint16_t err_maj;
@@ -343,7 +370,7 @@ sudosrv_dp_req_done(struct tevent_req *req)
     talloc_free(req);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Fatal error, killing connection!\n"));
-        talloc_free(dctx->cmd_ctx->cli_ctx);
+        talloc_free(cli_ctx);
         return;
     }
 
@@ -354,8 +381,7 @@ static void
 sudosrv_get_sudorules_dp_callback(uint16_t err_maj, uint32_t err_min,
                                   const char *err_msg, void *ptr)
 {
-    struct sudo_dom_ctx *dctx =
-        talloc_get_type(ptr, struct sudo_dom_ctx);
+    struct sudo_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct sudo_cmd_ctx);
     errno_t ret;
 
     if (err_maj) {
@@ -364,25 +390,19 @@ sudosrv_get_sudorules_dp_callback(uint16_t err_maj, uint32_t err_min,
                "Error: %u, %u, %s\n"
                "Will try to return what we have in cache\n",
                (unsigned int)err_maj, (unsigned int)err_min, err_msg));
-
-        /* Loop to the next domain if possible */
-        if (dctx->domain->next && dctx->cmd_ctx->check_next) {
-            dctx->domain = dctx->domain->next;
-            dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
-        }
     }
 
     DEBUG(SSSDBG_TRACE_INTERNAL, ("About to get sudo rules from cache\n"));
-    ret = sudosrv_get_sudorules_from_cache(dctx);
+    ret = sudosrv_get_sudorules_from_cache(cmd_ctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               ("Failed to make a request to our cache [%d]: %s\n",
               ret, strerror(ret)));
-        sudosrv_cmd_done(dctx, EIO);
+        sudosrv_cmd_done(cmd_ctx, EIO);
         return;
     }
 
-    sudosrv_cmd_done(dctx, ret);
+    sudosrv_cmd_done(cmd_ctx, ret);
 }
 
 static errno_t sudosrv_get_sudorules_query_cache(TALLOC_CTX *mem_ctx,
@@ -394,45 +414,53 @@ static errno_t sudosrv_get_sudorules_query_cache(TALLOC_CTX *mem_ctx,
                                                  struct sysdb_attrs ***_rules,
                                                  size_t *_count);
 
-static errno_t sudosrv_get_sudorules_from_cache(struct sudo_dom_ctx *dctx)
+static errno_t sudosrv_get_sudorules_from_cache(struct sudo_cmd_ctx *cmd_ctx)
 {
-    TALLOC_CTX *tmp_ctx;
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct sudo_ctx *sudo_ctx = cmd_ctx->sudo_ctx;
+    struct sysdb_ctx *sysdb = NULL;
+    char **groups = NULL;
+    const char *debug_name = NULL;
     errno_t ret;
-    struct sysdb_ctx *sysdb;
-    struct sudo_ctx *sudo_ctx = dctx->cmd_ctx->sudo_ctx;
-    uid_t uid;
-    char **groupnames;
-    const char *safe_name = dctx->cmd_ctx->username ?
-                            dctx->cmd_ctx->username : "default rules";
+
+    if (cmd_ctx->domain == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Domain is not set!\n"));
+        return EFAULT;
+    }
 
     tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) return ENOMEM;
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
+        return ENOMEM;
+    }
 
-    sysdb = dctx->domain->sysdb;
+    sysdb = cmd_ctx->domain->sysdb;
     if (sysdb == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              ("sysdb context not found for this domain!\n"));
+        DEBUG(SSSDBG_CRIT_FAILURE, ("sysdb context not found for this domain!\n"));
         ret = EIO;
         goto done;
     }
 
-    if (dctx->cmd_ctx->type == SSS_DP_SUDO_USER) {
-        ret = sysdb_get_sudo_user_info(tmp_ctx, dctx->orig_username,
-                                       sysdb, &uid, &groupnames);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
+    switch (cmd_ctx->type) {
+    case SSS_DP_SUDO_USER:
+        debug_name = cmd_ctx->cased_username;
+        ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, sysdb,
+                                       NULL, &groups);
+       if (ret != EOK) {
+           DEBUG(SSSDBG_CRIT_FAILURE,
                  ("Unable to retrieve user info [%d]: %s\n", strerror(ret)));
-            goto done;
-        }
-    } else {
-        uid = 0;
-        groupnames = NULL;
+           goto done;
+       }
+        break;
+    case SSS_DP_SUDO_DEFAULTS:
+        debug_name = "<default options>";
+        break;
     }
 
-    ret = sudosrv_get_sudorules_query_cache(dctx, sysdb, dctx->cmd_ctx->type,
-                                            dctx->orig_username,
-                                            uid, groupnames,
-                                            &dctx->res, &dctx->res_count);
+    ret = sudosrv_get_sudorules_query_cache(cmd_ctx, sysdb, cmd_ctx->type,
+                                            cmd_ctx->orig_username,
+                                            cmd_ctx->uid, groups,
+                                            &cmd_ctx->rules, &cmd_ctx->num_rules);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
              ("Unable to retrieve sudo rules [%d]: %s\n", strerror(ret)));
@@ -440,20 +468,20 @@ static errno_t sudosrv_get_sudorules_from_cache(struct sudo_dom_ctx *dctx)
     }
 
     /* Store result in in-memory cache */
-    ret = sudosrv_cache_set_entry(sudo_ctx->rctx->ev, sudo_ctx,
-                                  sudo_ctx->cache, dctx->domain,
-                                  dctx->cased_username, dctx->res_count,
-                                  dctx->res, sudo_ctx->cache_timeout);
+    ret = sudosrv_cache_set_entry(sudo_ctx->rctx->ev, sudo_ctx, sudo_ctx->cache,
+                                  cmd_ctx->domain, cmd_ctx->cased_username,
+                                  cmd_ctx->num_rules, cmd_ctx->rules,
+                                  sudo_ctx->cache_timeout);
     if (ret != EOK) {
         DEBUG(SSSDBG_MINOR_FAILURE, ("Unable to store rules in cache for "
-              "[%s@%s]\n", safe_name, dctx->domain->name));
+              "[%s@%s]\n", debug_name, cmd_ctx->domain->name));
     } else {
         DEBUG(SSSDBG_FUNC_DATA, ("Rules for [%s@%s] stored in in-memory cache\n",
-                                 safe_name, dctx->domain->name));
+                                 debug_name, cmd_ctx->domain->name));
     }
 
     DEBUG(SSSDBG_TRACE_FUNC, ("Returning rules for [%s@%s]\n",
-          safe_name, dctx->domain->name));
+            debug_name, cmd_ctx->domain->name));
 
     ret = EOK;
 done:
@@ -602,81 +630,3 @@ sort_sudo_rules(struct sysdb_attrs **rules, size_t count)
           sudo_order_cmp_fn);
     return EOK;
 }
-
-char * sudosrv_get_sudorules_parse_query(TALLOC_CTX *mem_ctx,
-                                         const char *query_body,
-                                         int query_len)
-{
-    /* empty string or not NULL terminated */
-    if (query_len < 2 || strnlen(query_body, query_len) == query_len) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query.\n"));
-        return NULL;
-    }
-
-    return talloc_strdup(mem_ctx, query_body);
-}
-
-/*
- * Response format:
- * <error_code(uint32_t)><num_entries(uint32_t)><rule1><rule2>...
- * <ruleN> = <num_attrs(uint32_t)><attr1><attr2>...
- * <attrN>  = <name(char*)>\0<num_values(uint32_t)><value1(char*)>\0<value2(char*)>\0...
- *
- * if <error_code> is not SSS_SUDO_ERROR_OK, the rest of the data is skipped.
- */
-int sudosrv_get_sudorules_build_response(TALLOC_CTX *mem_ctx,
-                                         uint32_t error,
-                                         int rules_num,
-                                         struct sysdb_attrs **rules,
-                                         uint8_t **_response_body,
-                                         size_t *_response_len)
-{
-    uint8_t *response_body = NULL;
-    size_t response_len = 0;
-    TALLOC_CTX *tmp_ctx = NULL;
-    int i = 0;
-    int ret = EOK;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
-        return ENOMEM;
-    }
-
-    /* error code */
-    ret = sudosrv_response_append_uint32(tmp_ctx, error,
-                                         &response_body, &response_len);
-    if (ret != EOK) {
-        goto fail;
-    }
-
-    if (error != SSS_SUDO_ERROR_OK) {
-        goto done;
-    }
-
-    /* rules count */
-    ret = sudosrv_response_append_uint32(tmp_ctx, (uint32_t)rules_num,
-                                         &response_body, &response_len);
-    if (ret != EOK) {
-        goto fail;
-    }
-
-    /* rules */
-    for (i = 0; i < rules_num; i++) {
-        ret = sudosrv_response_append_rule(tmp_ctx, rules[i]->num, rules[i]->a,
-                                           &response_body, &response_len);
-        if (ret != EOK) {
-            goto fail;
-        }
-    }
-
-done:
-    *_response_body = talloc_steal(mem_ctx, response_body);
-    *_response_len = response_len;
-
-    ret = EOK;
-
-fail:
-    talloc_free(tmp_ctx);
-    return ret;
-}
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
index fd96ba9..22b4b62 100644
--- a/src/responder/sudo/sudosrv_private.h
+++ b/src/responder/sudo/sudosrv_private.h
@@ -23,6 +23,7 @@
 
 #include <stdint.h>
 #include <talloc.h>
+#include <sys/types.h>
 
 #include "src/db/sysdb.h"
 #include "responder/common/responder.h"
@@ -57,20 +58,25 @@ struct sudo_cmd_ctx {
     struct cli_ctx *cli_ctx;
     struct sudo_ctx *sudo_ctx;
     enum sss_dp_sudo_type type;
+
+    /* input data */
+    uid_t uid;
     char *username;
+    const char *orig_username;
+    const char *cased_username;
+    struct sss_domain_info *domain;
     bool check_next;
+
+    /* output data */
+    struct sysdb_attrs **rules;
+    size_t num_rules;
 };
 
 struct sudo_dom_ctx {
     struct sudo_cmd_ctx *cmd_ctx;
     struct sss_domain_info *domain;
+    bool check_next;
     bool check_provider;
-    const char *orig_username;
-    const char *cased_username;
-
-    /* cache results */
-    struct sysdb_attrs **res;
-    size_t res_count;
 };
 
 struct sudo_dp_request {
@@ -80,46 +86,28 @@ struct sudo_dp_request {
 
 struct sss_cmd_table *get_sudo_cmds(void);
 
-errno_t sudosrv_cmd_done(struct sudo_dom_ctx *dctx, int ret);
+errno_t sudosrv_cmd_done(struct sudo_cmd_ctx *cmd_ctx, int ret);
 
 errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx);
 
-errno_t sudosrv_get_rules(struct sudo_dom_ctx *dctx);
-
-char * sudosrv_get_sudorules_parse_query(TALLOC_CTX *mem_ctx,
-                                         const char *query_body,
-                                         int query_len);
-
-int sudosrv_get_sudorules_build_response(TALLOC_CTX *mem_ctx,
-                                         uint32_t error,
-                                         int rules_num,
-                                         struct sysdb_attrs **rules,
-                                         uint8_t **_response_body,
-                                         size_t *_response_len);
-
-int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
-                                   const char *str,
-                                   size_t str_len,
-                                   uint8_t **_response_body,
-                                   size_t *_response_len);
-
-int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
-                                   uint32_t number,
-                                   uint8_t **_response_body,
-                                   size_t *_response_len);
-
-int sudosrv_response_append_rule(TALLOC_CTX *mem_ctx,
-                                 int attrs_num,
-                                 struct ldb_message_element *attrs,
-                                 uint8_t **_response_body,
-                                 size_t *_response_len);
-
-int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
-                                 const char *name,
-                                 unsigned int values_num,
-                                 struct ldb_val *values,
-                                 uint8_t **_response_body,
-                                 size_t *_response_len);
+errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx);
+
+int sudosrv_build_response(TALLOC_CTX *mem_ctx,
+                           uint32_t error,
+                           const char *domain,
+                           int rules_num,
+                           struct sysdb_attrs **rules,
+                           uint8_t **_response_body,
+                           size_t *_response_len);
+
+errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domains,
+                            struct sss_names_ctx *names_ctx,
+                            uint8_t *query_body,
+                            size_t query_len,
+                            uid_t *uid,
+                            char **_username,
+                            struct sss_domain_info **_domain);
 
 struct tevent_req *
 sss_dp_get_sudoers_send(TALLOC_CTX *mem_ctx,
diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c
index 8b98da6..f946bf7 100644
--- a/src/responder/sudo/sudosrv_query.c
+++ b/src/responder/sudo/sudosrv_query.c
@@ -26,11 +26,11 @@
 #include "util/util.h"
 #include "responder/sudo/sudosrv_private.h"
 
-int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
-                                   const char *str,
-                                   size_t str_len,
-                                   uint8_t **_response_body,
-                                   size_t *_response_len)
+static int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
+                                          const char *str,
+                                          size_t str_len,
+                                          uint8_t **_response_body,
+                                          size_t *_response_len)
 {
     size_t response_len = *_response_len;
     uint8_t *response_body = *_response_body;
@@ -50,10 +50,10 @@ int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
-int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
-                                   uint32_t number,
-                                   uint8_t **_response_body,
-                                   size_t *_response_len)
+static int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
+                                          uint32_t number,
+                                          uint8_t **_response_body,
+                                          size_t *_response_len)
 {
     size_t response_len = *_response_len;
     uint8_t *response_body = *_response_body;
@@ -72,11 +72,71 @@ int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
-int sudosrv_response_append_rule(TALLOC_CTX *mem_ctx,
-                                 int attrs_num,
-                                 struct ldb_message_element *attrs,
-                                 uint8_t **_response_body,
-                                 size_t *_response_len)
+static int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
+                                        const char *name,
+                                        unsigned int values_num,
+                                        struct ldb_val *values,
+                                        uint8_t **_response_body,
+                                        size_t *_response_len)
+{
+    uint8_t *response_body = *_response_body;
+    size_t response_len = *_response_len;
+    TALLOC_CTX *tmp_ctx = NULL;
+    int i = 0;
+    int ret = EOK;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
+        return ENOMEM;
+    }
+
+    /* attr name */
+    ret = sudosrv_response_append_string(tmp_ctx, name, strlen(name) + 1,
+                                         &response_body, &response_len);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* values count */
+    ret = sudosrv_response_append_uint32(tmp_ctx, values_num,
+                                         &response_body, &response_len);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* values */
+    for (i = 0; i < values_num; i++) {
+        if (strlen((char*)(values[i].data)) != values[i].length) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("value is not a string"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        ret = sudosrv_response_append_string(tmp_ctx,
+                                             (const char*)values[i].data,
+                                             values[i].length + 1,
+                                             &response_body, &response_len);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    *_response_body = talloc_steal(mem_ctx, response_body);
+    *_response_len = response_len;
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static int sudosrv_response_append_rule(TALLOC_CTX *mem_ctx,
+                                        int attrs_num,
+                                        struct ldb_message_element *attrs,
+                                        uint8_t **_response_body,
+                                        size_t *_response_len)
 {
     uint8_t *response_body = *_response_body;
     size_t response_len = *_response_len;
@@ -117,15 +177,24 @@ done:
     return ret;
 }
 
-int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
-                                 const char *name,
-                                 unsigned int values_num,
-                                 struct ldb_val *values,
-                                 uint8_t **_response_body,
-                                 size_t *_response_len)
+/*
+ * Response format:
+ * <error_code(uint32_t)><domain(char*)>\0<num_entries(uint32_t)><rule1><rule2>...
+ * <ruleN> = <num_attrs(uint32_t)><attr1><attr2>...
+ * <attrN>  = <name(char*)>\0<num_values(uint32_t)><value1(char*)>\0<value2(char*)>\0...
+ *
+ * if <error_code> is not SSS_SUDO_ERROR_OK, the rest of the data is skipped.
+ */
+int sudosrv_build_response(TALLOC_CTX *mem_ctx,
+                           uint32_t error,
+                           const char *domain,
+                           int rules_num,
+                           struct sysdb_attrs **rules,
+                           uint8_t **_response_body,
+                           size_t *_response_len)
 {
-    uint8_t *response_body = *_response_body;
-    size_t response_len = *_response_len;
+    uint8_t *response_body = NULL;
+    size_t response_len = 0;
     TALLOC_CTX *tmp_ctx = NULL;
     int i = 0;
     int ret = EOK;
@@ -136,42 +205,126 @@ int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    /* attr name */
-    ret = sudosrv_response_append_string(tmp_ctx, name, strlen(name) + 1,
+    /* error code */
+    ret = sudosrv_response_append_uint32(tmp_ctx, error,
                                          &response_body, &response_len);
     if (ret != EOK) {
+        goto fail;
+    }
+
+    if (error != SSS_SUDO_ERROR_OK) {
         goto done;
     }
 
-    /* values count */
-    ret = sudosrv_response_append_uint32(tmp_ctx, values_num,
+    /* domain name */
+    ret = sudosrv_response_append_string(tmp_ctx, domain, strlen(domain) + 1,
                                          &response_body, &response_len);
     if (ret != EOK) {
-        goto done;
+        goto fail;
     }
 
-    /* values */
-    for (i = 0; i < values_num; i++) {
-        if (strlen((char*)(values[i].data)) != values[i].length) {
-            DEBUG(SSSDBG_CRIT_FAILURE, ("value is not a string"));
-            ret = EINVAL;
-            goto done;
-        }
+    /* rules count */
+    ret = sudosrv_response_append_uint32(tmp_ctx, (uint32_t)rules_num,
+                                         &response_body, &response_len);
+    if (ret != EOK) {
+        goto fail;
+    }
 
-        ret = sudosrv_response_append_string(tmp_ctx,
-                                             (const char*)values[i].data,
-                                             values[i].length + 1,
-                                             &response_body, &response_len);
+    /* rules */
+    for (i = 0; i < rules_num; i++) {
+        ret = sudosrv_response_append_rule(tmp_ctx, rules[i]->num, rules[i]->a,
+                                           &response_body, &response_len);
         if (ret != EOK) {
-            goto done;
+            goto fail;
         }
     }
 
+done:
     *_response_body = talloc_steal(mem_ctx, response_body);
     *_response_len = response_len;
 
     ret = EOK;
 
+fail:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+/*
+ * Query format:
+ * <uid_t><username[@domain]>
+ */
+errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domains,
+                            struct sss_names_ctx *names_ctx,
+                            uint8_t *query_body,
+                            size_t query_len,
+                            uid_t *uid,
+                            char **_username,
+                            struct sss_domain_info **_domain)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct sss_domain_info *domain = NULL;
+    size_t offset = 0;
+    size_t rawname_len = 0;
+    char *rawname = NULL;
+    char *domainname = NULL;
+    char *username = NULL;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
+        return ENOMEM;
+    }
+
+    /* uid */
+
+    if (query_len < sizeof(uid_t)) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Query is too small\n"));
+        ret = EINVAL;
+        goto done;
+    }
+    safealign_memcpy(uid, query_body, sizeof(uid_t), &offset);
+
+    /* username[@domain] */
+
+    rawname = (char*)(query_body + offset);
+    rawname_len = query_len - offset;
+
+    if (rawname_len < 2) { /* at least one character and zero */
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Query does not contain username\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (!sss_utf8_check((uint8_t*)rawname, rawname_len - 1)) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Supplied data is not valid UTF-8 string\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* parse username */
+
+    ret = sss_parse_name(tmp_ctx, names_ctx, rawname, &domainname, &username);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid name received [%s]\n", rawname));
+        goto done;
+    }
+
+    if (domainname != NULL) {
+        domain = responder_get_domain(domains, domainname);
+        if (domain == NULL) {
+            ret = ENOENT;
+            goto done;
+        }
+    }
+
+    *_username = talloc_steal(mem_ctx, username);
+    *_domain = domain; /* do not steal on mem_ctx */
+
+    ret = EOK;
+
 done:
     talloc_free(tmp_ctx);
     return ret;
diff --git a/src/sss_client/sudo/sss_sudo.c b/src/sss_client/sudo/sss_sudo.c
index cb3c746..de83d0b 100644
--- a/src/sss_client/sudo/sss_sudo.c
+++ b/src/sss_client/sudo/sss_sudo.c
@@ -18,7 +18,10 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#define _GNU_SOURCE /* asprintf */
+
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <errno.h>
 
@@ -26,9 +29,10 @@
 #include "sss_client/sudo/sss_sudo.h"
 #include "sss_client/sudo/sss_sudo_private.h"
 
-static int sss_sudo_create_query(const char *username,
-                                 char **_query,
-                                 int *_query_len);
+int sss_sudo_create_query(uid_t uid,
+                          const char *username,
+                          uint8_t **_query,
+                          size_t *_query_len);
 
 static void sss_sudo_free_rules(unsigned int num_rules,
                                 struct sss_sudo_rule *rules);
@@ -37,19 +41,34 @@ static void sss_sudo_free_attrs(unsigned int num_attrs,
                                 struct sss_sudo_attr *attrs);
 
 static int sss_sudo_send_recv_generic(enum sss_cli_command command,
-                                      struct sss_cli_req_data *request,
+                                      uid_t uid,
+                                      const char *username,
                                       uint32_t *_error,
+                                      char **_domainname,
                                       struct sss_sudo_result **_result)
 {
+    struct sss_cli_req_data request;
+    uint8_t *query_buf = NULL;
+    size_t query_len = 0;
     uint8_t *reply_buf = NULL;
     size_t reply_len = 0;
     int errnop = 0;
     int ret = 0;
 
+    /* create query */
+
+    ret = sss_sudo_create_query(uid, username, &query_buf, &query_len);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    request.len = query_len;
+    request.data = (const void*)query_buf;
+
     /* send query and receive response */
 
     errnop = 0;
-    ret = sss_sudo_make_request(command, request,
+    ret = sss_sudo_make_request(command, &request,
                                 &reply_buf, &reply_len, &errnop);
     if (errnop != EOK) {
         return errnop;
@@ -58,68 +77,75 @@ static int sss_sudo_send_recv_generic(enum sss_cli_command command,
     /* parse structure */
 
     ret = sss_sudo_parse_response((const char*)reply_buf, reply_len,
-                                  _result, _error);
+                                  _domainname, _result, _error);
 
+done:
+    free(query_buf);
     free(reply_buf);
     return ret;
 }
 
-int sss_sudo_send_recv(const char *username,
+int sss_sudo_send_recv(uid_t uid,
+                       const char *username,
+                       const char *domainname,
                        uint32_t *_error,
                        struct sss_sudo_result **_result)
 {
-    struct sss_cli_req_data request;
-    char *query = NULL;
-    int query_len = 0;
-    int ret = 0;
-
-    /* create query */
+    char *fullname = NULL;
+    int ret;
 
-    ret = sss_sudo_create_query(username, &query, &query_len);
-    if (ret != EOK) {
-        goto done;
+    if (username == NULL || strlen(username) == 0) {
+        return EINVAL;
     }
 
-    request.len = query_len;
-    request.data = (const void*)query;
-
-    /* send query and recieve response */
+    if (domainname != NULL) {
+        ret = asprintf(&fullname, "%s@%s", username, domainname);
+        if (ret == -1) {
+            return ENOMEM;
+        }
+    } else {
+        fullname = strdup(username);
+        if (fullname == NULL) {
+            return ENOMEM;
+        }
+    }
 
-    ret = sss_sudo_send_recv_generic(SSS_SUDO_GET_SUDORULES, &request,
-                                     _error, _result);
+    /* send query and receive response */
 
-done:
-    free(query);
+    ret = sss_sudo_send_recv_generic(SSS_SUDO_GET_SUDORULES, uid, fullname,
+                                     _error, NULL, _result);
+    free(fullname);
     return ret;
 }
 
-int sss_sudo_send_recv_defaults(uint32_t *_error,
+int sss_sudo_send_recv_defaults(uid_t uid,
+                                const char *username,
+                                uint32_t *_error,
+                                char **_domainname,
                                 struct sss_sudo_result **_result)
 {
-    struct sss_cli_req_data request;
-
-    request.len = 0;
-    request.data = (const void*)NULL;
+    if (username == NULL || strlen(username) == 0) {
+        return EINVAL;
+    }
 
-    return sss_sudo_send_recv_generic(SSS_SUDO_GET_DEFAULTS, &request,
-                                      _error, _result);
+    return sss_sudo_send_recv_generic(SSS_SUDO_GET_DEFAULTS, uid, username,
+                                      _error, _domainname, _result);
 }
 
-int sss_sudo_create_query(const char *username, char **_query, int *_query_len)
+int sss_sudo_create_query(uid_t uid, const char *username,
+                          uint8_t **_query, size_t *_query_len)
 {
-    char *data = NULL;
-    int data_len = strlen(username) + 1;
-
-    if (data_len <= 1) {
-        return EINVAL;
-    }
+    uint8_t *data = NULL;
+    size_t username_len = strlen(username) * sizeof(char) + 1;
+    size_t data_len = sizeof(uid_t) + username_len;
 
-    data = (char*)malloc(data_len * sizeof(char));
+    data = (uint8_t*)malloc(data_len * sizeof(uint8_t));
     if (data == NULL) {
         return ENOMEM;
     }
 
-    memcpy(data, username, data_len);
+    memcpy(data, &uid, sizeof(uid_t));
+    memcpy(data + sizeof(uid_t), username, username_len);
 
     *_query = data;
     *_query_len = data_len;
diff --git a/src/sss_client/sudo/sss_sudo.h b/src/sss_client/sudo/sss_sudo.h
index fe01e65..1a275cf 100644
--- a/src/sss_client/sudo/sss_sudo.h
+++ b/src/sss_client/sudo/sss_sudo.h
@@ -30,6 +30,7 @@
  */
 
 #include <stdint.h>
+#include <sys/types.h>
 
 /** The value returned when the communication with SUDO is successful and
  *  the user was found in one of the domains
@@ -89,7 +90,9 @@ struct sss_sudo_result {
  * @brief Send a request to SSSD to retreive all SUDO rules for a given
  * user.
  *
+ * @param[in] uid         The uid of the user to retreive the rules for.
  * @param[in] username    The username to retreive the rules for
+ * @param[in] domainname  The domain name the user is a member of.
  * @param[out] _error     The result of the search in SSSD's domains. If the
  *                        user was present in the domain, the _error code is
  *                        SSS_SUDO_ERROR_OK and the _result structure is
@@ -109,7 +112,9 @@ struct sss_sudo_result {
  *         tell whether the result contains any rules or whether SSSD knew the
  *         user at all. That information is transferred in the _error parameter.
  */
-int sss_sudo_send_recv(const char *username,
+int sss_sudo_send_recv(uid_t uid,
+                       const char *username,
+                       const char *domainname,
                        uint32_t *_error,
                        struct sss_sudo_result **_result);
 
@@ -117,24 +122,35 @@ int sss_sudo_send_recv(const char *username,
  * @brief Send a request to SSSD to retrieve the default options, commonly
  * stored in the "cn=defaults" record,
  *
- * @param[out] _error     The result of the search in SSSD's domains. If the
- *                        options were present in the domain, the _error code
- *                        is SSS_SUDO_ERROR_OK and the _result structure is
- *                        returned even if it was empty (in other words
- *                        _result->num_rules == 0). Other problems are returned
- *                        as errno codes.
+ * @param[in] uid          The uid of the user to retreive the rules for.
  *
- * @param[out] _result    Newly allocated structure sss_result that contains
- *                        the options. If no options were found this structure
- *                        is "empty", which means that the num_rules member
- *                        is 0.
+ * @param[in] username     The username to retreive the rules for.
+ *
+ * @param[out] _error      The result of the search in SSSD's domains. If the
+ *                         options were present in the domain, the _error code
+ *                         is SSS_SUDO_ERROR_OK and the _result structure is
+ *                         returned even if it was empty (in other words
+ *                         _result->num_rules == 0). Other problems are returned
+ *                         as errno codes.
+ *
+ * @param[out] _domainname The domain name the user is a member of.
+ *
+ * @param[out] _result     Newly allocated structure sss_result that contains
+ *                         the options. If no options were found this structure
+ *                         is "empty", which means that the num_rules member
+ *                         is 0.
  *
  * @return 0 on success and other errno values on failure. The return value
  *         denotes whether communication with SSSD was successful. It does not
  *         tell whether the result contains any rules or whether SSSD knew the
  *         user at all. That information is transferred in the _error parameter.
+ *
+ * @note   The _domainname should be freed using free().
  */
-int sss_sudo_send_recv_defaults(uint32_t *_error,
+int sss_sudo_send_recv_defaults(uid_t uid,
+                                const char *username,
+                                uint32_t *_error,
+                                char **_domainname,
                                 struct sss_sudo_result **_result);
 
 /**
diff --git a/src/sss_client/sudo/sss_sudo_private.h b/src/sss_client/sudo/sss_sudo_private.h
index 674bf12..2827a94 100644
--- a/src/sss_client/sudo/sss_sudo_private.h
+++ b/src/sss_client/sudo/sss_sudo_private.h
@@ -26,6 +26,7 @@
 
 int sss_sudo_parse_response(const char *message,
                             size_t message_len,
+                            char **_domainname,
                             struct sss_sudo_result **_result,
                             uint32_t *_error);
 
diff --git a/src/sss_client/sudo/sss_sudo_response.c b/src/sss_client/sudo/sss_sudo_response.c
index 4e74d83..471d42e 100644
--- a/src/sss_client/sudo/sss_sudo_response.c
+++ b/src/sss_client/sudo/sss_sudo_response.c
@@ -51,10 +51,12 @@ static int sss_sudo_parse_string(const char *message,
 
 int sss_sudo_parse_response(const char *message,
                             size_t message_len,
+                            char **_domainname,
                             struct sss_sudo_result **_result,
                             uint32_t *_error)
 {
     struct sss_sudo_result *result = NULL;
+    char *domainname = NULL;
     size_t cursor = 0;
     int ret = EOK;
     int i = 0;
@@ -65,6 +67,16 @@ int sss_sudo_parse_response(const char *message,
         return ret;
     }
 
+    /* domain name */
+    ret = sss_sudo_parse_string(message, message_len, &cursor, &domainname);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    if (_domainname != NULL) {
+        *_domainname = domainname;
+    }
+
     /* result */
     result = malloc(sizeof(struct sss_sudo_result));
     if (result == NULL) {
diff --git a/src/sss_client/sudo_testcli/sudo_testcli.c b/src/sss_client/sudo_testcli/sudo_testcli.c
index b498ace..d86fb75 100644
--- a/src/sss_client/sudo_testcli/sudo_testcli.c
+++ b/src/sss_client/sudo_testcli/sudo_testcli.c
@@ -23,6 +23,8 @@
 #include <errno.h>
 #include <string.h>
 #include <talloc.h>
+#include <sys/types.h>
+#include <pwd.h>
 
 #include "sss_client/sss_cli.h"
 #include "sss_client/sudo/sss_sudo.h"
@@ -38,40 +40,74 @@ int main(int argc, char **argv)
 {
     int ret = 0;
     struct sss_sudo_result *result = NULL;
+    struct passwd *passwd = NULL;
+    const char *username = NULL;
+    char *domainname = NULL;
+    uid_t uid = 0;
     uint32_t error = 0;
 
-    if (argc > 2) {
-        fprintf(stderr, "Usage: sss_sudo_cli username\n");
+    if (argc > 3) {
+        fprintf(stderr, "Usage: sss_sudo_cli username [uid]\n");
         goto fail;
     }
 
-    /* get sss_result - it will send new query to responder */
-
-    if (argc == 1) {
-        ret = sss_sudo_send_recv_defaults(&error, &result);
-        if (ret != EOK) {
-            fprintf(stderr, "sss_sudo_send_recv_defaults() failed: %s\n", strerror(ret));
-            goto fail;
-        }
+    username = argv[1];
+    if (argc == 3) {
+        uid = atoi(argv[2]);
     } else {
-        ret = sss_sudo_send_recv(argv[1], &error, &result);
-        if (ret != EOK) {
-            fprintf(stderr, "sss_sudo_send_recv() failed: %s\n", strerror(ret));
+        passwd = getpwnam(username);
+        if (passwd == NULL) {
+            fprintf(stderr, "Unknown user\n");
             goto fail;
         }
+        uid = passwd->pw_uid;
+    }
+
+    /* get sss_result - it will send new query to responder */
+
+    /* get default options */
+
+    ret = sss_sudo_send_recv_defaults(uid, username, &error,
+                                      &domainname, &result);
+    if (ret != EOK) {
+        fprintf(stderr, "sss_sudo_send_recv_defaults() failed: %s\n",
+                strerror(ret));
+        goto fail;
+    }
+
+    printf("User [%s:%llu] found in domain: %s\n\n",
+            username, (unsigned long long)uid,
+            domainname != NULL ? domainname : "<NULL>");
+
+    printf("=== Printing response data [default options] ===\n");
+    printf("Response code: %d\n\n", error);
+    if (error == SSS_SUDO_ERROR_OK) {
+        print_sss_result(result);
+    }
+
+    sss_sudo_free_result(result);
+
+    /* get rules */
+
+    ret = sss_sudo_send_recv(uid, username, domainname, &error, &result);
+    if (ret != EOK) {
+        fprintf(stderr, "sss_sudo_send_recv() failed: %s\n", strerror(ret));
+        goto fail;
     }
 
-    printf("=== Printing response data ===\n");
+    printf("\n=== Printing response data [rules] ===\n");
     printf("Response code: %d\n\n", error);
     if (error == SSS_SUDO_ERROR_OK) {
         print_sss_result(result);
     }
 
 
+    free(domainname);
     sss_sudo_free_result(result);
     return 0;
 
 fail:
+    free(domainname);
     sss_sudo_free_result(result);
     return 1;
 }
-- 
1.7.6.5

