From 15ad60d83ee37b77adf465250708acf04092b9a9 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 5/6] sudo api: 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/responder/sudo/sudosrv_cmd.c           |  133 ++++++++++------------------
 src/responder/sudo/sudosrv_get_sudorules.c |   48 ++++++----
 src/responder/sudo/sudosrv_private.h       |    3 +
 src/responder/sudo/sudosrv_query.c         |   24 +++++-
 src/sss_client/sudo/sss_sudo.c             |  107 ++++++++++++++--------
 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 +++++++++++---
 9 files changed, 259 insertions(+), 173 deletions(-)

diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
index 6ef871f..0a796e2 100644
--- a/src/responder/sudo/sudosrv_cmd.c
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -84,7 +84,7 @@ static errno_t sudosrv_cmd_send_error(TALLOC_CTX *mem_ctx,
         return EFAULT;
     }
 
-    ret = sudosrv_build_response(mem_ctx, error, 0, NULL,
+    ret = sudosrv_build_response(mem_ctx, error, NULL, 0, NULL,
                                  &response_body, &response_len);
     if (ret != EOK) {
         return ret;
@@ -117,7 +117,7 @@ errno_t sudosrv_cmd_done(struct sudo_cmd_ctx *cmd_ctx, int ret)
 
         /* send result */
         ret = sudosrv_build_response(cmd_ctx, SSS_SUDO_ERROR_OK,
-                                     num_rules, rules,
+                                     cmd_ctx->domain->name, num_rules, rules,
                                      &response_body, &response_len);
         if (ret != EOK) {
             return EFAULT;
@@ -158,10 +158,12 @@ errno_t sudosrv_cmd_done(struct sudo_cmd_ctx *cmd_ctx, int ret)
 static int sudosrv_cmd(enum sss_dp_sudo_type type, struct cli_ctx *cli_ctx)
 {
     struct sudo_cmd_ctx *cmd_ctx = NULL;
-    struct sudo_dom_ctx *dctx = NULL;
+    struct sudo_dom_ctx *dom_ctx = NULL;
     uint8_t *query_body = NULL;
     size_t query_len = 0;
-    errno_t ret = EOK;
+    errno_t ret;
+
+    /* create cmd_ctx */
 
     cmd_ctx = talloc_zero(cli_ctx, struct sudo_cmd_ctx);
     if (cmd_ctx == NULL) {
@@ -169,109 +171,66 @@ static int sudosrv_cmd(enum sss_dp_sudo_type type, struct cli_ctx *cli_ctx)
         DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?\n"));
         return ENOMEM;
     }
+
     cmd_ctx->domain = NULL;
     cmd_ctx->cli_ctx = cli_ctx;
     cmd_ctx->type = type;
-    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) {
+    if (cmd_ctx->sudo_ctx == NULL) {
         DEBUG(SSSDBG_FATAL_FAILURE, ("sudo_ctx not set, killing connection!\n"));
-        ret = EFAULT;
-        goto done;
+        return EFAULT;
     }
 
-    /* create domain ctx */
-    dctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
-    if (dctx == NULL) {
-        ret = ENOMEM;
+    /* parse 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"));
+        ret = EINVAL;
         goto done;
     }
-    dctx->cmd_ctx = cmd_ctx;
-
-    switch (cmd_ctx->type) {
-    case SSS_DP_SUDO_USER:
-        /* 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"));
-            ret = EINVAL;
-            goto done;
-        }
-
-        ret = sudosrv_parse_query(cmd_ctx, cli_ctx->rctx,
-                                  cli_ctx->rctx->names, query_body, query_len,
-                                  &cmd_ctx->username, &cmd_ctx->domain);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query: %s\n", strerror(ret)));
-            goto done;
-        }
-
-        DEBUG(SSSDBG_FUNC_DATA, ("Requesting sudo rules for [%s] from [%s]\n",
-              cmd_ctx->username, cmd_ctx->domain ? cmd_ctx->domain->name : "<ALL>"));
 
-        if (cmd_ctx->domain == NULL) {
-            /* this is a multidomain search */
-            dctx->domain = cli_ctx->rctx->domains;
-            cmd_ctx->check_next = true;
-        } else {
-            dctx->domain = cmd_ctx->domain;
-            cmd_ctx->check_next = false;
-        }
-
-        /* 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,
-                                   &cmd_ctx->num_rules, &cmd_ctx->rules);
-        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 */
+    ret = sudosrv_parse_query(cmd_ctx, cli_ctx->rctx,
+                              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;
+    }
 
-        break;
-    case SSS_DP_SUDO_DEFAULTS:
-        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;
-        }
+    cmd_ctx->check_next = cmd_ctx->domain == NULL;
 
-        ret = sudosrv_cache_lookup(cmd_ctx->sudo_ctx->cache, dctx,
-                                   cmd_ctx->check_next, cmd_ctx->username,
-                                   &cmd_ctx->num_rules, &cmd_ctx->rules);
+    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;
+    }
 
-        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 */
-            cmd_ctx->domain = dctx->domain;
-            ret = sudosrv_get_rules(cmd_ctx);
-        } /* else error */
+    /* create domain ctx */
 
-        break;
+    dom_ctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
+    if (dom_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
     }
+    dom_ctx->cmd_ctx = cmd_ctx;
+    dom_ctx->domain = cmd_ctx->domain != NULL ? cmd_ctx->domain
+                                              : cli_ctx->rctx->domains;
+
+    ret = sudosrv_get_sudorules(dom_ctx);
 
 done:
     return sudosrv_cmd_done(cmd_ctx, ret);
 }
 
