Hi,

this patch set should fix https://fedorahosted.org/sssd/ticket/2958
"Support multiple principals for IPA users" so the IPA users can log in
with their Kerberos alias as well.

The overall code-path was already added for the UPN feature but had to
be extended at various places. The main difference is that the realm
part of the AD UPNs with an alternative domain suffix do not related to
a known domain name. The realm part from the Kerberos aliases can come
from any domain. The same is true for email addresses which are supported
by this patch set as well.

I know that Jakub had some concerns adding email addresses now and the
another attribute in the next version and so on. I would like to make
this scheme more generic so that the attributes which should be used for
login names can be configured. Unfortunately I didn't had the time do
already do it in this patch-set.

Adding a larger number of sources for the login name increases the
chance for collisions. But since each of the name types, Kerberos
principals and email addresses, are expected to be unique in their
domain, I hope the chances are still low enough.

bye,
Sumit
From b2012e5b0cc8af65f18a6e48774b80246c68c04a Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 22 Jul 2016 17:35:43 +0200
Subject: [PATCH 01/14] IPA: fix lookup by UPN for subdomains

Currently the user name used in the extdom exop request is
unconditionally set to the short name. While this is correct for the
general name based lookups it breaks UPN/email based lookups where the
name part after the @-sign might not match to domain name. I guess this
was introduce during the sysdb refactoring.
---
 src/providers/ipa/ipa_subdomains_id.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains_id.c 
