[SSSD] [PATCHES] PAM, NSS: allow UPN login names

Sumit Bose sbose at redhat.com
Tue Jul 22 19:47:35 UTC 2014


On Tue, Jul 22, 2014 at 05:55:21PM +0200, Pavel Březina wrote:
> On 07/22/2014 01:27 PM, Sumit Bose wrote:
> >Hi,
> >
> >this series of patches should solve
> >https://fedorahosted.org/sssd/ticket/1749 . The solution is a bit
> >different than the one outline in
> >https://fedorahosted.org/sssd/wiki/DesignDocs/NSSWithKerberosPrincipal
> >but after a couple of iterations I prefer this solution because it adds
> >only a minimal amount of new code and automatically covers features like
> >mid-point refresh, because the same code path is used. If we agree on
> >this approach I'll update the design page accordingly.
> >
> >The outline is in the commit message of the 5th patch, I'll copy it here
> >for easier reference:
> >
> >With this patch the NSS and PAM responders can handle user principal
> >names besides the fully qualified user names.
> >
> >User principal names are build from a user name and a domain suffix
> >separated by an '@' sign. But the domain suffix does not necessarily has
> >to be the same as the configured domain name in sssd.conf of the
> >dynamically discovered DNS domain name of a domain. The typical use case
> >is an Active Directory forest with lots of different domains. To not
> >force the users to remember the name of the individual domain they
> >belong to the AD administrator can set a common domain suffix for all
> >users from all domains in the forest. This is typically the domain name
> >used for emails to make it even more easy to the users to remember it.
> >
> >Since SSSD splits name and domain part at the '@' sign and the common
> >domain suffix might not be resolvable by DNS or the given user is not a
> >member of that domain (e.g. in the case where the forest root is used as
> >common domain suffix) SSSD might fail to look up the user.
> >
> >With this patch the NSS and PAM responder will do an extra lookup for a
> >UPN if the domain part of the given name is not known or the user was
> >not found and the login name contained the '@' sign.
> >
> >The first patch contains the needs changes for the LDAP provider,
> >patches 2, 3 and 4 some related cleanup and improvements. The main
> >functionality is in the 5th patch.
> >
> >bye,
> >Sumit
> 
> Hi,
> I went through the code and IMHO the approach you've chosen is good.

thank you for the fast response.

> 
> Can you make "U" value a macro?

sure, new version attached. (Patch 1 and 5 changed)

bye,
Sumit

> 
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From 354dc354cc68220ddb363300b577b3179c26aa26 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 7 Jul 2014 13:40:07 +0200
Subject: [PATCH 1/5] LDAP: If extra_value is 'U' do a UPN search

Besides the name the responders always send an extra string attribute to
the backends which is so far mostly empty. Since the only difference in
the processing of a request for a user name or a user principal name is
a different search attribute in the LDAP provider this extra value can
be used to indicate the type of the name. Providers which do not support
UPN lookup can just ignore this attribute.

Related to https://fedorahosted.org/sssd/ticket/1749
---
 src/providers/data_provider.h              |  2 ++
 src/providers/ldap/ldap_id.c               | 15 +++++++++++++--
 src/providers/ldap/sdap_async.h            |  1 +
 src/providers/ldap/sdap_async_initgroups.c | 11 +++++++++--
 4 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index ebb4fad..9af1362 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -147,6 +147,8 @@
  * length */
 #define DP_SEC_ID_LEN (sizeof(DP_SEC_ID) - 1)
 
+#define EXTRA_NAME_IS_UPN "U"
+
 /* AUTH related common data and functions */
 
 #define DEBUG_PAM_DATA(level, pd) do { \
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index c788b6b..eb349f3 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -70,6 +70,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
                                   struct sdap_id_conn_ctx *conn,
                                   const char *name,
                                   int filter_type,
