>From 72bd382751c2240df49b90d6f3e8977b780fcc26 Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@gnome.org>
Date: Wed, 11 Apr 2012 15:02:10 +0200
Subject: [PATCH] Make re_expression and full_name_format per domain options

 * Allows different user/domain qualified names for different
   domains. For example Domain\User or user@domain.
 * The global re_expression and full_name_format options remain
   as defaults for the domains.
 * Subdomains get the re_expression and full_name_format of
   their parent domain.

https://bugzilla.redhat.com/show_bug.cgi?id=811663
---
 src/confdb/confdb.h                     |    7 +-
 src/man/sssd.conf.5.xml                 |   67 ++++++++++----
 src/responder/autofs/autofssrv_cmd.c    |    4 +-
 src/responder/common/negcache.c         |   15 ++--
 src/responder/common/negcache.h         |    1 -
 src/responder/common/responder.h        |    6 +-
 src/responder/common/responder_common.c |   14 +--
 src/responder/nss/nsssrv.c              |    3 +-
 src/responder/nss/nsssrv_cmd.c          |   22 +++--
 src/responder/nss/nsssrv_netgroup.c     |    4 +-
 src/responder/nss/nsssrv_services.c     |   12 ++-
 src/responder/pam/pamsrv.c              |    3 +-
 src/responder/pam/pamsrv_cmd.c          |   20 ++---
 src/responder/ssh/sshsrv_cmd.c          |    4 +-
 src/responder/sudo/sudosrv_cmd.c        |    4 +-
 src/tools/tools_util.c                  |    2 +-
 src/util/domain_info_utils.c            |    1 +
 src/util/usertools.c                    |  145 +++++++++++++++++++++++++++++--
 src/util/util.h                         |    1 +
 19 files changed, 252 insertions(+), 83 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 5893897..db247b1 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -63,11 +63,13 @@
 #define CONFDB_MONITOR_SBUS_TIMEOUT "sbus_timeout"
 #define CONFDB_MONITOR_ACTIVE_SERVICES "services"
 #define CONFDB_MONITOR_ACTIVE_DOMAINS "domains"
-#define CONFDB_MONITOR_NAME_REGEX   "re_expression"
-#define CONFDB_MONITOR_FULL_NAME_FORMAT "full_name_format"
 #define CONFDB_MONITOR_TRY_INOTIFY "try_inotify"
 #define CONFDB_MONITOR_KRB5_RCACHEDIR "krb5_rcache_dir"
 
+/* Both monitor and domains */
+#define CONFDB_NAME_REGEX   "re_expression"
+#define CONFDB_FULL_NAME_FORMAT "full_name_format"
+
 /* Responders */
 #define CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT "get_domains_timeout"
 