b/src/providers/ipa/ipa_subdomains_id.c
index 
0a89ef5a68802fb7d5f9faeb9ef1d9f9cf8d14f2..4a237ca4e2878e0972f7bd884f81cda0317a6fb9
 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -344,6 +344,7 @@ struct ipa_get_subdom_acct {
     int entry_type;
     const char *filter;
     int filter_type;
+    const char *extra_value;
     bool use_pac;
     struct ldb_message *user_msg;
 
@@ -393,6 +394,7 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX 
*memctx,
     state->entry_type = (ar->entry_type & BE_REQ_TYPE_MASK);
     state->filter = ar->filter_value;
     state->filter_type = ar->filter_type;
+    state->extra_value = ar->extra_value;
 
     switch (state->entry_type) {
         case BE_REQ_USER:
@@ -499,10 +501,16 @@ static void ipa_get_subdom_acct_connected(struct 
tevent_req *subreq)
     switch (state->filter_type) {
         case BE_FILTER_NAME:
             req_input->type = REQ_INP_NAME;
-            /* The extdom plugin expects the shortname and domain separately */
-            ret = sss_parse_internal_fqname(req_input, state->filter,
-                                            &shortname, NULL);
-            req_input->inp.name = talloc_steal(req_input, shortname);
+            /* The extdom plugin expects the shortname and domain separately,
+             * but for UPN/email lookup we need to send the raw name */
+            if (state->extra_value != NULL
+                    && strcmp(state->extra_value, EXTRA_NAME_IS_UPN) == 0) {
+                req_input->inp.name = talloc_strdup(req_input, state->filter);
+            } else {
+                ret = sss_parse_internal_fqname(req_input, state->filter,
+                                                &shortname, NULL);
+                req_input->inp.name = talloc_steal(req_input, shortname);
+            }
             if (req_input->inp.name == NULL) {
                 DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
                 tevent_req_error(req, ENOMEM);
-- 
2.1.0

From 762cd84cc5851666607c871d53f4e309b8760f6c Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 22 Jul 2016 12:19:26 +0200
Subject: [PATCH 02/14] LDAP: allow multiple user principals

In general a user can have multiple principals and recent IPA version
added support to defined multiple principals. With this patch SSSD does
not only store the first but all principals read by LDAP from a server.

Resolves https://fedorahosted.org/sssd/ticket/2958
---
 src/providers/ldap/sdap_async_users.c | 32 ++++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/src/providers/ldap/sdap_async_users.c 
b/src/providers/ldap/sdap_async_users.c
index 
e44c045b3f8ff6aed33a42cf2919bc01aa41a243..28101a2d8a38f97d09d50a9f7e071a030b4f9719
 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -142,6 +142,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
     char *sid_str;
     char *dom_sid_str = NULL;
     struct sss_domain_info *subdomain;
+    size_t c;
 
     DEBUG(SSSDBG_TRACE_FUNC, "Save user\n");
 
@@ -440,20 +441,23 @@ int sdap_save_user(TALLOC_CTX *memctx,
         DEBUG(SSSDBG_TRACE_FUNC,
               "User principal is not available for [%s].\n", user_name);
     } else {
-        upn = talloc_strdup(user_attrs, (const char*) el->values[0].data);
-        if (!upn) {
-            ret = ENOMEM;
-            goto done;
-        }
-        if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
-            make_realm_upper_case(upn);
-        }
-        DEBUG(SSSDBG_TRACE_FUNC,
-              "Adding user principal [%s] to attributes of [%s].\n",
-               upn, user_name);
-        ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
-        if (ret) {
-            goto done;
+        for (c = 0; c < el->num_values; c++) {
+            upn = talloc_strdup(tmpctx, (const char*) el->values[c].data);
+            if (!upn) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
+                make_realm_upper_case(upn);
+            }
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Adding user principal [%s] to attributes of [%s].\n",
+                   upn, user_name);
+            ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
+            if (ret) {
+                goto done;
+            }
         }
     }
 
-- 
2.1.0

From 5c1a50ef35d816438880e4e9951d0355b283980f Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Sat, 18 Jun 2016 18:24:50 +0200
Subject: [PATCH 03/14] LDAP: new attribute option ldap_user_email

---
 src/config/SSSDConfig/__init__.py.in     |  1 +
 src/config/etc/sssd.api.d/sssd-ad.conf   |  1 +
 src/config/etc/sssd.api.d/sssd-ipa.conf  |  1 +
 src/config/etc/sssd.api.d/sssd-ldap.conf |  1 +
 src/db/sysdb.h                           |  1 +
 src/man/sssd-ldap.5.xml                  | 13 +++++++++++++
 src/providers/ad/ad_opts.c               |  1 +
 src/providers/ipa/ipa_opts.c             |  1 +
 src/providers/ldap/ldap_opts.c           |  3 +++
 src/providers/ldap/sdap.h                |  1 +
 10 files changed, 24 insertions(+)

diff --git a/src/config/SSSDConfig/__init__.py.in 
b/src/config/SSSDConfig/__init__.py.in
index 
b5e078d0118a15c10b43fbe050176943ec90e0ee..7856c4c6b2d675b7f7f0f5f2048086044e8fb5ea
 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -325,6 +325,7 @@ option_strings = {
     'ldap_user_ssh_public_key' : _('SSH public key attribute'),
     'ldap_user_auth_type' : _('attribute listing allowed authentication types 
for a user'),
     'ldap_user_certificate' : _('attribute containing the X509 certificate of 
the user'),
+    'ldap_user_email' : _('attribute containing the email address of the 
user'),
 
     'ldap_user_extra_attrs' : _('A list of extra attributes to download along 
with the user entry'),
 
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf 
b/src/config/etc/sssd.api.d/sssd-ad.conf
index 
23006d26ca6fe7ca2b912ef091b4c73d5d23bee1..87a74f4af0770874c71baaea02d2313721db78bf
 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -98,6 +98,7 @@ ldap_pwd_attribute = str, None, false
 ldap_user_ssh_public_key = str, None, false
 ldap_user_auth_type = str, None, false
 ldap_user_certificate = str, None, false
+ldap_user_email = str, None, false
 ldap_group_search_base = str, None, false
 ldap_group_search_scope = str, None, false
 ldap_group_search_filter = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf 
b/src/config/etc/sssd.api.d/sssd-ipa.conf
index 
67a46102b4e8dfff2b44b21ac18c0ad8822d7f3a..88da36ef4a0a067530dfd44b7a231f4f74c800f2
 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -92,6 +92,7 @@ ldap_pwd_attribute = str, None, false
 ldap_user_ssh_public_key = str, None, false
 ldap_user_auth_type = str, None, false
 ldap_user_certificate = str, None, false
+ldap_user_email = str, None, false
 ldap_group_search_base = str, None, false
 ldap_group_search_scope = str, None, false
 ldap_group_search_filter = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf 
b/src/config/etc/sssd.api.d/sssd-ldap.conf
index 
8b52f268af195bc68d45389cda52a0ad0aba1aa3..c2ad3463d26cd73b8146604c8060224449421fe6
 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -86,6 +86,7 @@ ldap_user_nds_login_allowed_time_map = str, None, false
 ldap_user_ssh_public_key = str, None, false
 ldap_user_auth_type = str, None, false
 ldap_user_certificate = str, None, false
+ldap_user_email = str, None, false
 ldap_group_search_base = str, None, false
 ldap_group_search_scope = str, None, false
 ldap_group_search_filter = str, None, false
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 
407ce3c18a7077e8fe45c3c9c7576ae626105122..bbc07ac00656ebe510211985f0288199212689a8
 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -139,6 +139,7 @@
 
 #define SYSDB_AUTH_TYPE "authType"
 #define SYSDB_USER_CERT "userCertificate"
+#define SYSDB_USER_EMAIL "mail"
 
 #define SYSDB_SUBDOMAIN_REALM "realmName"
 #define SYSDB_SUBDOMAIN_FLAT "flatName"
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index 
ce2051d9d3c7df51e26e54abf49e8a20bf5ba3d3..6009dd8dfa787874c085c293b2d1f8aac6d95714
 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -828,6 +828,19 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ldap_user_email (string)</term>
+                    <listitem>
+                        <para>
+                            Name of the LDAP attribute containing the email
+                            address of the user.
+                        </para>
+                        <para>
+                            Default: mail
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ldap_group_object_class (string)</term>
                     <listitem>
                         <para>
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index 
57dfcca6b998083c7cf9ac0bcb142ff7736cc8b9..829f9d9556bc3fa74a95eb76db0e31b19befe8fe
 100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -218,6 +218,7 @@ struct sdap_attr_map ad_2008r2_user_map[] = {
     { "ldap_user_ssh_public_key", NULL, SYSDB_SSH_PUBKEY, NULL },
     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
     { "ldap_user_certificate", "userCertificate;binary", SYSDB_USER_CERT, NULL 
},
+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index 
a0c318a511693d884f03f0372c592d633ebdcbae..cd3fe9ae4302ff4837a500b9a0c834dadb11f87d
 100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -204,6 +204,7 @@ struct sdap_attr_map ipa_user_map[] = {
     { "ldap_user_ssh_public_key", "ipaSshPubKey", SYSDB_SSH_PUBKEY, NULL },
     { "ldap_user_auth_type", "ipaUserAuthType", SYSDB_AUTH_TYPE, NULL },
     { "ldap_user_certificate", "userCertificate;binary", SYSDB_USER_CERT, NULL 
},
+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 
diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c
index 
524579d4fcd478f20678bebf2c3ce18f61ed0cb9..c6efe332f53c04f3cdc80875d5ca339ad90cb7ee
 100644
--- a/src/providers/ldap/ldap_opts.c
+++ b/src/providers/ldap/ldap_opts.c
@@ -180,6 +180,7 @@ struct sdap_attr_map rfc2307_user_map[] = {
     { "ldap_user_ssh_public_key", "sshPublicKey", SYSDB_SSH_PUBKEY, NULL },
     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
     { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 
@@ -237,6 +238,7 @@ struct sdap_attr_map rfc2307bis_user_map[] = {
     { "ldap_user_ssh_public_key", "sshPublicKey", SYSDB_SSH_PUBKEY, NULL },
     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
     { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 
@@ -294,6 +296,7 @@ struct sdap_attr_map gen_ad2008r2_user_map[] = {
     { "ldap_user_ssh_public_key", NULL, SYSDB_SSH_PUBKEY, NULL },
     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
     { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 
81da1144c657cb71ac860bbe82127a18759e0439..e3cb8464ff40538e1e7f1ba853ed71d9a5cc3c98
 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -284,6 +284,7 @@ enum sdap_user_attrs {
     SDAP_AT_USER_SSH_PUBLIC_KEY,
     SDAP_AT_USER_AUTH_TYPE,
     SDAP_AT_USER_CERT,
+    SDAP_AT_USER_EMAIL,
 
     SDAP_OPTS_USER /* attrs counter */
 };
-- 
2.1.0

From 3f09cfc6598d34ff42be1c85f448b5c0635410e0 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 20 Jun 2016 12:57:43 +0200
Subject: [PATCH 04/14] sysdb: include email in UPN searches

Email addresses and Kerberos user principals names (UPNs) do not only
look similar they also can be used to identify a user uniquely.

In future this approach should be replace by a more generic one where
the attributes which can uniquely identifies a user can be configured to
support even a wider range of login names.
---
 src/db/sysdb.h     | 2 +-
 src/db/sysdb_ops.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 
bbc07ac00656ebe510211985f0288199212689a8..5314ae082542c5a415216d16a6fc2b754fb6384b
 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -185,7 +185,7 @@
 #define SYSDB_PWNAM_FILTER 
"(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
 #define SYSDB_PWUID_FILTER "(&("SYSDB_UC")("SYSDB_UIDNUM"=%lu))"
 #define SYSDB_PWSID_FILTER "(&("SYSDB_UC")("SYSDB_SID_STR"=%s))"
-#define SYSDB_PWUPN_FILTER 
"(&("SYSDB_UC")(|("SYSDB_UPN"=%s)("SYSDB_CANONICAL_UPN"=%s)))"
+#define SYSDB_PWUPN_FILTER 
"(&("SYSDB_UC")(|("SYSDB_UPN"=%s)("SYSDB_CANONICAL_UPN"=%s)("SYSDB_USER_EMAIL"=%s)))"
 #define SYSDB_PWENT_FILTER "("SYSDB_UC")"
 
 #define SYSDB_GRNAM_FILTER 
"(&("SYSDB_GC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 
4755ea3427b99a51d73b7b9134e357cf2b987613..f59e1ebcd46441497290155949703ef03c832f91
 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -537,7 +537,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
     struct ldb_dn *base_dn;
     int ret;
     const char *def_attrs[] = { SYSDB_NAME, SYSDB_UPN, SYSDB_CANONICAL_UPN,
-                                NULL };
+                                SYSDB_USER_EMAIL, NULL };
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -553,7 +553,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
 
     ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
                      base_dn, LDB_SCOPE_SUBTREE, attrs ? attrs : def_attrs,
-                     SYSDB_PWUPN_FILTER, upn, upn);
+                     SYSDB_PWUPN_FILTER, upn, upn, upn);
     if (ret != EOK) {
         ret = sysdb_error_to_errno(ret);
         goto done;
-- 
2.1.0

From 6ac7dacf122da2933ce6fe9b2ceff356c1b65721 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 20 Jun 2016 12:58:16 +0200
Subject: [PATCH 05/14] LDAP: include email in UPN searches

---
 src/providers/ldap/ldap_id.c               | 18 +++++++++++++----
 src/providers/ldap/sdap_async_initgroups.c | 32 ++++++++++++++++++++++++------
 2 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 
2fccc7e6c2572c8a35f6bc7b2f09fcf074e8e63c..f27759e45c27f781617f53bc3ca5808a6dcbb601
 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -127,12 +127,22 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
         break;
     case BE_FILTER_NAME:
         if (extra_value && strcmp(extra_value, EXTRA_NAME_IS_UPN) == 0) {
-            attr_name = ctx->opts->user_map[SDAP_AT_USER_PRINC].name;
-
             ret = sss_filter_sanitize(state, filter_value, &clean_value);
             if (ret != EOK) {
                 goto done;
             }
+            /* TODO: Do we have to check the attribute names more carefully? */
+            user_filter = talloc_asprintf(state, "(|(%s=%s)(%s=%s))",
+                                   
ctx->opts->user_map[SDAP_AT_USER_PRINC].name,
+                                   clean_value,
+                                   
ctx->opts->user_map[SDAP_AT_USER_EMAIL].name,
+                                   clean_value);
+            talloc_zfree(clean_value);
+            if (user_filter == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+                ret = ENOMEM;
+                goto done;
+            }
         } else {
             attr_name = ctx->opts->user_map[SDAP_AT_USER_NAME].name;
 
@@ -242,8 +252,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
         goto done;
     }
 
-    if (attr_name == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Missing search attribute name.\n");
+    if (attr_name == NULL && user_filter == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Missing search attribute name or filter.\n");
         ret = EINVAL;
         goto done;
     }
diff --git a/src/providers/ldap/sdap_async_initgroups.c 
b/src/providers/ldap/sdap_async_initgroups.c
index 
17593f0a268813662d6c7fbf658b1eb4599ce3c3..0a42b18662a8fe12cf048aadfef257b5d9cb48a3
 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2736,13 +2736,25 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX 
*memctx,
         break;
     case BE_FILTER_NAME:
         if (extra_value && strcmp(extra_value, EXTRA_NAME_IS_UPN) == 0) {
-            search_attr =  state->opts->user_map[SDAP_AT_USER_PRINC].name;
 
             ret = sss_filter_sanitize(state, state->filter_value, &clean_name);
             if (ret != EOK) {
                 talloc_zfree(req);
                 return NULL;
             }
+
+            state->user_base_filter =
+                    talloc_asprintf(state,
+                                 "(&(|(%s=%s)(%s=%s))(objectclass=%s)",
+                                 
state->opts->user_map[SDAP_AT_USER_PRINC].name,
+                                 clean_name,
+                                 
state->opts->user_map[SDAP_AT_USER_EMAIL].name,
+                                 clean_name,
+                                 state->opts->user_map[SDAP_OC_USER].name);
+            if (state->user_base_filter == NULL) {
+                talloc_zfree(req);
+                return NULL;
+            }
         } else {
             search_attr = state->opts->user_map[SDAP_AT_USER_NAME].name;
 
@@ -2766,15 +2778,23 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX 
*memctx,
         return NULL;
     }
 
-    state->user_base_filter =
-            talloc_asprintf(state, "(&(%s=%s)(objectclass=%s)",
-                            search_attr, clean_name,
-                            state->opts->user_map[SDAP_OC_USER].name);
-    if (!state->user_base_filter) {
+    if (search_attr == NULL && state->user_base_filter == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Missing search attribute name or filter.\n");
         talloc_zfree(req);
         return NULL;
     }
 
+    if (state->user_base_filter == NULL) {
+        state->user_base_filter =
+                talloc_asprintf(state, "(&(%s=%s)(objectclass=%s)",
+                                search_attr, clean_name,
+                                state->opts->user_map[SDAP_OC_USER].name);
+        if (!state->user_base_filter) {
+            talloc_zfree(req);
+            return NULL;
+        }
+    }
+
     if (use_id_mapping) {
         /* When mapping IDs or looking for SIDs, we don't want to limit
          * ourselves to users with a UID value. But there must be a SID to map
-- 
2.1.0

From b3c9c37baa49ade82f8cf873a2e0d2e0e7e16fad Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 20 Jun 2016 13:37:56 +0200
Subject: [PATCH 06/14] NSS: add user email to fill_orig()

The IPA server must send the email address of a user to the clients to
allow login by email.
---
 src/db/sysdb.h                 | 1 +
 src/responder/nss/nsssrv_cmd.c | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 
5314ae082542c5a415216d16a6fc2b754fb6384b..921c1729064d2b151c2c71719e7a854b6a21176a
 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -220,6 +220,7 @@
                         SYSDB_SID_STR, \
                         SYSDB_UPN, \
                         SYSDB_USER_CERT, \
+                        SYSDB_USER_EMAIL, \
                         SYSDB_OVERRIDE_DN, \
                         SYSDB_OVERRIDE_OBJECT_DN, \
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 
1ae17969688fa29734ca14fd2b152decef1fdbca..da007d1e50351eed60d9c99cc60454ae40fc62d9
 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -4416,6 +4416,7 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx 
*dctx)
                                    SYSDB_AD_USER_ACCOUNT_CONTROL,
                                    SYSDB_SSH_PUBKEY,
                                    SYSDB_USER_CERT,
+                                   SYSDB_USER_EMAIL,
                                    SYSDB_ORIG_DN,
                                    SYSDB_ORIG_MEMBEROF,
                                    SYSDB_DEFAULT_ATTRS, NULL};
@@ -4972,6 +4973,7 @@ static errno_t fill_orig(struct sss_packet *packet,
                                     SYSDB_AD_USER_ACCOUNT_CONTROL,
                                     SYSDB_SSH_PUBKEY,
                                     SYSDB_USER_CERT,
+                                    SYSDB_USER_EMAIL,
                                     SYSDB_ORIG_DN,
                                     SYSDB_ORIG_MEMBEROF,
                                     NULL};
-- 
2.1.0

From 88118645466c50c52a81e5ff38d66ecb2dfda2a7 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 20 Jun 2016 16:11:11 +0200
Subject: [PATCH 07/14] utils: add is_email_from_domain()

---
 src/tests/cmocka/test_utils.c | 21 +++++++++++++++++++++
 src/util/domain_info_utils.c  | 28 ++++++++++++++++++++++++++++
 src/util/util.h               |  1 +
 3 files changed, 50 insertions(+)

diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index 
b08b19708bb59a076a79805fa37a15924152b8e2..ce6240483cbe9ed5ef172a840260df3da9268643
 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -1682,6 +1682,25 @@ static void test_sss_output_name(void **state)
     assert_true(check_leaks_pop(global_talloc_context) == true);
 }
 
+static void test_is_email_from_domain(void **state)
+{
+    struct dom_list_test_ctx *test_ctx = talloc_get_type(*state,
+                                                      struct 
dom_list_test_ctx);
+    struct sss_domain_info *d;
+
+    d = find_domain_by_name(test_ctx->dom_list, "name_0.dom", false);
+    assert_non_null(d);
+
+    assert_false(is_email_from_domain(NULL, NULL));
+    assert_false(is_email_from_domain("hello", NULL));
+    assert_false(is_email_from_domain(NULL, d));
+    assert_false(is_email_from_domain("hello", d));
+    assert_false(is_email_from_domain("hello@hello", d));
+
+    assert_true(is_email_from_domain("hello@name_0.dom", d));
+    assert_true(is_email_from_domain("hello@NaMe_0.DoM", d));
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -1710,6 +1729,8 @@ int main(int argc, const char *argv[])
                                         setup_dom_list, teardown_dom_list),
         cmocka_unit_test_setup_teardown(test_find_domain_by_name_disabled,
                                         setup_dom_list, teardown_dom_list),
+        cmocka_unit_test_setup_teardown(test_is_email_from_domain,
+                                        setup_dom_list, teardown_dom_list),
 
         cmocka_unit_test_setup_teardown(test_sss_names_init,
                                         confdb_test_setup,
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index 
360f70376c472466168d93d45b6c547d51dd18c6..2ef4eacf8905fb2b7edcc3cc76e4a0ade41fe31a
 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -749,3 +749,31 @@ void sss_domain_set_state(struct sss_domain_info *dom,
 {
     dom->state = state;
 }
+
+bool is_email_from_domain(const char *email, struct sss_domain_info *dom)
+{
+    const char *p;
+
+    if (email == NULL || dom == NULL) {
+        return false;
+    }
+
+    p = strchr(email, '@');
+    if (p == NULL) {
+        DEBUG(SSSDBG_TRACE_ALL,
+              "Input [%s] does not look like an email address.\n", email);
+        return false;
+    }
+
+    if (strcasecmp(p+1, dom->name) == 0) {
+        DEBUG(SSSDBG_TRACE_ALL, "Email [%s] is from domain [%s].\n", email,
+                                                                     
dom->name);
+        return true;
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "Email [%s] is not from domain [%s].\n", email,
+                                                                     
dom->name);
+
+    return false;
+}
+
diff --git a/src/util/util.h b/src/util/util.h
index 
8a5caa52c2dc5243c3ae51c5a38fd65a949f4ac4..ddb2158c207631eb2b81c8a8ea9b2a27ff526237
 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -514,6 +514,7 @@ struct sss_domain_info *find_domain_by_sid(struct 
sss_domain_info *domain,
 enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom);
 void sss_domain_set_state(struct sss_domain_info *dom,
                           enum sss_domain_state state);
+bool is_email_from_domain(const char *email, struct sss_domain_info *dom);
 
 struct sss_domain_info*
 sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
-- 
2.1.0

From 72fb323490acc4155227b56be6470965830b7ade Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Mon, 20 Jun 2016 16:30:03 +0200
Subject: [PATCH 08/14] LDAP/IPA: add local email address to aliases

Adding email-addresses from the local domain to the alias names is
strictly not needed by might help to speed up lookups in the NSS
responder.
---
 src/providers/ipa/ipa_s2n_exop.c | 49 ++++++++++++++++++++++++++++++++++++++++
 src/providers/ldap/sdap_utils.c  | 22 ++++++++++++++++++
 2 files changed, 71 insertions(+)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 
84ef83833cdd0eec1142e45667d574d14e4fd6a5..a8c415b4c86ccd3bd3b180c8df835c75420fbb21
 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -1885,6 +1885,49 @@ done:
     return ret;
 }
 
+static errno_t add_emails_to_aliases(struct sysdb_attrs *attrs,
+                                     struct sss_domain_info *dom)
+{
+    int ret;
+    const char **emails;
+    size_t c;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+        return ENOMEM;
+    }
+
+    ret = sysdb_attrs_get_string_array(attrs, SYSDB_USER_EMAIL, tmp_ctx,
+                                       &emails);
+    if (ret == EOK) {
+        for (c = 0; emails[c] != NULL; c++) {
+            if (is_email_from_domain(emails[c], dom)) {
+                ret = sysdb_attrs_add_lc_name_alias_safe(attrs, emails[c]);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_OP_FAILURE,
+                          "Failed to add lower-cased version of email [%s] "
+                          "into the alias list\n", emails[c]);
+                    goto done;
+                }
+            }
+        }
+    } else if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_ALL, "No email addresses available.\n");
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "sysdb_attrs_get_string_array failed, skipping ...\n");
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
 static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
                                     struct req_input *req_input,
                                     struct resp_attrs *attrs,
@@ -2030,6 +2073,12 @@ static errno_t ipa_s2n_save_objects(struct 
sss_domain_info *dom,
                 goto done;
             }
 
+            ret = add_emails_to_aliases(attrs->sysdb_attrs, dom);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "add_emails_to_aliases failed, skipping ...\n");
+            }
+
             if (upn == NULL) {
                 /* We also have to store a fake UPN here, because otherwise the
                  * krb5 child later won't be able to properly construct one as
diff --git a/src/providers/ldap/sdap_utils.c b/src/providers/ldap/sdap_utils.c
index 
696af51d66e279d718e9af142ce5ed871eae7727..a3a9642171ca057be5a59dfae192803b84c501c8
 100644
--- a/src/providers/ldap/sdap_utils.c
+++ b/src/providers/ldap/sdap_utils.c
@@ -87,6 +87,7 @@ sdap_save_all_names(const char *name,
     int i;
     bool lowercase = !dom->case_sensitive;
     bool store_as_fqdn;
+    const char **emails;
 
     switch (entry_type) {
     case SYSDB_MEMBER_USER:
@@ -143,6 +144,27 @@ sdap_save_all_names(const char *name,
 
     }
 
+    ret = sysdb_attrs_get_string_array(ldap_attrs, SYSDB_USER_EMAIL, tmp_ctx,
+                                       &emails);
+    if (ret == EOK) {
+        for (i = 0; emails[i] != NULL; i++) {
+            if (is_email_from_domain(emails[i], dom)) {
+                ret = sysdb_attrs_add_lc_name_alias_safe(attrs, emails[i]);
+                if (ret) {
+                    DEBUG(SSSDBG_OP_FAILURE,
+                          "Failed to add lower-cased version of email [%s] "
+                          "into the alias list\n", emails[i]);
+                    goto done;
+                }
+            }
+        }
+    } else if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_ALL, "No email addresses available.\n");
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "sysdb_attrs_get_string_array failed, skipping ...\n");
+    }
+
     ret = EOK;
 done:
     talloc_free(tmp_ctx);
-- 
2.1.0

From 0f2863a0fb1620bf86ffb828e0c51ead4f807b2a Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Tue, 21 Jun 2016 11:06:19 +0200
Subject: [PATCH 09/14] NSS: continue with UPN/email search if name was not
 found

Currently we only search for UPNs if the domain part of the name was not
know, with Kerberos aliases and email addresses we have to do this even
if the domain name is a know domain.
---
 src/responder/nss/nsssrv_cmd.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 
da007d1e50351eed60d9c99cc60454ae40fc62d9..3054ba4aef52c095546ccfc006c3e9770680dbac
 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -980,6 +980,7 @@ done:
 
 static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
                                       const char *err_msg, void *ptr);
+static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx);
 
 /* search for a user.
  * Returns:
@@ -1051,6 +1052,7 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx 
*dctx)
             /* There are no further domains or this was a
              * fully-qualified user request.
              */
+
             return ENOENT;
         }
 
@@ -1144,8 +1146,6 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx 
*dctx)
                 if (dom) continue;
             }
 
-            DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
-
             /* User not found in ldb -> delete user from memory cache. */
             ret = delete_entry_from_memcache(dctx->domain, name, nctx->rctx,
                                              nctx->pwd_mc_ctx, SSS_MC_PASSWD);
@@ -1163,6 +1163,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx 
*dctx)
                       "Deleting user from memcache failed.\n");
             }
 
+            DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
+
             return ENOENT;
         }
 
@@ -1215,7 +1217,7 @@ static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx)
 {
     int ret;
 
-    if (dctx->domain == NULL) {
+    if (dctx->cmdctx->name_is_upn == false) {
         dctx->domain = dctx->cmdctx->cctx->rctx->domains;
         dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
         dctx->cmdctx->check_next = true;
@@ -1563,6 +1565,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, 
struct cli_ctx *cctx)
 
     rawname = (const char *)body;
     dctx->mc_name = rawname;
+    dctx->rawname = rawname;
 
     DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with input [%s].\n",
           cmd, sss_cmd2str(dctx->cmdctx->cmd), rawname);
@@ -1588,7 +1591,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, 
struct cli_ctx *cctx)
         if (req == NULL) {
             ret = ENOMEM;
         } else {
-            dctx->rawname = rawname;
             tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
             ret = EAGAIN;
         }
@@ -1604,7 +1606,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, 
struct cli_ctx *cctx)
         if (req == NULL) {
             ret = ENOMEM;
         } else {
-            dctx->rawname = rawname;
             tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
             ret = EAGAIN;
         }
@@ -1626,7 +1627,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, 
struct cli_ctx *cctx)
         }
     } 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) {
-- 
2.1.0

From 84813abfb71dc23ad054c42af0e3d2eba1e74e7e Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Wed, 22 Jun 2016 18:21:11 +0200
Subject: [PATCH 10/14] PAM: continue with UPN/email search if name was not
 found

Currently we only search for UPNs if the domain part of the name was not
know, with Kerberos aliases and email addresses we have to do this even
if the domain name is a know domain.
---
 src/responder/pam/pamsrv_cmd.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 
3a35c3f08821aa23051989599d45b8b7b0677da4..1c759f009321cbb322fce624b506ed07f93f997b
 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -924,6 +924,39 @@ static void pam_check_user_dp_callback(uint16_t err_maj, 
uint32_t err_min,
 static int pam_check_user_search(struct pam_auth_req *preq);
 static int pam_check_user_done(struct pam_auth_req *preq, int ret);
 
+static errno_t pam_cmd_assume_upn(struct pam_auth_req *preq)
+{
+    int ret;
+
+    if (!preq->pd->name_is_upn
+            && preq->pd->logon_name != NULL
+            && strchr(preq->pd->logon_name, '@') != NULL) {
+        DEBUG(SSSDBG_TRACE_ALL,
+              "No entry found so far, trying UPN/email lookup with [%s].\n",
+              preq->pd->logon_name);
+        /* 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");
+            return ENOMEM;
+        }
+        preq->pd->name_is_upn = true;
+        preq->pd->domain = NULL;
+
+        ret = pam_check_user_search(preq);
+        if (ret == EOK) {
+            pam_dom_forwarder(preq);
+        }
+        return EOK;
+    }
+
+    return ENOENT;
+}
+
+
 /* TODO: we should probably return some sort of cookie that is set in the
  * PAM_ENVIRONMENT, so that we can save performing some calls and cache
  * data. */
@@ -1220,6 +1253,8 @@ static int pam_forwarder(struct cli_ctx *cctx, int 
pam_cmd)
     ret = pam_check_user_search(preq);
     if (ret == EOK) {
         pam_dom_forwarder(preq);
+    } else if (ret == ENOENT) {
+        ret = pam_cmd_assume_upn(preq);
     }
 
 done:
@@ -1417,6 +1452,8 @@ static void pam_forwarder_cb(struct tevent_req *req)
     ret = pam_check_user_search(preq);
     if (ret == EOK) {
         pam_dom_forwarder(preq);
+    } else if  (ret == ENOENT) {
+        ret = pam_cmd_assume_upn(preq);
     }
 
 done:
@@ -1694,6 +1731,8 @@ static void pam_check_user_dp_callback(uint16_t err_maj, 
uint32_t err_min,
         }
 
         pam_dom_forwarder(preq);
+    } else if (ret == ENOENT) {
+        ret = pam_cmd_assume_upn(preq);
     }
 
     ret = pam_check_user_done(preq, ret);
-- 
2.1.0

From bccc06776316459ec10c8103465664df2433f260 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 22 Jul 2016 17:34:20 +0200
Subject: [PATCH 12/14] PAM: Fix domain for UPN based lookups

Since sysdb_search_user_by_upn() searches the whole cache we have to set
the domain so that it matches the result.
---
 src/responder/pam/pamsrv_cmd.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 
1c759f009321cbb322fce624b506ed07f93f997b..66564f5d301a53dcdb5967f43ef4afdb897e9974
 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1474,6 +1474,7 @@ static int pam_check_user_search(struct pam_auth_req 
*preq)
     static const char *user_attrs[] = SYSDB_PW_ATTRS;
     struct ldb_message *msg;
     struct ldb_result *res;
+    const char *sysdb_name;
 
     while (dom) {
        /* if it is a domainless search, skip domains that require fully
@@ -1533,6 +1534,22 @@ static int pam_check_user_search(struct pam_auth_req 
*preq)
 
         if (preq->pd->name_is_upn) {
             ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
+
+            /* Since sysdb_search_user_by_upn() searches the whole cache we
+             * have to set the domain so that it matches the result. */
+            sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+            if (sysdb_name == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "Cached entry has no name.\n");
+                return EINVAL;
+            }
+            preq->domain = find_domain_by_object_name(get_domains_head(dom),
+                                                      sysdb_name);
+            if (preq->domain == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      "Cannot find matching domain for [%s].\n",
+                      sysdb_name);
+                return EINVAL;
+            }
         } else {
             ret = sysdb_getpwnam_with_views(preq, dom, name, &res);
             if (res->count > 1) {
-- 
2.1.0

From c132fa210341b30ec4586c0bb951ffb1dc6feb94 Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 22 Jul 2016 12:20:33 +0200
Subject: [PATCH 13/14] SDAP: add special handling for IPA Kerberos enterprise
 principal strings

Unfortunately principal aliases with an alternative realm are stored in
IPA as the string representation of an enterprise principal, i.e.
name\@[email protected]. To allow searches with the plain alias
'[email protected]' the returned value is converted before it is saved to
the cache.
---
 src/providers/ldap/sdap_async_users.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/providers/ldap/sdap_async_users.c 
b/src/providers/ldap/sdap_async_users.c
index 
28101a2d8a38f97d09d50a9f7e071a030b4f9719..cccd2506b3e1849101a8a06c39fe6cab263777b6
 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -143,6 +143,8 @@ int sdap_save_user(TALLOC_CTX *memctx,
     char *dom_sid_str = NULL;
     struct sss_domain_info *subdomain;
     size_t c;
+    char *p1;
+    char *p2;
 
     DEBUG(SSSDBG_TRACE_FUNC, "Save user\n");
 
@@ -448,6 +450,21 @@ int sdap_save_user(TALLOC_CTX *memctx,
                 goto done;
             }
 
+            /* Check for IPA Kerberos enterprise principal strings
+             * 'user\@[email protected]' and use '[email protected]' */
+            if ( (p1 = strchr(upn,'\\')) != NULL
+                    && *(p1 + 1) == '@'
+                    && (p2 = strchr(p1 + 2, '@')) != NULL) {
+                *p1 = '\0';
+                *p2 = '\0';
+                upn = talloc_asprintf(tmpctx, "%s%s", upn, p1 + 1);
+                if (upn == NULL) {
+                    DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+                    ret = ENOMEM;
+                    goto done;
+                }
+            }
+
             if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
                 make_realm_upper_case(upn);
             }
-- 
2.1.0

From 3b4deb7deb64136094aa5e70705b467a836dd05b Mon Sep 17 00:00:00 2001
From: Sumit Bose <[email protected]>
Date: Fri, 22 Jul 2016 20:10:42 +0200
Subject: [PATCH 14/14] SDAP: add enterprise principal strings for user
 searches

Unfortunately principal aliases with an alternative realm are stored in
IPA as the string representation of an enterprise principal, i.e.
name\@[email protected]. To be able to lookup the alternative
principal in LDAP properly the UPN search filter is extended to search
for this type of name as well.
---
 src/providers/ldap/ldap_common.h           |  5 +++++
 src/providers/ldap/ldap_id.c               | 10 +++++++--
 src/providers/ldap/sdap_async_initgroups.c |  7 +++++-
 src/providers/ldap/sdap_utils.c            | 28 ++++++++++++++++++++++++
 src/tests/cmocka/test_nested_groups.c      | 34 ++++++++++++++++++++++++++++++
 5 files changed, 81 insertions(+), 3 deletions(-)

diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 
713b1dadf4053339e02db3a34ffac7f778f52a98..ff7ff2854ee55238d0b044b6a95980d08ba3ee84
 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -300,6 +300,11 @@ char *sdap_combine_filters(TALLOC_CTX *mem_ctx,
                            const char *base_filter,
                            const char *extra_filter);
 
+char *get_enterprise_principal_string_filter(TALLOC_CTX *mem_ctx,
+                                             const char *attr_name,
+                                             const char *princ,
+                                             struct dp_option 
*sdap_basic_opts);
+
 char *sdap_get_access_filter(TALLOC_CTX *mem_ctx,
                              const char *base_filter);
 
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 
f27759e45c27f781617f53bc3ca5808a6dcbb601..fe0e219a255640b2934de4338ad796a65f348424
 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -89,6 +89,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
     enum idmap_error_code err;
     char *sid;
     char *user_filter = NULL;
+    char *ep_filter;
 
     req = tevent_req_create(memctx, &state, struct users_get_state);
     if (!req) return NULL;
@@ -131,12 +132,17 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
             if (ret != EOK) {
                 goto done;
             }
+
+            ep_filter = get_enterprise_principal_string_filter(state,
+                                   
ctx->opts->user_map[SDAP_AT_USER_PRINC].name,
+                                   clean_value, ctx->opts->basic);
             /* TODO: Do we have to check the attribute names more carefully? */
-            user_filter = talloc_asprintf(state, "(|(%s=%s)(%s=%s))",
+            user_filter = talloc_asprintf(state, "(|(%s=%s)(%s=%s)%s)",
                                    
ctx->opts->user_map[SDAP_AT_USER_PRINC].name,
                                    clean_value,
                                    
ctx->opts->user_map[SDAP_AT_USER_EMAIL].name,
-                                   clean_value);
+                                   clean_value,
+                                   ep_filter == NULL ? "" : ep_filter);
             talloc_zfree(clean_value);
             if (user_filter == NULL) {
                 DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
diff --git a/src/providers/ldap/sdap_async_initgroups.c 
b/src/providers/ldap/sdap_async_initgroups.c
index 
0a42b18662a8fe12cf048aadfef257b5d9cb48a3..4b90645877983456922a32113743cad65ccfcc9c
 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2683,6 +2683,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX 
*memctx,
     char *clean_name;
     bool use_id_mapping;
     const char *search_attr;
+    char *ep_filter;
 
     DEBUG(SSSDBG_TRACE_ALL, "Retrieving info for initgroups call\n");
 
@@ -2743,13 +2744,17 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX 
*memctx,
                 return NULL;
             }
 
+            ep_filter = get_enterprise_principal_string_filter(state,
+                                 
state->opts->user_map[SDAP_AT_USER_PRINC].name,
+                                 clean_name, state->opts->basic);
             state->user_base_filter =
                     talloc_asprintf(state,
-                                 "(&(|(%s=%s)(%s=%s))(objectclass=%s)",
+                                 "(&(|(%s=%s)(%s=%s)%s)(objectclass=%s)",
                                  
state->opts->user_map[SDAP_AT_USER_PRINC].name,
                                  clean_name,
                                  
state->opts->user_map[SDAP_AT_USER_EMAIL].name,
                                  clean_name,
+                                 ep_filter == NULL ? "" : ep_filter,
                                  state->opts->user_map[SDAP_OC_USER].name);
             if (state->user_base_filter == NULL) {
                 talloc_zfree(req);
diff --git a/src/providers/ldap/sdap_utils.c b/src/providers/ldap/sdap_utils.c
index 
a3a9642171ca057be5a59dfae192803b84c501c8..d224c89aa5f3948e9372cca64058ce1ef8d1cd6c
 100644
--- a/src/providers/ldap/sdap_utils.c
+++ b/src/providers/ldap/sdap_utils.c
@@ -227,3 +227,31 @@ char *sdap_combine_filters(TALLOC_CTX *mem_ctx,
 {
     return sdap_combine_filters_ex(mem_ctx, '&', base_filter, extra_filter);
 }
+
+char *get_enterprise_principal_string_filter(TALLOC_CTX *mem_ctx,
+                                             const char *attr_name,
+                                             const char *princ,
+                                             struct dp_option *sdap_basic_opts)
+{
+    const char *realm;
+    char *p;
+
+    if (attr_name == NULL || princ == NULL || sdap_basic_opts == NULL) {
+        return NULL;
+    }
+
+    realm = dp_opt_get_cstring(sdap_basic_opts, SDAP_KRB5_REALM);
+    if (realm == NULL) {
+        return NULL;
+    }
+
+    p = strchr(princ, '@');
+    if (p == NULL) {
+        return NULL;
+    }
+
+
+    return talloc_asprintf(mem_ctx, "(%s=%.*s\\\\@%s@%s)", attr_name,
+                                                           p - princ, princ,
+                                                           p + 1, realm);
+}
diff --git a/src/tests/cmocka/test_nested_groups.c 
b/src/tests/cmocka/test_nested_groups.c
index 
6af7e1f4393992e7f16d72b86e40664487896ea1..c8e80f29fb65f8f8935fea32cd4bf3e16de7d06f
 100644
--- a/src/tests/cmocka/test_nested_groups.c
+++ b/src/tests/cmocka/test_nested_groups.c
@@ -31,6 +31,7 @@
 #include "providers/ldap/sdap.h"
 #include "providers/ldap/sdap_idmap.h"
 #include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/ldap_opts.h"
 
 #define TESTS_PATH "tp_" BASE_FILE_STEM
 #define TEST_CONF_DB "test_ldap_nested_groups_conf.ldb"
@@ -1242,6 +1243,38 @@ static void nested_group_external_member_test(void 
**state)
                      nested_group.gr_name);
 }
 
+static void test_get_enterprise_principal_string_filter(void **state)
+{
+    int ret;
+    char *ep_filter;
+    struct dp_option *no_krb5_realm_opt = default_basic_opts;
+
+    struct dp_option *krb5_realm_opt;
+
+    ret = dp_copy_defaults(NULL, default_basic_opts, SDAP_OPTS_BASIC,
+                           &krb5_realm_opt);
+    assert_int_equal(ret, EOK);
+
+    ret = dp_opt_set_string(krb5_realm_opt, SDAP_KRB5_REALM, "TEST.DOM");
+    assert_int_equal(ret, EOK);
+
+    ep_filter = get_enterprise_principal_string_filter(NULL, NULL, NULL, NULL);
+    assert_null(ep_filter);
+
+    ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "[email protected]",
+                                                       no_krb5_realm_opt);
+    assert_null(ep_filter);
+
+    ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p",
+                                                       krb5_realm_opt);
+    assert_null(ep_filter);
+
+    ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "[email protected]",
+                                                       krb5_realm_opt);
+    assert_non_null(ep_filter);
+    assert_string_equal(ep_filter, "(aBC=p\\\\@[email protected])");
+    talloc_free(ep_filter);
+}
 
 int main(int argc, const char *argv[])
 {
@@ -1268,6 +1301,7 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(nested_group_external_member_test,
                                         nested_group_external_member_setup,
                                         nested_group_external_member_teardown),
+        cmocka_unit_test(test_get_enterprise_principal_string_filter),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
2.1.0

_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/admin/lists/[email protected]

Reply via email to