From 0d9b3b1ed8e3306480d183ad7fca719d94bf3b95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 30 Oct 2012 12:01:51 +0100
Subject: [PATCH 3/6] sudo: support users from subdomains

https://fedorahosted.org/sssd/ticket/1616
---
 src/responder/sudo/sudosrv_cmd.c           |  41 ++++++--
 src/responder/sudo/sudosrv_get_sudorules.c |  64 +++++++++---
 src/responder/sudo/sudosrv_private.h       |  17 ++--
 src/responder/sudo/sudosrv_query.c         | 152 ++++++++++++++++++++++-------
 4 files changed, 214 insertions(+), 60 deletions(-)

diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
index 9a87ed7d768b0bf35b1d1980895d08e4cf08d5f7..440c8c5da4a56105c1cb83fb6a7e8a79dd0c9289 100644
--- a/src/responder/sudo/sudosrv_cmd.c
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -156,10 +156,12 @@ errno_t sudosrv_cmd_done(struct sudo_cmd_ctx *cmd_ctx, int ret)
     return EOK;
 }
 
+static void sudosrv_cmd_parse_query_done(struct tevent_req *req);
+
 static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
 {
+    struct tevent_req *req = 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;
     errno_t ret;
@@ -199,11 +201,35 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
         goto done;
     }
 
-    ret = sudosrv_parse_query(cmd_ctx, cli_ctx->rctx,
-                              query_body, query_len,
-                              &cmd_ctx->uid, &cmd_ctx->username, &cmd_ctx->domain);
+    req = sudosrv_parse_query_send(cmd_ctx, cli_ctx->rctx,
+                                   query_body, query_len);
+    if (req == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(req, sudosrv_cmd_parse_query_done, cmd_ctx);
+
+    ret = EAGAIN;
+
+done:
+    return sudosrv_cmd_done(cmd_ctx, ret);
+}
+
+static void sudosrv_cmd_parse_query_done(struct tevent_req *req)
+{
+    struct sudo_cmd_ctx *cmd_ctx = NULL;
+    struct sudo_dom_ctx *dom_ctx = NULL;
+    errno_t ret;
+
+    cmd_ctx = tevent_req_callback_data(req, struct sudo_cmd_ctx);
+
+    ret = sudosrv_parse_query_recv(cmd_ctx, req, &cmd_ctx->uid,
+                                   &cmd_ctx->username, &cmd_ctx->domain);
+    talloc_zfree(req);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query: %s\n", strerror(ret)));
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query [%d]: %s\n",
+                                    ret, strerror(ret)));
         goto done;
     }
 
@@ -231,15 +257,14 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
     }
     dom_ctx->cmd_ctx = cmd_ctx;
     dom_ctx->domain = cmd_ctx->domain != NULL ? cmd_ctx->domain
-                                              : cli_ctx->rctx->domains;
+                                              : cmd_ctx->cli_ctx->rctx->domains;
 
     ret = sudosrv_get_sudorules(dom_ctx);
 
 done:
-    return sudosrv_cmd_done(cmd_ctx, ret);
+    sudosrv_cmd_done(cmd_ctx, ret);
 }
 
-
 static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
 {
     return sudosrv_cmd(SSS_SUDO_USER, cli_ctx);
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
index 9081cbd7075fd305becd8b36b5c7e077607d0952..6d6b95d3fd04a20dceb16becc571403b85885d0e 100644
--- a/src/responder/sudo/sudosrv_get_sudorules.c
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -29,6 +29,22 @@
 #include "db/sysdb_sudo.h"
 #include "responder/sudo/sudosrv_private.h"
 
+static struct sysdb_ctx* sudosrv_get_user_sysdb(struct sss_domain_info *domain)
+{
+    return domain->sysdb;
+}
+
+static struct sysdb_ctx* sudosrv_get_rules_sysdb(struct sss_domain_info *domain)
+{
+    if (domain->parent == NULL) {
+        return domain->sysdb;
+    } else {
+        /* sudo rules are stored under parent domain basedn, so we will return
+         * parent's sysdb context */
+        return domain->parent->sysdb;
+    }
+}
+
 static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx);
 
 errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx)
@@ -112,7 +128,7 @@ static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx)
         DEBUG(SSSDBG_FUNC_DATA, ("Requesting info about [%s@%s]\n",
               name, dom->name));
 
-        sysdb = dctx->domain->sysdb;
+        sysdb = sudosrv_get_user_sysdb(dctx->domain);
         if (sysdb == NULL) {
              DEBUG(SSSDBG_CRIT_FAILURE,
                    ("sysdb context not found for this domain!\n"));
@@ -327,7 +343,8 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
     TALLOC_CTX *tmp_ctx = NULL;
     struct tevent_req *dpreq = NULL;
     struct dp_callback_ctx *cb_ctx = NULL;
-    struct sysdb_ctx *sysdb;
+    struct sysdb_ctx *user_sysdb = NULL;
+    struct sysdb_ctx *rules_sysdb = NULL;
     char **groupnames = NULL;
     size_t expired_rules_num = 0;
     struct sysdb_attrs **expired_rules = NULL;
@@ -341,7 +358,21 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
         return EFAULT;
     }
 
