From e940351cabb934a177847b353276bab08cfd6bde Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Fri, 15 Jun 2012 14:19:34 -0400
Subject: [PATCH] Fix re_expression matching with subdomains

This patch fixes an issue which resulted in a need to initialize
responder with data from local domain, otherwise it would not correctly
detect requests for subdomains. Similar situation can occur if new
subdomain is added at runtime.

The solution is to ask for a list of subdomains in case there is a
candidate domain identified in the process of matching re_expressions
with given name.
---
 src/responder/nss/nsssrv_cmd.c     |  134 ++++++++++++++++++++++++++----------
 src/responder/nss/nsssrv_private.h |    2 +-
 src/responder/pam/pamsrv_cmd.c     |   53 ++++++++++++--
 src/util/usertools.c               |   34 +++++----
 4 files changed, 163 insertions(+), 60 deletions(-)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index fc504700c3089b5744dcd24100baa01b44c15eea..5c5f8060bac0f48b09bdd6c6c5efa7c5a33aad94 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -798,8 +798,18 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx)
     domname = NULL;
     ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
                                      &domname, &cmdctx->name);
-    if (ret != EOK) {
-        DEBUG(2, ("Invalid name received [%s]\n", rawname));
+    if (ret == EAGAIN) {
+        req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
+        if (req == NULL) {
+            ret = ENOMEM;
+        } else {
+            dctx->rawname = rawname;
+            tevent_req_set_callback(req, nss_cmd_getpwnam_cb, dctx);
+            ret = EAGAIN;
+        }
+        goto done;
+    } if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
         ret = ENOENT;
         goto done;
     }
@@ -810,18 +820,12 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx)
     if (domname) {
         dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
         if (!dctx->domain) {
-            req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
-            if (req == NULL) {
-                ret = ENOMEM;
-            } else {
-                dctx->domname = domname;
-                tevent_req_set_callback(req, nss_cmd_getpwnam_cb, dctx);
-                ret = EAGAIN;
-            }
+            ret = ENOENT;
             goto done;
         }
     } else {
         /* this is a multidomain search */
+        dctx->rawname = rawname;
         dctx->domain = cctx->rctx->domains;
         cmdctx->check_next = true;
         if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
@@ -854,6 +858,8 @@ static void nss_cmd_getpwnam_cb(struct tevent_req *req)
     struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct cli_ctx *cctx = cmdctx->cctx;
+    char *domname = NULL;
+    const char *rawname = dctx->rawname;
     errno_t ret;
 
     ret = sss_dp_get_domains_recv(req);
@@ -862,12 +868,27 @@ static void nss_cmd_getpwnam_cb(struct tevent_req *req)
         goto done;
     }
 
-    if (dctx->domname) {
-        dctx->domain = responder_get_domain(dctx, cctx->rctx, dctx->domname);
+    ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
+                                     &domname, &cmdctx->name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
+        ret = ENOENT;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s] from [%s]\n",
+              cmdctx->name, domname?domname:"<ALL>"));
+
+    if (domname) {
+        dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
         if (dctx->domain == NULL) {
             ret = ENOENT;
             goto done;
         }
+    } else {
+        /* this is a multidomain search */
+        dctx->domain = cctx->rctx->domains;
+        cmdctx->check_next = true;
     }
 
     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
@@ -2264,7 +2285,17 @@ static int nss_cmd_getgrnam(struct cli_ctx *cctx)
     domname = NULL;
     ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
                                      &domname, &cmdctx->name);
-    if (ret != EOK) {
+    if (ret == EAGAIN) {
+        req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
+        if (req == NULL) {
+            ret = ENOMEM;
+        } else {
+            dctx->rawname = rawname;
+            tevent_req_set_callback(req, nss_cmd_getgrnam_cb, dctx);
+            ret = EAGAIN;
+        }
+        goto done;
+    } else if (ret != EOK) {
         DEBUG(2, ("Invalid name received [%s]\n", rawname));
         ret = ENOENT;
         goto done;
@@ -2276,18 +2307,12 @@ static int nss_cmd_getgrnam(struct cli_ctx *cctx)
     if (domname) {
         dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
         if (!dctx->domain) {
-            req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
-            if (req == NULL) {
-                ret = ENOMEM;
-            } else {
-                dctx->domname = domname;
-                tevent_req_set_callback(req, nss_cmd_getgrnam_cb, dctx);
-                ret = EAGAIN;
-            }
+            ret = ENOENT;
             goto done;
         }
     } else {
         /* this is a multidomain search */
+        dctx->rawname = rawname;
         dctx->domain = cctx->rctx->domains;
         cmdctx->check_next = true;
         if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
@@ -2320,6 +2345,8 @@ static void nss_cmd_getgrnam_cb(struct tevent_req *req)
     struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct cli_ctx *cctx = cmdctx->cctx;
+    char *domname = NULL;
+    const char *rawname = dctx->rawname;
     errno_t ret;
 
     ret = sss_dp_get_domains_recv(req);
@@ -2328,12 +2355,26 @@ static void nss_cmd_getgrnam_cb(struct tevent_req *req)
         goto done;
     }
 
-    if (dctx->domname) {
-        dctx->domain = responder_get_domain(dctx, cctx->rctx, dctx->domname);
-        if (dctx->domain == NULL) {
+    ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
+                                     &domname, &cmdctx->name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
+        ret = ENOENT;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s] from [%s]\n",
+                              cmdctx->name, domname?domname:"<ALL>"));
+    if (domname) {
+        dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
+        if (!dctx->domain) {
             ret = ENOENT;
             goto done;
         }
+    } else {
+        /* this is a multidomain search */
+        dctx->domain = cctx->rctx->domains;
+        cmdctx->check_next = true;
     }
 
     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
@@ -3374,7 +3415,17 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx)
     domname = NULL;
     ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
                                      &domname, &cmdctx->name);