@@ -207,6 +209,7 @@ struct sss_domain_info {
     int pwd_expiration_warning;
 
     struct sysdb_ctx *sysdb;
+    struct sss_names_ctx *names;
 
     struct sss_domain_info **subdomains;
     uint32_t subdomain_count;
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index c946c6e..4e38ffe 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -122,25 +122,12 @@
                         <term>re_expression (string)</term>
                         <listitem>
                             <para>
-                                Regular expression that describes how to parse the string
+                                Default regular expression that describes how to parse the string
                                 containing user name and domain into these components.
                             </para>
                             <para>
-                                Default: <quote>(?P&lt;name&gt;[^@]+)@?(?P&lt;domain&gt;[^@]*$)</quote>
-                                which translates to "the name is everything up to the
-                                <quote>@</quote> sign, the domain everything after that"
-                            </para>
-                            <para>
-                                PLEASE NOTE: the support for non-unique named
-                                subpatterns is not available on all platforms
-                                (e.g. RHEL5 and SLES10). Only platforms with
-                                libpcre version 7 or higher can support non-unique
-                                named subpatterns.
-                            </para>
-                            <para>
-                                PLEASE NOTE ALSO: older version of libpcre only
-                                support the Python syntax (?P&lt;name&gt;) to label
-                                subpatterns.
+                                Each domain can have an individual regular expression configured.
+                                see DOMAIN SECTIONS for more info on these regular expressions.
                             </para>
                         </listitem>
                     </varlistentry>
@@ -148,7 +135,7 @@
                         <term>full_name_format (string)</term>
                         <listitem>
                             <para>
-                                A <citerefentry>
+                                The default <citerefentry>
                                     <refentrytitle>printf</refentrytitle>
                                     <manvolnum>3</manvolnum>
                                 </citerefentry>-compatible format that describes how to
@@ -156,7 +143,8 @@
                                 name.
                             </para>
                             <para>
-                                Default: <quote>%1$s@%2$s</quote>.
+                                Each domain can have an individual format string configured.
+                                see DOMAIN SECTIONS for more info on this option.
                             </para>
                         </listitem>
                     </varlistentry>
@@ -1287,6 +1275,49 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>re_expression (string)</term>
+                    <listitem>
+                        <para>
+                            Regular expression for this domain that describes how to parse
+                            the string containing user name and domain into these components.
+                        </para>
+                        <para>
+                            Default: <quote>(?P&lt;name&gt;[^@]+)@?(?P&lt;domain&gt;[^@]*$)</quote>
+                            which translates to "the name is everything up to the
+                            <quote>@</quote> sign, the domain everything after that"
+                        </para>
+                        <para>
+                            PLEASE NOTE: the support for non-unique named
+                            subpatterns is not available on all platforms
+                            (e.g. RHEL5 and SLES10). Only platforms with
+                            libpcre version 7 or higher can support non-unique
+                            named subpatterns.
+                        </para>
+                        <para>
+                            PLEASE NOTE ALSO: older version of libpcre only
+                            support the Python syntax (?P&lt;name&gt;) to label
+                            subpatterns.
+                        </para>
+                    </listitem>
+                </varlistentry>
+                <varlistentry>
+                    <term>full_name_format (string)</term>
+                    <listitem>
+                        <para>
+                            A <citerefentry>
+                                <refentrytitle>printf</refentrytitle>
+                                <manvolnum>3</manvolnum>
+                            </citerefentry>-compatible format that describes how to
+                             translate a (name, domain) tuple for this domain into a fully
+                             qualified name.
+                        </para>
+                        <para>
+                            Default: <quote>%1$s@%2$s</quote>.
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>lookup_family_order (string)</term>
                     <listitem>
                         <para>
diff --git a/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c
index 7064591..4dfa94e 100644
--- a/src/responder/autofs/autofssrv_cmd.c
+++ b/src/responder/autofs/autofssrv_cmd.c
@@ -374,8 +374,8 @@ setautomntent_send(TALLOC_CTX *mem_ctx,
     dctx->cmd_ctx = state->cmdctx;
     state->dctx = dctx;
 
-    ret = sss_parse_name(state, client->rctx->names, rawname,
-                         &domname, &state->mapname);
+    ret = sss_parse_name_for_domains(state, client->rctx->domains, rawname,
+                                     &domname, &state->mapname);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
               ("Invalid name received [%s]\n", rawname));
diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
index dd4c000..b59b1f3 100644
--- a/src/responder/common/negcache.c
+++ b/src/responder/common/negcache.c
@@ -565,7 +565,6 @@ int sss_ncache_reset_permament(struct sss_nc_ctx *ctx)
 
 errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
                                struct confdb_ctx *cdb,
-                               struct sss_names_ctx *names_ctx,
                                struct resp_ctx *rctx)
 {
     errno_t ret;
@@ -597,8 +596,8 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
         filter_set = true;
 
         for (i = 0; (filter_list && filter_list[i]); i++) {
-            ret = sss_parse_name(tmpctx, names_ctx, filter_list[i],
-                                 &domainname, &name);
+            ret = sss_parse_name_for_domains(tmpctx, domain_list, filter_list[i],
+                                             &domainname, &name);
             if (ret != EOK) {
                 DEBUG(1, ("Invalid name in filterUsers list: [%s] (%d)\n",
                          filter_list[i], ret));
@@ -642,8 +641,8 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
     else if (ret != EOK) goto done;
 
     for (i = 0; (filter_list && filter_list[i]); i++) {
-        ret = sss_parse_name(tmpctx, names_ctx, filter_list[i],
-                             &domainname, &name);
+        ret = sss_parse_name_for_domains(tmpctx, domain_list, filter_list[i],
+                                         &domainname, &name);
         if (ret != EOK) {
             DEBUG(1, ("Invalid name in filterUsers list: [%s] (%d)\n",
                      filter_list[i], ret));
@@ -694,7 +693,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
         filter_set = true;
 
         for (i = 0; (filter_list && filter_list[i]); i++) {
-            ret = sss_parse_name(tmpctx, names_ctx, filter_list[i],
+            ret = sss_parse_name(tmpctx, dom->names, filter_list[i],
                                  &domainname, &name);
             if (ret != EOK) {
                 DEBUG(1, ("Invalid name in filterGroups list: [%s] (%d)\n",
@@ -739,8 +738,8 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
     else if (ret != EOK) goto done;
 
     for (i = 0; (filter_list && filter_list[i]); i++) {
-        ret = sss_parse_name(tmpctx, names_ctx, filter_list[i],
-                             &domainname, &name);
+        ret = sss_parse_name_for_domains(tmpctx, domain_list, filter_list[i],
+                                         &domainname, &name);
         if (ret != EOK) {
             DEBUG(1, ("Invalid name in filterGroups list: [%s] (%d)\n",
                      filter_list[i], ret));
diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h
index 9d070c6..b2e6a7a 100644
--- a/src/responder/common/negcache.h
+++ b/src/responder/common/negcache.h
@@ -72,7 +72,6 @@ int sss_ncache_reset_permament(struct sss_nc_ctx *ctx);
  */
 errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
                                struct confdb_ctx *cdb,
-                               struct sss_names_ctx *names_ctx,
                                struct resp_ctx *rctx);
 
 #endif /* _NSS_NEG_CACHE_H_ */
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 27a58ea..2cc8544 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -93,8 +93,6 @@ struct resp_ctx {
     const char *sss_pipe_name;
     const char *confdb_service_path;
 
-    struct sss_names_ctx *names;
-
     hash_table_t *dp_request_table;
 
     struct timeval get_domains_last_call;
@@ -154,6 +152,10 @@ int sss_parse_name(TALLOC_CTX *memctx,
                    struct sss_names_ctx *snctx,
                    const char *orig, char **domain, char **name);
 
+int sss_parse_name_for_domains(TALLOC_CTX *memctx,
+                               struct sss_domain_info *domains,
+                               const char *orig, char **domain, char **name);
+
 int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain,
                            struct be_conn **_conn);
 struct sss_domain_info *
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index faff0fe..2e3e98a 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -581,6 +581,14 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
             continue;
         }
 
+        ret = sss_names_init(rctx->cdb, rctx->cdb, dom->name, &dom->names);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  ("fatal error initializing regex data for domain: %s\n",
+                   dom->name));
+            return ret;
+        }
+
         ret = sss_dp_init(rctx, dp_intf, cli_name, dom);
         if (ret != EOK) {
             DEBUG(0, ("fatal error setting up backend connector\n"));
@@ -594,12 +602,6 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
         return ret;
     }
 
-    ret = sss_names_init(rctx, rctx->cdb, &rctx->names);
-    if (ret != EOK) {
-        DEBUG(0, ("fatal error initializing regex data\n"));
-        return ret;
-    }
-
     /* after all initializations we are ready to listen on our socket */
     ret = set_unix_socket(rctx);
     if (ret != EOK) {
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index 85bf6dc..6225380 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -162,8 +162,7 @@ static int nss_get_config(struct nss_ctx *nctx,
         nctx->cache_refresh_percent = 0;
     }
 
-    ret = sss_ncache_prepopulate(nctx->ncache, cdb, nctx->rctx->names,
-                                 nctx->rctx);
+    ret = sss_ncache_prepopulate(nctx->ncache, cdb, nctx->rctx);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 9f28644..fc50470 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -235,11 +235,13 @@ static int fill_pwent(struct sss_packet *packet,
     int i, ret, num, t;
     bool add_domain = dom->fqnames;
     const char *domain = dom->name;
-    const char *namefmt = nctx->rctx->names->fq_fmt;
+    const char *namefmt;
     bool packet_initialized = false;
     int ncret;
     TALLOC_CTX *tmp_ctx = NULL;
 
+    namefmt = dom->names->fq_fmt;
+
     if (add_domain) dom_len = strlen(domain);
 
     to_sized_string(&pwfield, nctx->pwfield);
@@ -794,8 +796,8 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx)
     rawname = (const char *)body;
 
     domname = NULL;
-    ret = sss_parse_name(cmdctx, cctx->rctx->names, rawname,
-                         &domname, &cmdctx->name);
+    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));
         ret = ENOENT;
@@ -1681,7 +1683,7 @@ static int fill_members(struct sss_packet *packet,
     size_t rsize = *_rsize;
     char *tmpstr;
     struct sized_string name;
-    const char *namefmt = nctx->rctx->names->fq_fmt;
+    const char *namefmt = dom->names->fq_fmt;
     TALLOC_CTX *tmp_ctx = NULL;
 
     size_t delim;
@@ -1820,9 +1822,11 @@ static int fill_grent(struct sss_packet *packet,
     size_t rzero, rsize;
     bool add_domain = dom->fqnames;
     const char *domain = dom->name;
-    const char *namefmt = nctx->rctx->names->fq_fmt;
+    const char *namefmt;
     TALLOC_CTX *tmp_ctx = NULL;
 
+    namefmt = dom->names->fq_fmt;
+
     if (add_domain) {
         delim = 1;
         dom_len = strlen(domain);
@@ -2258,8 +2262,8 @@ static int nss_cmd_getgrnam(struct cli_ctx *cctx)
     rawname = (const char *)body;
 
     domname = NULL;
-    ret = sss_parse_name(cmdctx, cctx->rctx->names, rawname,
-                         &domname, &cmdctx->name);
+    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));
         ret = ENOENT;
@@ -3368,8 +3372,8 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx)
     rawname = (const char *)body;
 
     domname = NULL;
-    ret = sss_parse_name(cmdctx, cctx->rctx->names, rawname,
-                         &domname, &cmdctx->name);
+    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));
         ret = ENOENT;
diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 593b7e4..774d182 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -197,8 +197,8 @@ static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
     dctx = state->dctx;
     dctx->cmdctx = state->cmdctx;
 
-    ret = sss_parse_name(state, client->rctx->names, rawname,
-                         &domname, &state->netgr_shortname);
+    ret = sss_parse_name_for_domains(state, client->rctx->domains, rawname,
+                                     &domname, &state->netgr_shortname);
     if (ret != EOK) {
         DEBUG(2, ("Invalid name received [%s]\n", rawname));
         goto error;
diff --git a/src/responder/nss/nsssrv_services.c b/src/responder/nss/nsssrv_services.c
index 2e539f1..e0ee21c 100644
--- a/src/responder/nss/nsssrv_services.c
+++ b/src/responder/nss/nsssrv_services.c
@@ -779,7 +779,7 @@ done:
 
 errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
                             uint8_t *body, size_t blen,
-                            struct sss_names_ctx *names,
+                            struct sss_domain_info *domains,
                             char **domain_name,
                             char **service_name,
                             char **service_protocol);
@@ -820,7 +820,7 @@ int nss_cmd_getservbyname(struct cli_ctx *cctx)
     }
 
     ret = parse_getservbyname(cmdctx, body, blen,
-                              cctx->rctx->names,
+                              cctx->rctx->domains,
                               &domname,
                               &service_name,
                               &service_protocol);
@@ -870,7 +870,7 @@ done:
 
 errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
                             uint8_t *body, size_t blen,
-                            struct sss_names_ctx *names,
+                            struct sss_domain_info *domains,
                             char **domain_name,
                             char **service_name,
                             char **service_protocol)
@@ -959,8 +959,8 @@ errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
         }
     }
 
-    ret = sss_parse_name(tmp_ctx, names, rawname,
-                         &domname, &svc_name);
+    ret = sss_parse_name_for_domains(tmp_ctx, domains, rawname,
+                                     &domname, &svc_name);
     if (ret != EOK) {
         DEBUG(SSSDBG_MINOR_FAILURE,
               ("Could not split name and domain of [%s]\n",
@@ -1034,7 +1034,6 @@ nss_cmd_getserv_done(struct tevent_req *req)
 
 errno_t parse_getservbyport(TALLOC_CTX *mem_ctx,
                             uint8_t *body, size_t blen,
-                            struct sss_names_ctx *names,
                             uint16_t *service_port,
                             char **service_protocol)
 {
@@ -1138,7 +1137,6 @@ int nss_cmd_getservbyport(struct cli_ctx *cctx)
     }
 
     ret = parse_getservbyport(cmdctx, body, blen,
-                              cctx->rctx->names,
                               &port,
                               &service_protocol);
     if (ret != EOK) {
diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
index ecbf7d9..c94596c 100644
--- a/src/responder/pam/pamsrv.c
+++ b/src/responder/pam/pamsrv.c
@@ -171,8 +171,7 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = sss_ncache_prepopulate(pctx->ncache, cdb, pctx->rctx->names,
-                                 pctx->rctx);
+    ret = sss_ncache_prepopulate(pctx->ncache, cdb, pctx->rctx);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index f1fdc3f..f6c1e83 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -115,7 +115,7 @@ static int pd_set_primary_name(const struct ldb_message *msg,struct pam_data *pd
     return EOK;
 }
 
-static int pam_parse_in_data_v2(struct sss_names_ctx *snctx,
+static int pam_parse_in_data_v2(struct sss_domain_info *domains,
                              struct pam_data *pd,
                              uint8_t *body, size_t blen)
 {
@@ -153,8 +153,8 @@ static int pam_parse_in_data_v2(struct sss_names_ctx *snctx,
                     ret = extract_string(&pam_user, size, body, blen, &c);
                     if (ret != EOK) return ret;
 
-                    ret = sss_parse_name(pd, snctx, pam_user,
-                                         &pd->domain, &pd->user);
+                    ret = sss_parse_name_for_domains(pd, domains, pam_user,
+                                                     &pd->domain, &pd->user);
                     if (ret != EOK) return ret;
                     break;
                 case SSS_PAM_ITEM_SERVICE:
@@ -205,13 +205,13 @@ static int pam_parse_in_data_v2(struct sss_names_ctx *snctx,
 
 }
 
-static int pam_parse_in_data_v3(struct sss_names_ctx *snctx,
+static int pam_parse_in_data_v3(struct sss_domain_info *domains,
                              struct pam_data *pd,
                              uint8_t *body, size_t blen)
 {
     int ret;
 
-    ret = pam_parse_in_data_v2(snctx, pd, body, blen);
+    ret = pam_parse_in_data_v2(domains, pd, body, blen);
     if (ret != EOK) {
         DEBUG(1, ("pam_parse_in_data_v2 failed.\n"));
         return ret;
@@ -225,7 +225,7 @@ static int pam_parse_in_data_v3(struct sss_names_ctx *snctx,
     return EOK;
 }
 
-static int pam_parse_in_data(struct sss_names_ctx *snctx,
+static int pam_parse_in_data(struct sss_domain_info *domains,
                              struct pam_data *pd,
                              uint8_t *body, size_t blen)
 {
@@ -241,7 +241,7 @@ static int pam_parse_in_data(struct sss_names_ctx *snctx,
     for (start = end; end < last; end++) if (body[end] == '\0') break;
     if (body[end++] != '\0') return EINVAL;
 
-    ret = sss_parse_name(pd, snctx, (char *)&body[start], &pd->domain, &pd->user);
+    ret = sss_parse_name_for_domains(pd, domains, (char *)&body[start], &pd->domain, &pd->user);
     if (ret != EOK) return ret;
 
     for (start = end; end < last; end++) if (body[end] == '\0') break;
@@ -891,13 +891,13 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
 
     switch (cctx->cli_protocol_version->version) {
         case 1:
-            ret = pam_parse_in_data(cctx->rctx->names, pd, body, blen);
+            ret = pam_parse_in_data(cctx->rctx->domains, pd, body, blen);
             break;
         case 2:
-            ret = pam_parse_in_data_v2(cctx->rctx->names, pd, body, blen);
+            ret = pam_parse_in_data_v2(cctx->rctx->domains, pd, body, blen);
             break;
         case 3:
-            ret = pam_parse_in_data_v3(cctx->rctx->names, pd, body, blen);
+            ret = pam_parse_in_data_v3(cctx->rctx->domains, pd, body, blen);
             break;
         default:
             DEBUG(1, ("Illegal protocol version [%d].\n",
diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c
index fa02025..3c67fd4 100644
--- a/src/responder/ssh/sshsrv_cmd.c
+++ b/src/responder/ssh/sshsrv_cmd.c
@@ -704,8 +704,8 @@ ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx)
     }
     c += name_len;
 
-    ret = sss_parse_name(cmd_ctx, cctx->rctx->names, name,
-                         &cmd_ctx->domname, &cmd_ctx->name);
+    ret = sss_parse_name_for_domains(cmd_ctx, cctx->rctx->domains, name,
+                                     &cmd_ctx->domname, &cmd_ctx->name);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", name));
         return ENOENT;
diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
index 0f31df1..f72e2be 100644
--- a/src/responder/sudo/sudosrv_cmd.c
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -214,8 +214,8 @@ static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
     }
 
     domname = NULL;
-    ret = sss_parse_name(cmd_ctx, cli_ctx->rctx->names, rawname,
-                         &domname, &cmd_ctx->username);
+    ret = sss_parse_name_for_domains(cmd_ctx, cli_ctx->rctx->domains, rawname,
+                                     &domname, &cmd_ctx->username);
     if (ret != EOK) {
         DEBUG(2, ("Invalid name received [%s]\n", rawname));
         ret = ENOENT;
diff --git a/src/tools/tools_util.c b/src/tools/tools_util.c
index 871ba2b..fbb1d81 100644
--- a/src/tools/tools_util.c
+++ b/src/tools/tools_util.c
@@ -268,7 +268,7 @@ int init_sss_tools(struct tools_ctx **_tctx)
         goto fini;
     }
 
-    ret = sss_names_init(tctx, tctx->confdb, &tctx->snctx);
+    ret = sss_names_init(tctx, tctx->confdb, tctx->local->name, &tctx->snctx);
     if (ret != EOK) {
         DEBUG(1, ("Could not set up parsing\n"));
         goto fini;
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index 45f98d8..6eed835 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -85,6 +85,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
     dom->netgroup_timeout = parent->netgroup_timeout;
     dom->service_timeout = parent->service_timeout;
     dom->override_homedir = parent->override_homedir;
+    dom->names = parent->names;
 
     dom->subdomain_homedir = parent->subdomain_homedir;
 
diff --git a/src/util/usertools.c b/src/util/usertools.c
index ff189e3..7675585 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -54,10 +54,13 @@ static int sss_names_ctx_destructor(struct sss_names_ctx *snctx)
     return 0;
 }
 
-int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct sss_names_ctx **out)
+int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
+                   const char *domain, struct sss_names_ctx **out)
 {
     struct sss_names_ctx *ctx;
+    TALLOC_CTX *tmpctx = NULL;
     const char *errstr;
+    char *conf_path;
     int errval;
     int errpos;
     int ret;
@@ -66,10 +69,26 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct sss_names
     if (!ctx) return ENOMEM;
     talloc_set_destructor(ctx, sss_names_ctx_destructor);
 
-    ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
-                            CONFDB_MONITOR_NAME_REGEX, NULL, &ctx->re_pattern);
+    tmpctx = talloc_new(NULL);
+    if (tmpctx == NULL) goto done;
+
+    conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, domain);
+    if (conf_path == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = confdb_get_string(cdb, ctx, conf_path,
+                            CONFDB_NAME_REGEX, NULL, &ctx->re_pattern);
     if (ret != EOK) goto done;
 
+    /* If not found in the domain, look in globals */
+    if (ctx->re_pattern == NULL) {
+        ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
+                                CONFDB_NAME_REGEX, NULL, &ctx->re_pattern);
+        if (ret != EOK) goto done;
+    }
+
     if (!ctx->re_pattern) {
         ctx->re_pattern = talloc_strdup(ctx,
                                 "(?P<name>[^@]+)@?(?P<domain>[^@]*$)");
@@ -87,10 +106,17 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct sss_names
 #endif
     }
 
-    ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
-                            CONFDB_MONITOR_FULL_NAME_FORMAT, NULL, &ctx->fq_fmt);
+    ret = confdb_get_string(cdb, ctx, conf_path,
+                            CONFDB_FULL_NAME_FORMAT, NULL, &ctx->fq_fmt);
     if (ret != EOK) goto done;
 
+    /* If not found in the domain, look in globals */
+    if (ctx->fq_fmt == NULL) {
+        ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
+                                CONFDB_FULL_NAME_FORMAT, NULL, &ctx->fq_fmt);
+        if (ret != EOK) goto done;
+    }
+
     if (!ctx->fq_fmt) {
         ctx->fq_fmt = talloc_strdup(ctx, "%1$s@%2$s");
         if (!ctx->fq_fmt) {
@@ -113,6 +139,7 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct sss_names
     ret = EOK;
 
 done:
+    talloc_free(tmpctx);
     if (ret != EOK) {
         talloc_free(ctx);
     }
@@ -132,8 +159,10 @@ int sss_parse_name(TALLOC_CTX *memctx,
     origlen = strlen(orig);
 
     ret = pcre_exec(re, NULL, orig, origlen, 0, PCRE_NOTEMPTY, ovec, 30);
-    if (ret < 0) {
-        DEBUG(2, ("PCRE Matching error, %d\n", ret));
+    if (ret == PCRE_ERROR_NOMATCH) {
+        return EINVAL;
+    } else if (ret < 0) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("PCRE Matching error, %d\n", ret));
         return EINVAL;
     }
 
@@ -174,6 +203,108 @@ int sss_parse_name(TALLOC_CTX *memctx,
     return EOK;
 }
 
+static struct sss_domain_info * match_any_domain_or_subdomain_name (
+        struct sss_domain_info *dom, const char *dmatch)
+{
+    uint32_t i;
+
+    if (strcasecmp (dom->name, dmatch) == 0)
+        return dom;
+
+    for (i = 0; i < dom->subdomain_count; i++) {
+        if (strcasecmp(dom->subdomains[i]->name, dmatch) == 0) {
+            return dom->subdomains[i];
+        }
+    }
+
+    return NULL;
+}
+
+int sss_parse_name_for_domains(TALLOC_CTX *memctx,
+                               struct sss_domain_info *domains,
+                               const char *orig, char **domain, char **name)
+{
+    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;
+    TALLOC_CTX *tmp_ctx;
+    int code;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL)
+        return ENOMEM;
+
+    rname = NULL;
+    rdomain = NULL;
+
+    for (dom = domains; dom != NULL; dom = dom->next) {
+        code = sss_parse_name(tmp_ctx, dom->names, orig, &dmatch, &nmatch);
+        if (code == EOK) {
+            /*
+             * If the name matched without the domain part, make note of it.
+             * All the other domain expressions must agree on the domain-less
+             * 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;
+                }
+                only_name_seen = true;
+
+            /*
+             * If a domain was returned, then it must match the name of the
+             * domain that this expression was found on, or one of the
+             * subdomains.
+             */
+            } else {
+                match = match_any_domain_or_subdomain_name (dom, dmatch);
+                if (match != NULL) {
+                    DEBUG(SSSDBG_FUNC_DATA, ("name '%s' matched expression for "
+                                             "domain '%s', user is %s\n",
+                                             orig, dom->name, nmatch));
+                    rdomain = dmatch;
+                    rname = nmatch;
+                    break;
+                }
+            }
+
+        /* EINVAL is returned when name doesn't match */
+        } else if (code != EINVAL) {
+            talloc_free(tmp_ctx);
+            return code;
+        }
+    }
+
+    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)
+        *domain = talloc_steal(memctx, rdomain);
+    if (rname != NULL)
+        *name = talloc_steal(memctx, rname);
+
+    talloc_free(tmp_ctx);
+
+    if (rdomain == NULL && rname == NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              ("name '%s' did not match any domain's expression\n", orig));
+        return EINVAL;
+    }
+
+    return EOK;
+}
+
 char *
 sss_get_cased_name(TALLOC_CTX *mem_ctx,
                    const char *orig_name,
diff --git a/src/util/util.h b/src/util/util.h
index 2912f89..b51aebb 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -397,6 +397,7 @@ struct sss_names_ctx {
 
 int sss_names_init(TALLOC_CTX *mem_ctx,
                    struct confdb_ctx *cdb,
+                   const char *domain,
                    struct sss_names_ctx **out);
 
 int sss_parse_name(TALLOC_CTX *memctx,
-- 
1.7.10.2