+                                  const char *extra_value,
                                   int attrs_type,
                                   bool noexist_delete)
 {
@@ -111,7 +112,11 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
                                                           sdom->dom->domain_id);
     switch (filter_type) {
     case BE_FILTER_NAME:
-        attr_name = ctx->opts->user_map[SDAP_AT_USER_NAME].name;
+        if (extra_value && strcmp(extra_value, EXTRA_NAME_IS_UPN) == 0) {
+            attr_name = ctx->opts->user_map[SDAP_AT_USER_PRINC].name;
+        } else {
+            attr_name = ctx->opts->user_map[SDAP_AT_USER_NAME].name;
+        }
         ret = sss_filter_sanitize(state, name, &clean_name);
         if (ret != EOK) {
             goto done;
@@ -918,6 +923,7 @@ struct groups_by_user_state {
     struct sss_domain_info *domain;
 
     const char *name;
+    const char *extra_value;
     const char **attrs;
 
     int dp_error;
@@ -935,6 +941,7 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
                                               struct sdap_domain *sdom,
                                               struct sdap_id_conn_ctx *conn,
                                               const char *name,
+                                              const char *extra_value,
                                               bool noexist_delete)
 {
     struct tevent_req *req;
@@ -959,6 +966,7 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
     }
 
     state->name = name;
+    state->extra_value = extra_value;
     state->domain = sdom->dom;
     state->sysdb = sdom->dom->sysdb;
 
@@ -1020,6 +1028,7 @@ static void groups_by_user_connect_done(struct tevent_req *subreq)
                                   state->ctx,
                                   state->conn,
                                   state->name,
+                                  state->extra_value,
                                   state->attrs);
     if (!subreq) {
         tevent_req_error(req, ENOMEM);
@@ -1320,6 +1329,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                                 sdom, conn,
                                 ar->filter_value,
                                 ar->filter_type,
+                                ar->extra_value,
                                 ar->attr_type,
                                 noexist_delete);
         break;
@@ -1358,6 +1368,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
         subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx,
                                      sdom, conn,
                                      ar->filter_value,
+                                     ar->extra_value,
                                      noexist_delete);
         break;
 
@@ -1701,7 +1712,7 @@ static void get_user_and_group_groups_done(struct tevent_req *subreq)
      * Retry with users. */
     subreq = users_get_send(req, state->ev, state->id_ctx,
                             state->sdom, state->conn,
-                            state->filter_val, state->filter_type,
+                            state->filter_val, state->filter_type, NULL,
                             state->attrs_type, state->noexist_delete);
     if (subreq == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "groups_get_send failed.\n");
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index 808254a..7bb69f2 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -134,6 +134,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
                                         struct sdap_id_ctx *id_ctx,
                                         struct sdap_id_conn_ctx *conn,
                                         const char *name,
+                                        const char *extra_value,
                                         const char **grp_attrs);
 int sdap_get_initgr_recv(struct tevent_req *req);
 
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index c7169dd..43e156b 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2629,6 +2629,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
                                         struct sdap_id_ctx *id_ctx,
                                         struct sdap_id_conn_ctx *conn,
                                         const char *name,
+                                        const char *extra_value,
                                         const char **grp_attrs)
 {
     struct tevent_req *req;
@@ -2636,6 +2637,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
     int ret;
     char *clean_name;
     bool use_id_mapping;
+    const char *search_attr;
 
     DEBUG(SSSDBG_TRACE_ALL, "Retrieving info for initgroups call\n");
 
@@ -2674,10 +2676,15 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
         return NULL;
     }
 