-    if (ret != EOK) {
+    if (ret == EAGAIN) {
+        req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
+        if (req == NULL) {
+            ret = ENOMEM;
+        } else {
+            dctx->rawname = rawname;
+            tevent_req_set_callback(req, nss_cmd_initgroups_cb, dctx);
+            ret = EAGAIN;
+        }
+        goto done;
+    } else if (ret != EOK) {
         DEBUG(2, ("Invalid name received [%s]\n", rawname));
         ret = ENOENT;
         goto done;
@@ -3386,18 +3437,12 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx)
     if (domname) {
         dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
         if (!dctx->domain) {
-            req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
-            if (req == NULL) {
-                ret = ENOMEM;
-            } else {
-                dctx->domname = domname;
-                tevent_req_set_callback(req, nss_cmd_initgroups_cb, dctx);
-                ret = EAGAIN;
-            }
+            ret = ENOENT;
             goto done;
         }
     } else {
         /* this is a multidomain search */
+        dctx->rawname = rawname;
         dctx->domain = cctx->rctx->domains;
         cmdctx->check_next = true;
         if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
@@ -3430,6 +3475,8 @@ static void nss_cmd_initgroups_cb(struct tevent_req *req)
     struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct cli_ctx *cctx = cmdctx->cctx;
+    char *domname = NULL;
+    const char *rawname = dctx->rawname;
     errno_t ret;
 
     ret = sss_dp_get_domains_recv(req);
@@ -3438,12 +3485,27 @@ static void nss_cmd_initgroups_cb(struct tevent_req *req)
         goto done;
     }
 
-    if (dctx->domname) {
-        dctx->domain = responder_get_domain(dctx, cctx->rctx, dctx->domname);
-        if (dctx->domain == NULL) {
+    ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains, rawname,
+                                     &domname, &cmdctx->name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", rawname));
+        ret = ENOENT;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s] from [%s]\n",
+                              cmdctx->name, domname?domname:"<ALL>"));
+
+    if (domname) {
+        dctx->domain = responder_get_domain(dctx, cctx->rctx, domname);
+        if (!dctx->domain) {
             ret = ENOENT;
             goto done;
         }
+    } else {
+        /* this is a multidomain search */
+        dctx->domain = cctx->rctx->domains;
+        cmdctx->check_next = true;
     }
 
     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 57975943bda6f7217a08c9581a06444b390915ef..f1d47c3bc4a62fb816b7b262083e36c05383673d 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -64,7 +64,7 @@ struct nss_dom_ctx {
     struct sss_domain_info *domain;
 
     /* For a case when we are discovering subdomains */
-    const char *domname;
+    const char *rawname;
 
     bool check_provider;
 
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index f6c1e835093fe24f9723347cbfe3d4f3818ae5ec..98a7d11ea3275e6eedb652055abff768ffc6a963 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -904,7 +904,16 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
                       cctx->cli_protocol_version->version));
             ret = EINVAL;
     }
-    if (ret != EOK) {
+    if (ret == EAGAIN) {
+        req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, pd->domain);
+        if (req == NULL) {
+            ret = ENOMEM;
+        } else {
+            tevent_req_set_callback(req, pam_forwarder_cb, preq);
+            ret = EAGAIN;
+        }
+        goto done;
+    } else if (ret != EOK) {
         ret = EINVAL;
         goto done;
     }