-    sysdb = cmd_ctx->domain->sysdb;
+    user_sysdb = sudosrv_get_user_sysdb(cmd_ctx->domain);
+    if (user_sysdb == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("user sysdb context not found for this domain!\n"));
+        ret = EIO;
+        goto done;
+    }
+
+    rules_sysdb = sudosrv_get_rules_sysdb(cmd_ctx->domain);
+    if (rules_sysdb == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("rules sysdb context not found for this domain!\n"));
+        ret = EIO;
+        goto done;
+    }
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -367,7 +398,7 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
      * expired rules for this user and defaults at once we will save one
      * provider call
      */
-    ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, sysdb,
+    ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, user_sysdb,
                                    NULL, &groupnames);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -379,7 +410,7 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx)
             | SYSDB_SUDO_FILTER_INCLUDE_DFL
             | SYSDB_SUDO_FILTER_ONLY_EXPIRED
             | SYSDB_SUDO_FILTER_USERINFO;
-    ret = sudosrv_get_sudorules_query_cache(tmp_ctx, sysdb, cmd_ctx->type,
+    ret = sudosrv_get_sudorules_query_cache(tmp_ctx, rules_sysdb, cmd_ctx->type,
                                             attrs, flags, cmd_ctx->orig_username,
                                             cmd_ctx->uid, groupnames,
                                             &expired_rules, &expired_rules_num);
@@ -535,7 +566,8 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx,
 {
     TALLOC_CTX *tmp_ctx;
     errno_t ret;
-    struct sysdb_ctx *sysdb;
+    struct sysdb_ctx *user_sysdb = NULL;
+    struct sysdb_ctx *rules_sysdb = NULL;
     char **groupnames = NULL;
     const char *debug_name = NULL;
     unsigned int flags = SYSDB_SUDO_FILTER_NONE;
@@ -565,10 +597,18 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    sysdb = cmd_ctx->domain->sysdb;
-    if (sysdb == NULL) {
+    user_sysdb = sudosrv_get_user_sysdb(cmd_ctx->domain);
+    if (user_sysdb == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE,
-              ("sysdb context not found for this domain!\n"));
+              ("user sysdb context not found for this domain!\n"));
+        ret = EIO;
+        goto done;
+    }
+
+    rules_sysdb = sudosrv_get_rules_sysdb(cmd_ctx->domain);
+    if (rules_sysdb == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("rules sysdb context not found for this domain!\n"));
         ret = EIO;
         goto done;
     }
@@ -576,8 +616,8 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx,
     switch (cmd_ctx->type) {
     case SSS_SUDO_USER:
         debug_name = cmd_ctx->cased_username;
-        ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, sysdb,
-                                       NULL, &groupnames);
+        ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username,
+                                       user_sysdb, NULL, &groupnames);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                  ("Unable to retrieve user info [%d]: %s\n", strerror(ret)));
@@ -591,7 +631,7 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx,
         break;
     }
 
-    ret = sudosrv_get_sudorules_query_cache(tmp_ctx, sysdb, cmd_ctx->type,
+    ret = sudosrv_get_sudorules_query_cache(tmp_ctx, rules_sysdb, cmd_ctx->type,
                                             attrs, flags, cmd_ctx->orig_username,
                                             cmd_ctx->uid, groupnames,
                                             &rules, &num_rules);
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
index 55ea1383533188eaf8f3f731b24fe0cce3eb197d..b805940759f409ebd532707faf93d09dc3e51e04 100644
--- a/src/responder/sudo/sudosrv_private.h
+++ b/src/responder/sudo/sudosrv_private.h
@@ -90,13 +90,16 @@ errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx);
 
 errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx);
 
-errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
-                            struct resp_ctx *rctx,
-                            uint8_t *query_body,
-                            size_t query_len,
-                            uid_t *_uid,
-                            char **_username,
-                            struct sss_domain_info **_domain);
+struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
+                                            struct resp_ctx *rctx,
+                                            uint8_t *query_body,
+                                            size_t query_len);
+
+errno_t sudosrv_parse_query_recv(TALLOC_CTX *mem_ctx,
+                                 struct tevent_req *req,
+                                 uid_t *_uid,
+                                 char **_username,
+                                 struct sss_domain_info **_domain);
 
 errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
                                uint32_t error,
diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c
index 824f682c6c753bc47f303af09f03363ad79af987..d76ecbb9deb7a59adaa319fc7ef4ddb5e3071f4c 100644
--- a/src/responder/sudo/sudosrv_query.c
+++ b/src/responder/sudo/sudosrv_query.c
@@ -22,6 +22,7 @@
 #include <stdint.h>
 #include <errno.h>
 #include <talloc.h>
+#include <tevent.h>
 
 #include "util/util.h"
 #include "responder/sudo/sudosrv_private.h"