+    if (extra_value && strcmp(extra_value, EXTRA_NAME_IS_UPN) == 0) {
+        search_attr =  state->opts->user_map[SDAP_AT_USER_PRINC].name;
+    } else {
+        search_attr =  state->opts->user_map[SDAP_AT_USER_NAME].name;
+    }
+
     state->user_base_filter =
             talloc_asprintf(state, "(&(%s=%s)(objectclass=%s)",
-                            state->opts->user_map[SDAP_AT_USER_NAME].name,
-                            clean_name,
+                            search_attr, clean_name,
                             state->opts->user_map[SDAP_OC_USER].name);
     if (!state->user_base_filter) {
         talloc_zfree(req);
-- 
1.8.3.1

-------------- next part --------------
From cecef5b7f2f1967ba153a813d528026e76c3007b Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 24 Jun 2014 18:29:20 +0200
Subject: [PATCH 2/5] PAM: extract checks from parsing routines

This patch saves the original name given at a login prompt and send to
the PAM responder in the logon_name member of the pam_data struct for
later use.

Additionally it separates the parsing of the data send by the PAM client
and the checks of this data.
---
 src/providers/data_provider.h    |  1 +
 src/providers/dp_pam_data_util.c |  1 +
 src/responder/pam/pamsrv_cmd.c   | 52 ++++++++++++++--------------------------
 3 files changed, 20 insertions(+), 34 deletions(-)

diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index 9af1362..742fcca 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -175,6 +175,7 @@ struct pam_data {
     struct sss_auth_token *authtok;
     struct sss_auth_token *newauthtok;
     uint32_t cli_pid;
+    char *logon_name;
 
     int pam_status;
     int response_delay;
diff --git a/src/providers/dp_pam_data_util.c b/src/providers/dp_pam_data_util.c
index 705169d..313948b 100644
--- a/src/providers/dp_pam_data_util.c
+++ b/src/providers/dp_pam_data_util.c
@@ -192,6 +192,7 @@ void pam_print_data(int l, struct pam_data *pd)
     DEBUG(l, "newauthtok type: %d\n", sss_authtok_get_type(pd->newauthtok));
     DEBUG(l, "priv: %d\n", pd->priv);
     DEBUG(l, "cli_pid: %d\n", pd->cli_pid);
+    DEBUG(l, "logon name: %s\n", PAM_SAFE_ITEM(pd->logon_name));
 }
 
 int pam_add_response(struct pam_data *pd, enum response_type type,
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 140d541..fd58f89 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -134,15 +134,12 @@ 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_domain_info *domains,
-                                const char *default_domain,
-                                struct pam_data *pd,
+static int pam_parse_in_data_v2(struct pam_data *pd,
                                 uint8_t *body, size_t blen)
 {
     size_t c;
     uint32_t type;
     uint32_t size;
-    char *pam_user;
     int ret;
     uint32_t start;
     uint32_t terminator;
@@ -178,12 +175,7 @@ static int pam_parse_in_data_v2(struct sss_domain_info *domains,
 
             switch(type) {
                 case SSS_PAM_ITEM_USER:
-                    ret = extract_string(&pam_user, size, body, blen, &c);
-                    if (ret != EOK) return ret;
-
-                    ret = sss_parse_name_for_domains(pd, domains,
-                                                     default_domain, pam_user,
-                                                     &pd->domain, &pd->user);
+                    ret = extract_string(&pd->logon_name, size, body, blen, &c);
                     if (ret != EOK) return ret;
                     break;
                 case SSS_PAM_ITEM_SERVICE:
@@ -226,22 +218,16 @@ static int pam_parse_in_data_v2(struct sss_domain_info *domains,
 
     } while(c < blen);
 
-    if (pd->user == NULL || *pd->user == '\0') return EINVAL;
-
-    DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd);
-
     return EOK;
 
 }
 
-static int pam_parse_in_data_v3(struct sss_domain_info *domains,
-                                const char *default_domain,
-                                struct pam_data *pd,
+static int pam_parse_in_data_v3(struct pam_data *pd,
                                 uint8_t *body, size_t blen)
 {
     int ret;
 
-    ret = pam_parse_in_data_v2(domains, default_domain, pd, body, blen);
+    ret = pam_parse_in_data_v2(pd, body, blen);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "pam_parse_in_data_v2 failed.\n");
         return ret;
@@ -284,9 +270,7 @@ static int extract_authtok_v1(struct sss_auth_token *tok,
     return ret;
 }
 
-static int pam_parse_in_data(struct sss_domain_info *domains,
-                             const char *default_domain,
-                             struct pam_data *pd,
+static int pam_parse_in_data(struct pam_data *pd,
                              uint8_t *body, size_t blen)
 {
     size_t start;
@@ -300,10 +284,7 @@ static int pam_parse_in_data(struct sss_domain_info *domains,
     /* user name */
     for (start = end; end < last; end++) if (body[end] == '\0') break;
     if (body[end++] != '\0') return EINVAL;
-
-    ret = sss_parse_name_for_domains(pd, domains, default_domain,
-                                     (char *)&body[start], &pd->domain, &pd->user);
-    if (ret != EOK) return ret;
+    pd->logon_name = (char *) &body[start];
 
     for (start = end; end < last; end++) if (body[end] == '\0') break;
     if (body[end++] != '\0') return EINVAL;
@@ -743,25 +724,28 @@ errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *pd)
 
     switch (cctx->cli_protocol_version->version) {
         case 1:
-            ret = pam_parse_in_data(cctx->rctx->domains,
-                                    cctx->rctx->default_domain, pd,
-                                    body, blen);
+            ret = pam_parse_in_data(pd, body, blen);
             break;
         case 2:
-            ret = pam_parse_in_data_v2(cctx->rctx->domains,
-                                       cctx->rctx->default_domain, pd,
-                                       body, blen);
+            ret = pam_parse_in_data_v2(pd, body, blen);
             break;
         case 3:
-            ret = pam_parse_in_data_v3(cctx->rctx->domains,
-                                       cctx->rctx->default_domain, pd,
-                                       body, blen);
+            ret = pam_parse_in_data_v3(pd, body, blen);
             break;
         default:
             DEBUG(SSSDBG_CRIT_FAILURE, "Illegal protocol version [%d].\n",
                       cctx->cli_protocol_version->version);
             ret = EINVAL;
     }
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sss_parse_name_for_domains(pd, cctx->rctx->domains,
+                                     cctx->rctx->default_domain, pd->logon_name,
+                                     &pd->domain, &pd->user);
+
+    DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd);
 
 done:
     return ret;
-- 
1.8.3.1

-------------- next part --------------
From adb6d5418eb3d4774d380098a906d260e0af7e11 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 8 Jul 2014 11:43:04 +0200
Subject: [PATCH 3/5] PAM: remove ldb_result member from pam_auth_req context

This member was used only in a single call where a local variable suits
better.
---
 src/responder/pam/pamsrv.h     |  1 -
 src/responder/pam/pamsrv_cmd.c | 11 ++++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 129419b..1e37a77 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -51,7 +51,6 @@ struct pam_auth_req {
 
     pam_dp_callback_t *callback;
 
-    struct ldb_result *res;
     bool check_provider;
     void *data;
 
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index fd58f89..b7f9f34 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -916,6 +916,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
     struct dp_callback_ctx *cb_ctx;
     struct pam_ctx *pctx =
             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+    struct ldb_result *res;
 
     while (dom) {
        /* if it is a domainless search, skip domains that require fully
@@ -970,20 +971,20 @@ static int pam_check_user_search(struct pam_auth_req *preq)
             return EFAULT;
         }
 
-        ret = sysdb_getpwnam(preq, dom, name, &preq->res);
+        ret = sysdb_getpwnam(preq, dom, name, &res);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Failed to make request to our cache!\n");
             return EIO;
         }
 
-        if (preq->res->count > 1) {
+        if (res->count > 1) {
             DEBUG(SSSDBG_FATAL_FAILURE,
                   "getpwnam call returned more than one result !?!\n");
             return ENOENT;
         }
 
-        if (preq->res->count == 0) {
+        if (res->count == 0) {
             if (preq->check_provider == false) {
                 /* set negative cache only if not result of cache check */
                 ret = sss_ncache_set_user(pctx->ncache, false, dom, name);
@@ -1012,7 +1013,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
 
         /* if we need to check the remote account go on */
         if (preq->check_provider) {
-            cacheExpire = ldb_msg_find_attr_as_uint64(preq->res->msgs[0],
+            cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
                                                       SYSDB_CACHE_EXPIRE, 0);
             if (cacheExpire < time(NULL)) {
                 break;
@@ -1023,7 +1024,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
               "Returning info for user [%s@%s]\n", name, dom->name);
 
         /* We might have searched by alias. Pass on the primary name */
-        ret = pd_set_primary_name(preq->res->msgs[0], preq->pd);
+        ret = pd_set_primary_name(res->msgs[0], preq->pd);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n");
             return ret;
-- 
1.8.3.1

-------------- next part --------------
From 34efb3edb72dfe5dc88e8831da10733532fdef71 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 7 Jul 2014 11:57:03 +0200
Subject: [PATCH 4/5] NSS: check_cache() add extra option

This patch adds a new parameter to check_cache() to allow to set the
extra value which is send to the backend during lookup requests.
---
 src/responder/nss/nsssrv_cmd.c      | 19 ++++++++++---------
 src/responder/nss/nsssrv_netgroup.c |  2 +-
 src/responder/nss/nsssrv_private.h  |  1 +
 3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index a168a3e..1b7c9ae 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -515,6 +515,7 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
                     int req_type,
                     const char *opt_name,
                     uint32_t opt_id,
+                    const char *extra,
                     sss_dp_callback_t callback,
                     void *pvt)
 {
@@ -573,7 +574,7 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
              "Performing midpoint cache update on [%s]\n", opt_name);
 
         req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
-                                      req_type, opt_name, opt_id, NULL);
+                                      req_type, opt_name, opt_id, extra);
         if (!req) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Out of memory sending out-of-band data provider "
@@ -602,7 +603,7 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
         }
 
         req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
-                                      req_type, opt_name, opt_id, NULL);
+                                      req_type, opt_name, opt_id, extra);
         if (!req) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Out of memory sending data provider request\n");
@@ -817,7 +818,7 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
          * yet) then verify that the cache is uptodate */
         if (dctx->check_provider) {
             ret = check_cache(dctx, nctx, dctx->res,
-                              SSS_DP_USER, name, 0,
+                              SSS_DP_USER, name, 0, NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -1392,7 +1393,7 @@ static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx)
          * yet) then verify that the cache is uptodate */
         if (dctx->check_provider) {
             ret = check_cache(dctx, nctx, dctx->res,
-                              SSS_DP_USER, NULL, cmdctx->id,
+                              SSS_DP_USER, NULL, cmdctx->id, NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -2767,7 +2768,7 @@ static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
          * yet) then verify that the cache is uptodate */
         if (dctx->check_provider) {
             ret = check_cache(dctx, nctx, dctx->res,
-                              SSS_DP_GROUP, name, 0,
+                              SSS_DP_GROUP, name, 0, NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -2882,7 +2883,7 @@ static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx)
          * yet) then verify that the cache is uptodate */
         if (dctx->check_provider) {
             ret = check_cache(dctx, nctx, dctx->res,
-                              SSS_DP_GROUP, NULL, cmdctx->id,
+                              SSS_DP_GROUP, NULL, cmdctx->id, NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -3776,7 +3777,7 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
          * yet) then verify that the cache is uptodate */
         if (dctx->check_provider) {
             ret = check_cache(dctx, nctx, dctx->res,
-                              SSS_DP_INITGROUPS, name, 0,
+                              SSS_DP_INITGROUPS, name, 0, NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -4041,7 +4042,7 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
             }
 
             ret = check_cache(dctx, nctx, dctx->res,
-                              req_type, req_name, req_id,
+                              req_type, req_name, req_id, NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -4144,7 +4145,7 @@ static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx)
      * yet) then verify that the cache is uptodate */
     if (dctx->check_provider) {
         ret = check_cache(dctx, nctx, dctx->res,
-                          SSS_DP_SECID, cmdctx->secid, 0,
+                          SSS_DP_SECID, cmdctx->secid, 0, NULL,
                           nss_cmd_getby_dp_callback,
                           dctx);
         if (ret != EOK) {
diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 86cb8cd..80b5c3c 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -581,7 +581,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
                               step_ctx->nctx,
                               step_ctx->dctx->res,
                               SSS_DP_NETGR,
-                              name, 0,
+                              name, 0, NULL,
                               lookup_netgr_dp_callback,
                               step_ctx);
             if (ret != EOK) {
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 2dcc07b..23bb6a8 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -122,6 +122,7 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
                     int req_type,
                     const char *opt_name,
                     uint32_t opt_id,
+                    const char *extra,
                     sss_dp_callback_t callback,
                     void *pvt);
 
-- 
1.8.3.1

-------------- next part --------------
From a797b5b10cfaa036aa7d6e5de4ff390d6cf8cdd0 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 24 Jun 2014 18:30:01 +0200
Subject: [PATCH 5/5] PAM, NSS: allow UPN login names

With this patch the NSS and PAM responders can handle user principal
names besides the fully qualified user names.

User principal names are build from a user name and a domain suffix
separated by an '@' sign. But the domain suffix does not necessarily has
to be the same as the configured domain name in sssd.conf of the
dynamically discovered DNS domain name of a domain. The typical use case
is an Active Directory forest with lots of different domains. To not
force the users to remember the name of the individual domain they
belong to the AD administrator can set a common domain suffix for all
users from all domains in the forest. This is typically the domain name
used for emails to make it even more easy to the users to remember it.

Since SSSD splits name and domain part at the '@' sign and the common
domain suffix might not be resolvable by DNS or the given user is not a
member of that domain (e.g. in the case where the forest root is used as
common domain suffix) SSSD might fail to look up the user.

With this patch the NSS and PAM responder will do an extra lookup for a
UPN if the domain part of the given name is not known or the user was
not found and the login name contained the '@' sign.

Resolves https://fedorahosted.org/sssd/ticket/1749
---
 src/providers/data_provider.h      |  1 +
 src/responder/nss/nsssrv_cmd.c     | 83 ++++++++++++++++++++++++++++++++++++--
 src/responder/nss/nsssrv_private.h |  1 +
 src/responder/pam/pamsrv_cmd.c     | 46 ++++++++++++++-------
 4 files changed, 112 insertions(+), 19 deletions(-)

diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index 742fcca..b127985 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -176,6 +176,7 @@ struct pam_data {
     struct sss_auth_token *newauthtok;
     uint32_t cli_pid;
     char *logon_name;
+    bool name_is_upn;
 
     int pam_status;
     int response_delay;
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 1b7c9ae..1e269bb 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -27,6 +27,7 @@
 #include "responder/nss/nsssrv_services.h"
 #include "responder/nss/nsssrv_mmap_cache.h"
 #include "responder/common/negcache.h"
+#include "providers/data_provider.h"
 #include "confdb/confdb.h"
 #include "db/sysdb.h"
 #include "sss_client/idmap/sss_nss_idmap.h"
@@ -719,13 +720,16 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
     char *name = NULL;
     struct nss_ctx *nctx;
     int ret;
+    static const char *user_attrs[] = SYSDB_PW_ATTRS;
+    struct ldb_message *msg;
 
     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
 
     while (dom) {
        /* if it is a domainless search, skip domains that require fully
          * qualified names instead */
-        while (dom && cmdctx->check_next && dom->fqnames) {
+        while (dom && cmdctx->check_next && dom->fqnames
+                && !cmdctx->name_is_upn) {
             dom = get_next_domain(dom, false);
         }
 
@@ -774,7 +778,37 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
             return EIO;
         }
 
-        ret = sysdb_getpwnam(cmdctx, dom, name, &dctx->res);
+        if (cmdctx->name_is_upn) {
+            ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
+            if (ret != EOK && ret != ENOENT) {
+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
+                return ret;
+            }
+
+            dctx->res = talloc_zero(cmdctx, struct ldb_result);
+            if (dctx->res == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+                return ENOMEM;
+            }
+
+            if (ret == ENOENT) {
+                dctx->res->count = 0;
+                dctx->res->msgs = NULL;
+                ret = EOK;
+            } else {
+                dctx->res->count = 1;
+                dctx->res->msgs = talloc_array(dctx->res,
+                                               struct ldb_message *, 1);
+                if (dctx->res->msgs == NULL) {
+                    DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+                    return ENOMEM;
+                }
+
+                dctx->res->msgs[0] = talloc_steal(dctx->res->msgs, msg);
+            }
+        } else {
+            ret = sysdb_getpwnam(cmdctx, dom, name, &dctx->res);
+        }
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Failed to make request to our cache!\n");
@@ -818,7 +852,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
          * yet) then verify that the cache is uptodate */
         if (dctx->check_provider) {
             ret = check_cache(dctx, nctx, dctx->res,
-                              SSS_DP_USER, name, 0, NULL,
+                              SSS_DP_USER, name, 0,
+                              cmdctx->name_is_upn ? EXTRA_NAME_IS_UPN : NULL,
                               nss_cmd_getby_dp_callback,
                               dctx);
             if (ret != EOK) {
@@ -841,6 +876,30 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
     return ENOENT;
 }
 
+static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx)
+{
+    int ret;
+
+    if (dctx->domain == NULL) {
+        dctx->domain = dctx->cmdctx->cctx->rctx->domains;
+        dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
+        dctx->cmdctx->check_next = true;
+        dctx->cmdctx->name = talloc_strdup(dctx->cmdctx, dctx->rawname);
+        dctx->cmdctx->name_is_upn = true;
+        if (dctx->cmdctx->name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+            return ENOMEM;
+        }
+    }
+
+    ret = nss_cmd_getpwnam_search(dctx);
+    if (ret == EOK) {
+        ret = nss_cmd_getpw_send_reply(dctx, false);
+    }
+
+    return ret;
+}
+
 static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx);
 static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter);
 static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx);
@@ -989,6 +1048,12 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
     }
 
 done:
+    if (ret == ENOENT && dctx->cmdctx->cmd == SSS_NSS_GETPWNAM \
+        && strchr(dctx->rawname, '@') != NULL) {
+        /* assume Kerberos principal */
+        ret = nss_cmd_assume_upn(dctx);
+    }
+
     ret = nss_cmd_done(cmdctx, ret);
     if (ret) {
         NSS_CMD_FATAL_ERROR(cctx);
@@ -1194,6 +1259,9 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
         if (ret == EOK) {
             /* we have results to return */
             ret = nss_cmd_getpw_send_reply(dctx, false);
+        } else if (ret == ENOENT && strchr(dctx->rawname, '@') != NULL) {
+            /* assume Kerberos principal */
+            ret = nss_cmd_assume_upn(dctx);
         }
         break;
     case SSS_NSS_GETGRNAM:
@@ -1244,7 +1312,11 @@ static void nss_cmd_getbynam_done(struct tevent_req *req)
     ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains,
                                      cctx->rctx->default_domain, rawname,
                                      &domname, &cmdctx->name);
-    if (ret != EOK) {
+    if (ret == EAGAIN && dctx->cmdctx->cmd == SSS_NSS_GETPWNAM) {
+        /* assume Kerberos principal */
+        ret = nss_cmd_assume_upn(dctx);
+        goto done;
+    } else if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
         ret = ENOENT;
         goto done;
@@ -1274,6 +1346,9 @@ static void nss_cmd_getbynam_done(struct tevent_req *req)
         if (ret == EOK) {
             /* we have results to return */
             ret = nss_cmd_getpw_send_reply(dctx, false);
+        } else if (ret == ENOENT && strchr(dctx->rawname, '@') != NULL) {
+            /* assume Kerberos principal */
+            ret = nss_cmd_assume_upn(dctx);
         }
         break;
     case SSS_NSS_GETGRNAM:
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 23bb6a8..e5f3bde 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -31,6 +31,7 @@ struct nss_cmd_ctx {
     struct cli_ctx *cctx;
     enum sss_cli_command cmd;
     char *name;
+    bool name_is_upn;
     uint32_t id;
     char *secid;
 
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index b7f9f34..e4e7a0d 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -882,7 +882,22 @@ static void pam_forwarder_cb(struct tevent_req *req)
     pd = preq->pd;
 
     ret = pam_forwarder_parse_data(cctx, pd);
-    if (ret != EOK) {
+    if (ret == EAGAIN) {
+        if (strchr(preq->pd->logon_name, '@') == NULL) {
+            goto done;
+        }
+        /* Assuming Kerberos principal */
+        preq->domain = preq->cctx->rctx->domains;
+        preq->check_provider = NEED_CHECK_PROVIDER(preq->domain->provider);
+        preq->pd->user = talloc_strdup(preq->pd, preq->pd->logon_name);
+        if (preq->pd->user == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+            ret = ENOMEM;
+            goto done;
+        }
+        preq->pd->name_is_upn = true;
+        preq->pd->domain = NULL;
+    } else if (ret != EOK) {
         ret = EINVAL;
         goto done;
     }
@@ -917,11 +932,14 @@ static int pam_check_user_search(struct pam_auth_req *preq)
     struct pam_ctx *pctx =
             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
     struct ldb_result *res;
+    static const char *user_attrs[] = SYSDB_PW_ATTRS;
+    struct ldb_message *msg;
 
     while (dom) {
        /* if it is a domainless search, skip domains that require fully
          * qualified names instead */
-        while (dom && !preq->pd->domain && dom->fqnames) {
+        while (dom && !preq->pd->domain && !preq->pd->name_is_upn
+                && dom->fqnames) {
             dom = get_next_domain(dom, false);
         }
 
@@ -971,20 +989,18 @@ static int pam_check_user_search(struct pam_auth_req *preq)
             return EFAULT;
         }
 
-        ret = sysdb_getpwnam(preq, dom, name, &res);
-        if (ret != EOK) {
+        if (preq->pd->name_is_upn) {
+            ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
+        } else {
+            ret = sysdb_search_user_by_name(preq, dom, name, user_attrs, &msg);
+        }
+        if (ret != EOK && ret != ENOENT) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Failed to make request to our cache!\n");
             return EIO;
         }
 
-        if (res->count > 1) {
-            DEBUG(SSSDBG_FATAL_FAILURE,
-                  "getpwnam call returned more than one result !?!\n");
-            return ENOENT;
-        }
-
-        if (res->count == 0) {
+        if (ret == ENOENT) {
             if (preq->check_provider == false) {
                 /* set negative cache only if not result of cache check */
                 ret = sss_ncache_set_user(pctx->ncache, false, dom, name);
@@ -1013,7 +1029,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
 
         /* if we need to check the remote account go on */
         if (preq->check_provider) {
-            cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
+            cacheExpire = ldb_msg_find_attr_as_uint64(msg,
                                                       SYSDB_CACHE_EXPIRE, 0);
             if (cacheExpire < time(NULL)) {
                 break;
@@ -1024,7 +1040,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
               "Returning info for user [%s@%s]\n", name, dom->name);
 
         /* We might have searched by alias. Pass on the primary name */
-        ret = pd_set_primary_name(res->msgs[0], preq->pd);
+        ret = pd_set_primary_name(msg, preq->pd);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Could not canonicalize username\n");
             return ret;
@@ -1046,8 +1062,8 @@ static int pam_check_user_search(struct pam_auth_req *preq)
         preq->check_provider = false;
 
         dpreq = sss_dp_get_account_send(preq, preq->cctx->rctx,
-                                        dom, false, SSS_DP_INITGROUPS,
-                                        name, 0, NULL);
+                              dom, false, SSS_DP_INITGROUPS, name, 0,
+                              preq->pd->name_is_upn ? EXTRA_NAME_IS_UPN : NULL);
         if (!dpreq) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Out of memory sending data provider request\n");
-- 
1.8.3.1



More information about the sssd-devel mailing list