@@ -913,13 +922,7 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
     if (pd->domain) {
         preq->domain = responder_get_domain(preq, cctx->rctx, pd->domain);
         if (!preq->domain) {
-            req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, pd->domain);
-            if (req == NULL) {
-                ret = ENOMEM;
-            } else {
-                tevent_req_set_callback(req, pam_forwarder_cb, preq);
-                ret = EAGAIN;
-            }
+            ret = ENOENT;
             goto done;
         }
     } else {
@@ -966,10 +969,14 @@ done:
 
 static void pam_forwarder_cb(struct tevent_req *req)
 {
+    uint32_t terminator = SSS_END_OF_PAM_REQUEST;
     struct pam_auth_req *preq = tevent_req_callback_data(req,
                                                          struct pam_auth_req);
     struct cli_ctx *cctx = preq->cctx;
+    struct pam_data *pd;
     errno_t ret = EOK;
+    uint8_t *body;
+    size_t blen;
 
     ret = sss_dp_get_domains_recv(req);
     talloc_free(req);
@@ -977,6 +984,36 @@ static void pam_forwarder_cb(struct tevent_req *req)
         goto done;
     }
 
+    pd = preq->pd;
+
+    sss_packet_get_body(cctx->creq->in, &body, &blen);
+    if (blen >= sizeof(uint32_t) &&
+        memcmp(&body[blen - sizeof(uint32_t)], &terminator, sizeof(uint32_t)) != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Received data not terminated.\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    switch (cctx->cli_protocol_version->version) {
+        case 1:
+            ret = pam_parse_in_data(cctx->rctx->domains, pd, body, blen);
+            break;
+        case 2:
+            ret = pam_parse_in_data_v2(cctx->rctx->domains, pd, body, blen);
+            break;
+        case 3:
+            ret = pam_parse_in_data_v3(cctx->rctx->domains, pd, body, blen);
+            break;
+        default:
+            DEBUG(SSSDBG_OP_FAILURE, ("Illegal protocol version [%d].\n",
+                                      cctx->cli_protocol_version->version));
+            ret = EINVAL;
+    }
+    if (ret != EOK) {
+        ret = EINVAL;
+        goto done;
+    }
+
     if (preq->pd->domain) {
         preq->domain = responder_get_domain(preq, cctx->rctx, preq->pd->domain);
         if (preq->domain == NULL) {
diff --git a/src/util/usertools.c b/src/util/usertools.c
index a3d210e36645c331e180729b734264462f30e05a..dbc48e2d38c67172a35e83f5ce9936abe2fe648e 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -230,9 +230,9 @@ int sss_parse_name_for_domains(TALLOC_CTX *memctx,
     struct sss_domain_info *dom, *match;
     char *rdomain, *rname;
     char *dmatch, *nmatch;
-    char *only_name = NULL;
-    bool only_name_seen = false;
-    bool only_name_mismatch = false;
+    char *candidate_name = NULL;
+    char *candidate_domain = NULL;
+    bool name_mismatch = false;
     TALLOC_CTX *tmp_ctx;
     int code;
 
@@ -252,13 +252,11 @@ int sss_parse_name_for_domains(TALLOC_CTX *memctx,
              * name.
              */
             if (dmatch == NULL) {
-                if (!only_name_seen) {
-                    only_name = nmatch;
-                } else if (nmatch == NULL || only_name == NULL ||
-                           strcasecmp(only_name, nmatch) != 0) {
-                    only_name_mismatch = true;
+                if (candidate_name == NULL) {
+                    candidate_name = nmatch;
+                } else if (strcasecmp(candidate_name, nmatch) != 0) {
+                    name_mismatch = true;
                 }
-                only_name_seen = true;
 
             /*
              * If a domain was returned, then it must match the name of the
@@ -274,6 +272,8 @@ int sss_parse_name_for_domains(TALLOC_CTX *memctx,
                     rdomain = dmatch;
                     rname = nmatch;
                     break;
+                } else if (candidate_name == NULL) {
+                    candidate_domain = dmatch;
                 }
             }
 
@@ -284,12 +284,16 @@ int sss_parse_name_for_domains(TALLOC_CTX *memctx,
         }
     }
 
-    if (rdomain == NULL && rname == NULL &&
-        only_name_seen && !only_name_mismatch && only_name != NULL) {
-        DEBUG(SSSDBG_FUNC_DATA,
-              ("name '%s' matched without domain, user is %s\n", orig, nmatch));
-        rdomain = NULL;
-        rname = only_name;
+    if (rdomain == NULL && rname == NULL) {
+        if (candidate_name && !name_mismatch) {
+            DEBUG(SSSDBG_FUNC_DATA,
+                  ("name '%s' matched without domain, user is %s\n", orig, nmatch));
+            rdomain = NULL;
+            rname = candidate_name;
+        } else if (candidate_domain) {
+            *domain = talloc_steal(memctx, candidate_domain);
+            return EAGAIN;
+        }
     }
 
     if (rdomain != NULL)
-- 
1.7.7.6

