>From ab8786126f1467c7b889598961b75c40f8fdafa5 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Fri, 15 Mar 2013 15:27:31 -0400
Subject: [PATCH] ldap: Fallback option for rfc2307 schema

Add option to fallback to fetch local users if rfc2307is being used.
This is useful for cases where people added local users as LDAP members
and rely on these group memberships to be maintained on the local host.

Disabled by default as it violates identity domain separation.

Ticket:
https://fedorahosted.org/sssd/ticket/1020
---
 Makefile.am                                |  1 +
 src/providers/ad/ad_opts.h                 |  1 +
 src/providers/ipa/ipa_opts.h               |  1 +
 src/providers/ldap/ldap_id.c               | 38 ++++++++++++
 src/providers/ldap/ldap_opts.h             |  1 +
 src/providers/ldap/sdap.h                  |  1 +
 src/providers/ldap/sdap_async_initgroups.c | 17 +++++-
 src/providers/ldap/sdap_async_private.h    | 10 +---
 src/providers/ldap/sdap_async_users.c      | 92 ++++++++++++++++++++++++++++++
 src/providers/ldap/sdap_users.h            | 43 ++++++++++++++
 10 files changed, 194 insertions(+), 11 deletions(-)
 create mode 100644 src/providers/ldap/sdap_users.h

diff --git a/Makefile.am b/Makefile.am
index 4cb8b64364fb8e6b8e5f1bebc4a420e7f8573126..ef7617a556c28ff446b0210261e0169803d4f241 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -439,6 +439,7 @@ dist_noinst_HEADERS = \
     src/providers/ldap/sdap_id_op.h \
     src/providers/ldap/ldap_opts.h \
     src/providers/ldap/sdap_range.h \
+    src/providers/ldap/sdap_users.h \
     src/providers/ipa/ipa_common.h \
     src/providers/ipa/ipa_config.h \
     src/providers/ipa/ipa_access.h \
diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
index bcf848c07832ad299b2fe1d5fa71e2b1557663ff..c283f714af690a05148ba0bd2451922716b1ee1e 100644
--- a/src/providers/ad/ad_opts.h
+++ b/src/providers/ad/ad_opts.h
@@ -120,6 +120,7 @@ struct dp_option ad_def_ldap_opts[] = {
     { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "ldap_rfc2307_fallback_to_local_users", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 66ca8ba7d1308b9e91e77f01fc309a0ba6a0927d..6a78ac5b55c362384cd0e42eaff75958afb38f77 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -135,6 +135,7 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "ldap_rfc2307_fallback_to_local_users", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index d24b8aa69d9d24337e69390de7b97354192bcd88..823cf4165e391826aba88382ddad072f4d8f04f9 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -32,6 +32,7 @@
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap_async.h"
 #include "providers/ldap/sdap_idmap.h"
+#include "providers/ldap/sdap_users.h"
 
 /* =Users-Related-Functions-(by-name,by-uid)============================== */
 
@@ -244,6 +245,43 @@ static void users_get_done(struct tevent_req *subreq)
         return;
     }
 
+    if ((ret == ENOENT) &&
+        (state->ctx->opts->schema_type == SDAP_SCHEMA_RFC2307) &&
+        (dp_opt_get_bool(state->ctx->opts->basic,
+                         SDAP_RFC2307_FALLBACK_TO_LOCAL_USERS) == true)) {
+        struct sysdb_attrs **usr_attrs;
+        const char *name = NULL;
+        bool fallback;
+
+        switch (state->filter_type) {
+        case BE_FILTER_NAME:
+            name = state->name;
+            fallback = true;
+            break;
+        case BE_FILTER_IDNUM:
+            uid = (uid_t) strtouint32(state->name, &endptr, 10);
+            if (errno || *endptr || (state->name == endptr)) {
+                tevent_req_error(req, errno ? errno : EINVAL);
+                return;
+            }
+            fallback = true;
+            break;
+        default:
+            fallback = false;
+            break;
+        }
+
+        if (fallback) {
+            ret = sdap_fallback_local_user(state, state->ctx->opts,
+                                           name, uid, &usr_attrs);
+            if (ret == EOK) {
+                ret = sdap_save_user(state, state->sysdb,
+                                     state->ctx->opts, state->domain,
+                                     usr_attrs[0], false, NULL, 0);
+            }
+        }
+    }
+
     if (ret && ret != ENOENT) {
         state->dp_error = dp_error;
         tevent_req_error(req, ret);
diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h
index 5a941a4c6448cbfb574cb430dfb27adbe240c6e9..2ed89f977839e4a7e3ea2c51f04e8db76a883fd9 100644
--- a/src/providers/ldap/ldap_opts.h
+++ b/src/providers/ldap/ldap_opts.h
@@ -112,6 +112,7 @@ struct dp_option default_basic_opts[] = {
     { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "ldap_rfc2307_fallback_to_local_users", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index d143657999f081dc067c43e0e7dedbb4a6ef3489..5bbf27594e7eab81876aea4631c59feb01e56120 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -228,6 +228,7 @@ enum sdap_basic_opt {
     SDAP_IDMAP_DEFAULT_DOMAIN_SID,
     SDAP_AD_MATCHING_RULE_GROUPS,
     SDAP_AD_MATCHING_RULE_INITGROUPS,
+    SDAP_RFC2307_FALLBACK_TO_LOCAL_USERS,
 
     SDAP_OPTS_BASIC /* opts counter */
 };
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index a79ceb59d2417594f2233ff6022b1adc47d7bedd..5e6dde1db4d27a3c72572fd310bfd811c39532c0 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -26,6 +26,7 @@
 #include "providers/ldap/sdap_async_private.h"
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap_idmap.h"
+#include "providers/ldap/sdap_users.h"
 
 /* ==Save-fake-group-list=====================================*/
 static errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
@@ -2667,8 +2668,20 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
             return;
         }
 
-        tevent_req_error(req, ENOENT);
-        return;
+        /* fallback to fetch a local user if required */
+        if ((state->opts->schema_type == SDAP_SCHEMA_RFC2307) &&
+            (dp_opt_get_bool(state->opts->basic,
+                             SDAP_RFC2307_FALLBACK_TO_LOCAL_USERS) == true)) {
+            ret = sdap_fallback_local_user(state, state->opts,
+                                           state->name, -1, &usr_attrs);
+        } else {
+            ret = ENOENT;
+        }
+
+        if (ret != EOK) {
+            tevent_req_error(req, ret);
+            return;
+        }
     } else if (count != 1) {
         DEBUG(2, ("Expected one user entry and got %d\n", count));
         tevent_req_error(req, EINVAL);
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
index 156255b97a67973bb0bba690d18d934a9d18a026..71cb2e53fdbae4a4ab153f421ad6e608936baa80 100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -89,15 +89,6 @@ int sdap_get_tgt_recv(struct tevent_req *req,
                       char **ccname,
                       time_t *expire_time_out);
 
-int sdap_save_user(TALLOC_CTX *memctx,
-                   struct sysdb_ctx *ctx,
-                   struct sdap_options *opts,
-                   struct sss_domain_info *dom,
-                   struct sysdb_attrs *attrs,
-                   bool is_initgr,
-                   char **_usn_value,
-                   time_t now);
-
 int sdap_save_users(TALLOC_CTX *memctx,
                     struct sysdb_ctx *sysdb,
                     struct sss_domain_info *dom,
@@ -120,4 +111,5 @@ errno_t get_sysdb_grouplist(TALLOC_CTX *mem_ctx,
                             struct sss_domain_info *domain,
                             const char *name,
                             char ***grouplist);
+
 #endif /* _SDAP_ASYNC_PRIVATE_H_ */
diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index cd577a7f7f878a8cc7ae8f201b5b15e61a0fe0af..ced0dfd429a9f365b655dbbda2dae8a88b1299b3 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -26,6 +26,8 @@
 #include "providers/ldap/sdap_async_private.h"
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap_idmap.h"
+#include "providers/ldap/sdap_users.h"
+
 
 /* ==Save-User-Entry====================================================== */
 
@@ -701,3 +703,93 @@ int sdap_get_users_recv(struct tevent_req *req,
 
     return EOK;
 }
+
+/* ==Fetch-Fallback-local-user============================================ */
+
+errno_t sdap_fallback_local_user(TALLOC_CTX *memctx,
+                                 struct sdap_options *opts,
+                                 const char *name, uid_t uid,
+                                 struct sysdb_attrs ***reply)
+{
+    struct sysdb_attrs **ua;
+    struct sysdb_attrs *user;
+    struct passwd *pwd;
+    int ret;
+
+    if (name) {
+        pwd = getpwnam(name);
+    } else {
+        pwd = getpwuid(uid);
+    }
+
+    if (!pwd) {
+        return errno ? errno : ENOENT;
+    }
+
+    ua = talloc_array(memctx, struct sysdb_attrs *, 2);
+    if (!ua) {
+        ret = ENOMEM;
+        goto done;
+    }
+    ua[1] = NULL;
+
+    user = sysdb_new_attrs(ua);
+    if (!user) {
+        ret = ENOMEM;
+        goto done;
+    }
+    ua[0] = user;
+
+    ret = sysdb_attrs_add_string(user, SYSDB_NAME, pwd->pw_name);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (pwd->pw_passwd) {
+        ret = sysdb_attrs_add_string(user, SYSDB_PWD, pwd->pw_passwd);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    ret = sysdb_attrs_add_long(user, SYSDB_UIDNUM, (long)pwd->pw_uid);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_long(user, SYSDB_GIDNUM, (long)pwd->pw_gid);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (pwd->pw_gecos) {
+        ret = sysdb_attrs_add_string(user, SYSDB_GECOS, pwd->pw_gecos);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    if (pwd->pw_dir) {
+        ret = sysdb_attrs_add_string(user, SYSDB_HOMEDIR, pwd->pw_dir);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    if (pwd->pw_shell) {
+        ret = sysdb_attrs_add_string(user, SYSDB_SHELL, pwd->pw_shell);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+done:
+    if (ret != EOK) {
+        talloc_free(ua);
+    } else {
+        *reply = ua;
+    }
+
+    return ret;
+}
+
diff --git a/src/providers/ldap/sdap_users.h b/src/providers/ldap/sdap_users.h
new file mode 100644
index 0000000000000000000000000000000000000000..16620a9edbdebe94a8be3846baa491281dcdc5f8
--- /dev/null
+++ b/src/providers/ldap/sdap_users.h
@@ -0,0 +1,43 @@
+/*
+    SSSD
+
+    Async LDAP Helper routines
+
+    Copyright (C) Simo Sorce <ssorce@redhat.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SDAP_USERS_H_
+#define _SDAP_USERS_H_
+
+#include "config.h"
+
+/* shared non-async user functions */
+
+errno_t sdap_fallback_local_user(TALLOC_CTX *memctx,
+                             struct sdap_options *opts,
+                             const char *name, uid_t uid,
+                             struct sysdb_attrs ***reply);
+
+int sdap_save_user(TALLOC_CTX *memctx,
+                   struct sysdb_ctx *ctx,
+                   struct sdap_options *opts,
+                   struct sss_domain_info *dom,
+                   struct sysdb_attrs *attrs,
+                   bool is_initgr,
+                   char **_usn_value,
+                   time_t now);
+
+#endif /* _SDAP_USERS_H_ */
-- 
1.8.1.4