@@ -250,34 +251,38 @@ fail:
     return ret;
 }
 
-/*
- * Query format:
- * <uid><username[@domain]>
- */
-errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
-                            struct resp_ctx *rctx,
-                            uint8_t *query_body,
-                            size_t query_len,
-                            uid_t *_uid,
-                            char **_username,
-                            struct sss_domain_info **_domain)
+struct sudosrv_parse_query_state {
+    struct resp_ctx *rctx;
+    uid_t uid;
+    char *rawname;
+};
+
+static void sudosrv_parse_query_done(struct tevent_req *subreq);
+
+struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
+                                            struct resp_ctx *rctx,
+                                            uint8_t *query_body,
+                                            size_t query_len)
 {
-    TALLOC_CTX *tmp_ctx = NULL;
-    struct sss_domain_info *domain = NULL;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct sudosrv_parse_query_state *state = NULL;
     size_t offset = 0;
     size_t rawname_len = 0;
     char *rawname = NULL;
     char *domainname = NULL;
-    char *username = NULL;
-    uid_t uid;
     errno_t ret;
 
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
-        return ENOMEM;
+    /* create request */
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sudosrv_parse_query_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
     }
 
+    state->rctx = rctx;
+
     /* uid */
 
     if (query_len < sizeof(uid_t)) {
@@ -285,7 +290,7 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
         ret = EINVAL;
         goto done;
     }
-    safealign_memcpy(&uid, query_body, sizeof(uid_t), &offset);
+    safealign_memcpy(&state->uid, query_body, sizeof(uid_t), &offset);
 
     /* username[@domain] */
 
@@ -312,31 +317,112 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
 
     /* parse username */
 
-    ret = sss_parse_name_for_domains(tmp_ctx, rctx->domains,
-                                     rctx->default_domain, rawname,
-                                     &domainname, &username);
-    if (ret != EOK) {
+    state->rawname = rawname;
+    ret = sss_parse_name_for_domains(state, rctx->domains,
+                                     rctx->default_domain, state->rawname,
+                                     &domainname, NULL);
+    if (ret == EAGAIN) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Domain [%s] not found, "
+              "sending subdomain request\n", domainname));
+
+        subreq = sss_dp_get_domains_send(state, rctx, true, domainname);
+        if (req == NULL) {
+            ret = ENOMEM;
+        } else {
+            tevent_req_set_callback(subreq, sudosrv_parse_query_done, req);
+            ret = EAGAIN;
+        }
+        goto done;
+    } else if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid name received [%s]\n", rawname));
         goto done;
     }
 
+    ret = EOK;
+
+done:
+    if (ret != EAGAIN) {
+        if (ret == EOK) {
+            tevent_req_done(req);
+        } else {
+            tevent_req_error(req, ret);
+        }
+        tevent_req_post(req, rctx->ev);
+    }
+
+    return req;
+}
+
+static void sudosrv_parse_query_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+
+    ret = sss_dp_get_domains_recv(subreq);
+    talloc_free(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    }
+
+    tevent_req_done(req);
+}
+
+errno_t sudosrv_parse_query_recv(TALLOC_CTX *mem_ctx,
+                                 struct tevent_req *req,
+                                 uid_t *_uid,
+                                 char **_username,
+                                 struct sss_domain_info **_domain)
+{
+    struct sudosrv_parse_query_state *state = NULL;
+    struct sss_domain_info *domain = NULL;
+    char *username = NULL;
+    char *domainname = NULL;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct sudosrv_parse_query_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (state->rawname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("No query specified?!\n"));
+        return EINVAL;
+    }
+
+    /* Try to parse username@domain again because if the first call
+     * returned EAGAIN, then username is unset. If we get EAGAIN again,
+     * we will not search for it again.
+     */
+    ret = sss_parse_name_for_domains(state, state->rctx->domains,
+                                     state->rctx->default_domain,
+                                     state->rawname,
+                                     &domainname, &username);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Unable to parse domain [%d]: %s\n",
+                                  ret, strerror(ret)));
+        return ret;
+    }
+
+    if (username == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("No username specified!\n"));
+        return EINVAL;
+    }
+
     if (domainname != NULL) {
         /* mem_ctx because it duplicates only subdomains not domains
          * so I cannot easily steal it */
-        domain = responder_get_domain(mem_ctx, rctx, domainname);
+        domain = responder_get_domain(mem_ctx, state->rctx, domainname);
         if (domain == NULL) {
-            ret = ENOENT;
-            goto done;
+            DEBUG(SSSDBG_OP_FAILURE, ("Corresponding domain [%s] has not been "
+                                      "found\n", domainname));
+            return ENOENT;
         }
     }
 
-    *_uid = uid;
+    *_uid = state->uid;
     *_username = talloc_steal(mem_ctx, username);
     *_domain = domain; /* do not steal on mem_ctx */
 
-    ret = EOK;
-
-done:
-    talloc_free(tmp_ctx);
-    return ret;
+    return EOK;
 }
-- 
1.7.11.7