+
 static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
 {
     return sudosrv_cmd(SSS_DP_SUDO_USER, cli_ctx);
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
index c392027..2b90fb5 100644
--- a/src/responder/sudo/sudosrv_get_sudorules.c
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -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);
@@ -265,7 +281,7 @@ 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->cmd_ctx, EIO);
+        sudosrv_cmd_done(dctx->cmd_ctx, ret);
         return;
     }
 
@@ -297,8 +313,18 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
     struct tevent_req *dpreq;
     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,
@@ -393,7 +419,6 @@ static errno_t sudosrv_get_sudorules_from_cache(struct sudo_cmd_ctx *cmd_ctx)
     TALLOC_CTX *tmp_ctx;
     errno_t ret;
     struct sysdb_ctx *sysdb;
-    struct sudo_ctx *sudo_ctx = cmd_ctx->sudo_ctx;
     uid_t uid = 0;
     char **groupnames = NULL;
     const char *debug_name = NULL;
@@ -421,7 +446,7 @@ static errno_t sudosrv_get_sudorules_from_cache(struct sudo_cmd_ctx *cmd_ctx)
     case SSS_DP_SUDO_USER:
         debug_name = cmd_ctx->cased_username;
         ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, sysdb,
-                                       &uid, &groupnames);
+                                       NULL, &groupnames);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                  ("Unable to retrieve user info [%d]: %s\n", strerror(ret)));
@@ -443,19 +468,6 @@ static errno_t sudosrv_get_sudorules_from_cache(struct sudo_cmd_ctx *cmd_ctx)
         goto done;
     }
 
-    /* Store result in in-memory cache */
-    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", debug_name, cmd_ctx->domain->name));
-    } else {
-        DEBUG(SSSDBG_FUNC_DATA, ("Rules for [%s@%s] stored in in-memory cache\n",
-                                 debug_name, cmd_ctx->domain->name));
-    }
-
     DEBUG(SSSDBG_TRACE_FUNC, ("Returning rules for [%s@%s]\n",
           debug_name, cmd_ctx->domain->name));
 
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
index 45de0f1..97dd837 100644
--- a/src/responder/sudo/sudosrv_private.h
+++ b/src/responder/sudo/sudosrv_private.h
@@ -60,6 +60,7 @@ struct sudo_cmd_ctx {
     enum sss_dp_sudo_type type;
 
     /* input data */
+    uid_t uid;
     char *username;
     const char *orig_username;
     const char *cased_username;
@@ -95,11 +96,13 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
                             struct sss_names_ctx *names_ctx,
                             uint8_t *query_body,
                             size_t query_len,
+                            uid_t *_uid,
                             char **_username,
                             struct sss_domain_info **_domain);
 
 errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
                                uint32_t error,
+                               const char *domain,
                                int rules_num,
                                struct sysdb_attrs **rules,
                                uint8_t **_response_body,
diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c
index 2dbd9e0..0d2a9d5 100644
--- a/src/responder/sudo/sudosrv_query.c
+++ b/src/responder/sudo/sudosrv_query.c
@@ -179,7 +179,7 @@ done:
 
 /*
  * Response format:
- * <error_code(uint32_t)><num_entries(uint32_t)><rule1><rule2>...
+ * <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...
  *
@@ -187,6 +187,7 @@ done:
  */
 errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
                                uint32_t error,
+                               const char *domain,
                                int rules_num,
                                struct sysdb_attrs **rules,
                                uint8_t **_response_body,
@@ -215,6 +216,13 @@ errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    /* domain name */
+    ret = sudosrv_response_append_string(tmp_ctx, domain, strlen(domain) + 1,
+                                         &response_body, &response_len);
+    if (ret != EOK) {
+        goto fail;
+    }
+
     /* rules count */
     ret = sudosrv_response_append_uint32(tmp_ctx, (uint32_t)rules_num,
                                          &response_body, &response_len);
@@ -244,13 +252,14 @@ fail:
 
 /*
  * Query format:
- * <username[@domain]>
+ * <uid><username[@domain]>
  */
 errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
                             struct resp_ctx *rctx,
                             struct sss_names_ctx *names_ctx,
                             uint8_t *query_body,
                             size_t query_len,
+                            uid_t *_uid,
                             char **_username,
                             struct sss_domain_info **_domain)
 {
@@ -261,6 +270,7 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
     char *rawname = NULL;
     char *domainname = NULL;
     char *username = NULL;
+    uid_t uid;
     errno_t ret;
 
     tmp_ctx = talloc_new(NULL);
@@ -269,6 +279,15 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
         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);
@@ -310,6 +329,7 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
         }
     }
 
+    *_uid = uid;
     *_username = talloc_steal(mem_ctx, username);
     *_domain = domain; /* do not steal on mem_ctx */
 
diff --git a/src/sss_client/sudo/sss_sudo.c b/src/sss_client/sudo/sss_sudo.c
index 378df98..e2bb3e0 100644
--- a/src/sss_client/sudo/sss_sudo.c
+++ b/src/sss_client/sudo/sss_sudo.c
@@ -18,17 +18,22 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "config.h"
+
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <errno.h>
 
+#include "util/util.h"
 #include "sss_client/sss_cli.h"
 #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 +42,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 (ret != SSS_STATUS_SUCCESS) {
         ret = errnop;
@@ -59,69 +79,76 @@ 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;
+    char *fullname = NULL;
+    int ret;
 
-    /* create query */
-
-    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;
+    size_t offset = 0;
 
-    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);
+    SAFEALIGN_SET_VALUE(data, uid, uid_t, &offset);
+    memcpy(data + offset, 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

