[PATCHES] idmap_rfc2307 with test

Christof Schmitt christof.schmitt at us.ibm.com
Thu Feb 21 14:41:26 MST 2013


Here is an updated version of the idmap_rfc2307 module. The patches
apply to current master. The first five patches eliminate some code
duplication and allow the reuse of some ad and ldap code.  The last
patch implements a testcase for the new idmap module.

-- 
Christof Schmitt || IBM || SONAS System Development || Tucson, AZ
christof.schmitt at us.ibm.com  ||  +1-520-799-2469  (T/L: 321-2469)
-------------- next part --------------
>From 5e9e5f393450c366914ea6b6ace64bef7c76833c Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:30:48 -0700
Subject: [PATCH 01/11] s3-winbindd: Move common code for LDAP id mapping to idmap_utils

idmap_ad and idmap_ldap use the same helper functions and the same
maximum query size. Move the code to idmap_utils so that it can be
shared by every module issuing LDAP queries.
---
 source3/winbindd/idmap_ad.c    |   43 +++---------------------------------
 source3/winbindd/idmap_ldap.c  |   46 +--------------------------------------
 source3/winbindd/idmap_proto.h |    6 +++++
 source3/winbindd/idmap_util.c  |   41 +++++++++++++++++++++++++++++++++++
 4 files changed, 53 insertions(+), 83 deletions(-)

diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index bfe7d4b..d6f31ba 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -41,7 +41,6 @@
 
 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
 
-#define IDMAP_AD_MAX_IDS 30
 #define CHECK_ALLOC_DONE(mem) do { \
      if (!mem) { \
            DEBUG(0, ("Out of memory!\n")); \
@@ -250,40 +249,6 @@ static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom)
 }
 
 /************************************************************************
- Search up to IDMAP_AD_MAX_IDS entries in maps for a match.
- ***********************************************************************/
-
-static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id)
-{
-	int i;
-
-	for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
-		if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
-			return maps[i];
-		}
-	}
-
-	return NULL;	
-}
-
-/************************************************************************
- Search up to IDMAP_AD_MAX_IDS entries in maps for a match
- ***********************************************************************/
-
-static struct id_map *find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
-{
-	int i;
-
-	for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
-		if (dom_sid_equal(maps[i]->sid, sid)) {
-			return maps[i];
-		}
-	}
-
-	return NULL;	
-}
-
-/************************************************************************
  ***********************************************************************/
 
 static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
@@ -337,7 +302,7 @@ static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map
 
 again:
 	bidx = idx;
-	for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
+	for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) {
 		switch (ids[idx]->xid.type) {
 		case ID_TYPE_UID:     
 			if ( ! u_filter) {
@@ -462,7 +427,7 @@ again:
 			continue;
 		}
 
-		map = find_map_by_id(&ids[bidx], type, id);
+		map = idmap_find_map_by_id(&ids[bidx], type, id);
 		if (!map) {
 			DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
 			continue;
@@ -567,7 +532,7 @@ again:
 	CHECK_ALLOC_DONE(filter);
 
 	bidx = idx;
-	for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
+	for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) {
 
 		ids[idx]->status = ID_UNKNOWN;
 
@@ -617,7 +582,7 @@ again:
 			continue;
 		}
 
-		map = find_map_by_sid(&ids[bidx], &sid);
+		map = idmap_find_map_by_sid(&ids[bidx], &sid);
 		if (!map) {
 			DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
 			continue;
diff --git a/source3/winbindd/idmap_ldap.c b/source3/winbindd/idmap_ldap.c
index 0c58bf4..03872e7 100644
--- a/source3/winbindd/idmap_ldap.c
+++ b/source3/winbindd/idmap_ldap.c
@@ -663,34 +663,10 @@ static NTSTATUS idmap_ldap_new_mapping(struct idmap_domain *dom, struct id_map *
 	return ret;
 }
 
-
-/* max number of ids requested per batch query */
-#define IDMAP_LDAP_MAX_IDS 30
-
 /**********************************
  lookup a set of unix ids.
 **********************************/
 
-/* this function searches up to IDMAP_LDAP_MAX_IDS entries
- * in maps for a match */
-static struct id_map *find_map_by_id(struct id_map **maps,
-				     enum id_type type,
-				     uint32_t id)
-{
-	int i;
-
-	for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) {
-		if (maps[i] == NULL) { /* end of the run */
-			return NULL;
-		}
-		if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
-			return maps[i];
-		}
-	}
-
-	return NULL;
-}
-
 static NTSTATUS idmap_ldap_unixids_to_sids(struct idmap_domain *dom,
 					   struct id_map **ids)
 {
@@ -846,7 +822,7 @@ again:
 		}
 		TALLOC_FREE(tmp);
 
-		map = find_map_by_id(&ids[bidx], type, id);
+		map = idmap_find_map_by_id(&ids[bidx], type, id);
 		if (!map) {
 			DEBUG(2, ("WARNING: couldn't match sid (%s) "
 				  "with requested ids\n", sidstr));
@@ -903,24 +879,6 @@ done:
  lookup a set of sids.
 **********************************/
 
-/* this function searches up to IDMAP_LDAP_MAX_IDS entries
- * in maps for a match */
-static struct id_map *find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
-{
-	int i;
-
-	for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) {
-		if (maps[i] == NULL) { /* end of the run */
-			return NULL;
-		}
-		if (dom_sid_equal(maps[i]->sid, sid)) {
-			return maps[i];
-		}
-	}
-
-	return NULL;
-}
-
 static NTSTATUS idmap_ldap_sids_to_unixids(struct idmap_domain *dom,
 					   struct id_map **ids)
 {
@@ -1053,7 +1011,7 @@ again:
 			continue;
 		}
 
-		map = find_map_by_sid(&ids[bidx], &sid);
+		map = idmap_find_map_by_sid(&ids[bidx], &sid);
 		if (!map) {
 			DEBUG(2, ("WARNING: couldn't find entry sid (%s) "
 				  "in ids", sidstr));
diff --git a/source3/winbindd/idmap_proto.h b/source3/winbindd/idmap_proto.h
index 892b258..a709807 100644
--- a/source3/winbindd/idmap_proto.h
+++ b/source3/winbindd/idmap_proto.h
@@ -54,5 +54,11 @@ NTSTATUS idmap_tdb_init(void);
 NTSTATUS idmap_uid_to_sid(const char *domname, struct dom_sid *sid, uid_t uid);
 NTSTATUS idmap_gid_to_sid(const char *domname, struct dom_sid *sid, gid_t gid);
 bool idmap_unix_id_is_in_range(uint32_t id, struct idmap_domain *dom);
+struct id_map *idmap_find_map_by_id(struct id_map **maps, enum id_type type,
+				    uint32_t id);
+struct id_map *idmap_find_map_by_sid(struct id_map **maps, struct dom_sid *sid);
+
+/* max number of ids requested per LDAP batch query */
+#define IDMAP_LDAP_MAX_IDS 30
 
 #endif /* _WINBINDD_IDMAP_PROTO_H_ */
diff --git a/source3/winbindd/idmap_util.c b/source3/winbindd/idmap_util.c
index 9d9ada7..cbad91d 100644
--- a/source3/winbindd/idmap_util.c
+++ b/source3/winbindd/idmap_util.c
@@ -172,3 +172,44 @@ bool idmap_unix_id_is_in_range(uint32_t id, struct idmap_domain *dom)
 
 	return true;
 }
+
+/**
+ * Helper for unixids_to_sids: find entry by id in mapping array,
+ * search up to IDMAP_AD_MAX_IDS entries
+ */
+struct id_map *idmap_find_map_by_id(struct id_map **maps, enum id_type type,
+				    uint32_t id)
+{
+	int i;
+
+	for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) {
+		if (maps[i] == NULL) { /* end of the run */
+			return NULL;
+		}
+		if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
+			return maps[i];
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * Helper for sids_to_unix_ids: find entry by SID in mapping array,
+ * search up to IDMAP_AD_MAX_IDS entries
+ */
+struct id_map *idmap_find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
+{
+	int i;
+
+	for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) {
+		if (maps[i] == NULL) { /* end of the run */
+			return NULL;
+		}
+		if (dom_sid_equal(maps[i]->sid, sid)) {
+			return maps[i];
+		}
+	}
+
+	return NULL;
+}
-- 
1.7.1


>From b4b140f916af4f076a0fda4a828db45d81ac34f6 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:31:19 -0700
Subject: [PATCH 02/11] s3-winbindd: Move idmap_fetch_secret to idmap_utils.c for reuse

---
 source3/winbindd/idmap_ldap.c  |   24 ------------------------
 source3/winbindd/idmap_proto.h |    2 ++
 source3/winbindd/idmap_util.c  |   25 +++++++++++++++++++++++++
 3 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/source3/winbindd/idmap_ldap.c b/source3/winbindd/idmap_ldap.c
index 03872e7..0520f21 100644
--- a/source3/winbindd/idmap_ldap.c
+++ b/source3/winbindd/idmap_ldap.c
@@ -39,30 +39,6 @@
 #include "smbldap.h"
 #include "passdb/pdb_ldap_schema.h"
 
-static char *idmap_fetch_secret(const char *backend,
-				const char *domain, const char *identity)
-{
-	char *tmp, *ret;
-	int r;
-
-	r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain);
-
-	if (r < 0)
-		return NULL;
-
-	/* make sure the key is case insensitive */
-	if (!strupper_m(tmp)) {
-		SAFE_FREE(tmp);
-		return NULL;
-	}
-
-	ret = secrets_fetch_generic(tmp, identity);
-
-	SAFE_FREE(tmp);
-
-	return ret;
-}
-
 struct idmap_ldap_context {
 	struct smbldap_state *smbldap_state;
 	char *url;
diff --git a/source3/winbindd/idmap_proto.h b/source3/winbindd/idmap_proto.h
index a709807..f7af8ed 100644
--- a/source3/winbindd/idmap_proto.h
+++ b/source3/winbindd/idmap_proto.h
@@ -57,6 +57,8 @@ bool idmap_unix_id_is_in_range(uint32_t id, struct idmap_domain *dom);
 struct id_map *idmap_find_map_by_id(struct id_map **maps, enum id_type type,
 				    uint32_t id);
 struct id_map *idmap_find_map_by_sid(struct id_map **maps, struct dom_sid *sid);
+char *idmap_fetch_secret(const char *backend, const char *domain,
+			 const char *identity);
 
 /* max number of ids requested per LDAP batch query */
 #define IDMAP_LDAP_MAX_IDS 30
diff --git a/source3/winbindd/idmap_util.c b/source3/winbindd/idmap_util.c
index cbad91d..a068298 100644
--- a/source3/winbindd/idmap_util.c
+++ b/source3/winbindd/idmap_util.c
@@ -24,6 +24,7 @@
 #include "idmap.h"
 #include "idmap_cache.h"
 #include "../libcli/security/security.h"
+#include "secrets.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_IDMAP
@@ -213,3 +214,27 @@ struct id_map *idmap_find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
 
 	return NULL;
 }
+
+char *idmap_fetch_secret(const char *backend, const char *domain,
+			 const char *identity)
+{
+	char *tmp, *ret;
+	int r;
+
+	r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain);
+
+	if (r < 0)
+		return NULL;
+
+	/* make sure the key is case insensitive */
+	if (!strupper_m(tmp)) {
+		SAFE_FREE(tmp);
+		return NULL;
+	}
+
+	ret = secrets_fetch_generic(tmp, identity);
+
+	SAFE_FREE(tmp);
+
+	return ret;
+}
-- 
1.7.1


>From 4ad90a5bbd71d3fcc4714013e657a39fbdbc3100 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:31:28 -0700
Subject: [PATCH 03/11] s3-winbindd: Move code for verifying ADS connection to common helper function

---
 source3/winbindd/idmap_ad.c       |   26 +----------------
 source3/winbindd/winbindd_ads.c   |   54 ++++++++++++++++++++++--------------
 source3/winbindd/winbindd_proto.h |    6 ++++
 3 files changed, 41 insertions(+), 45 deletions(-)

diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index d6f31ba..0e00a34 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -39,8 +39,6 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_IDMAP
 
-#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
-
 #define CHECK_ALLOC_DONE(mem) do { \
      if (!mem) { \
            DEBUG(0, ("Out of memory!\n")); \
@@ -74,29 +72,9 @@ static ADS_STATUS ad_idmap_cached_connection_internal(struct idmap_domain *dom)
 
 	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
 
+	ads_cached_connection_reuse(&ctx->ads);
 	if (ctx->ads != NULL) {
-
-		time_t expire;
-		time_t now = time(NULL);
-
-		ads = ctx->ads;
-
-		expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
-
-		/* check for a valid structure */
-		DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
-			  (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
-
-		if ( ads->config.realm && (expire > time(NULL))) {
-			return ADS_SUCCESS;
-		} else {
-			/* we own this ADS_STRUCT so make sure it goes away */
-			DEBUG(7,("Deleting expired krb5 credential cache\n"));
-			ads->is_mine = True;
-			ads_destroy( &ads );
-			ads_kdestroy(WINBIND_CCACHE_NAME);
-			ctx->ads = NULL;
-		}
+		return ADS_SUCCESS;
 	}
 
 	/* we don't want this to affect the users ccache */
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
index 921d408..ea6170d 100644
--- a/source3/winbindd/winbindd_ads.c
+++ b/source3/winbindd/winbindd_ads.c
@@ -40,43 +40,55 @@
 
 extern struct winbindd_methods reconnect_methods;
 
-/*
-  return our ads connections structure for a domain. We keep the connection
-  open to make things faster
-*/
-static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
+/**
+ * Check if cached connection can be reused. If the connection cannot
+ * be reused the ADS_STRUCT is freed and the pointer is set to NULL.
+ */
+void ads_cached_connection_reuse(ADS_STRUCT **adsp)
 {
-	ADS_STRUCT *ads;
-	ADS_STATUS status;
-	fstring dc_name;
-	struct sockaddr_storage dc_ss;
-
-	DEBUG(10,("ads_cached_connection\n"));
 
-	if (domain->private_data) {
+	ADS_STRUCT *ads = *adsp;
 
+	if (ads != NULL) {
 		time_t expire;
 		time_t now = time(NULL);
 
-		/* check for a valid structure */
-		ads = (ADS_STRUCT *)domain->private_data;
-
 		expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
 
-		DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
-			  (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
+		DEBUG(7, ("Current tickets expire in %d seconds (at %d, time "
+			  "is now %d)\n", (uint32)expire - (uint32)now,
+			  (uint32) expire, (uint32) now));
 
 		if ( ads->config.realm && (expire > now)) {
-			return ads;
+			return;
 		} else {
 			/* we own this ADS_STRUCT so make sure it goes away */
 			DEBUG(7,("Deleting expired krb5 credential cache\n"));
 			ads->is_mine = True;
 			ads_destroy( &ads );
-			ads_kdestroy("MEMORY:winbind_ccache");
-			domain->private_data = NULL;
+			ads_kdestroy(WINBIND_CCACHE_NAME);
+			*adsp = NULL;
 		}
 	}
+}
+
+/*
+  return our ads connections structure for a domain. We keep the connection
+  open to make things faster
+*/
+static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
+{
+	ADS_STRUCT *ads;
+	ADS_STATUS status;
+	fstring dc_name;
+	struct sockaddr_storage dc_ss;
+
+	DEBUG(10,("ads_cached_connection\n"));
+	ads_cached_connection_reuse((ADS_STRUCT **)&domain->private_data);
+
+	if (domain->private_data) {
+		return (ADS_STRUCT *)domain->private_data;
+	}
 
 	ads = ads_init(domain->alt_name, domain->name, NULL);
 	if (!ads) {
@@ -1282,7 +1294,7 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
 			ads = (ADS_STRUCT *)domain->private_data;
 			ads->is_mine = True;
 			ads_destroy(&ads);
-			ads_kdestroy("MEMORY:winbind_ccache");
+			ads_kdestroy(WINBIND_CCACHE_NAME);
 			domain->private_data = NULL;
 		}
 	}
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 00b7c32..0d75791 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -23,6 +23,8 @@
 #ifndef _WINBINDD_PROTO_H_
 #define _WINBINDD_PROTO_H_
 
+#include "ads.h"
+
 /* The following definitions come from winbindd/winbindd.c  */
 struct messaging_context *winbind_messaging_context(void);
 void request_error(struct winbindd_cli_state *state);
@@ -890,4 +892,8 @@ NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
 				 struct rpc_pipe_client **samr_pipe,
 				 struct policy_handle *samr_domain_hnd);
 
+/* The following definitions come from winbindd/winbindd_ads.c  */
+#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
+void ads_cached_connection_reuse(ADS_STRUCT **ads);
+
 #endif /*  _WINBINDD_PROTO_H_  */
-- 
1.7.1


>From 6d7460df31d88762a2f110ba053d0c992c005d80 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:31:37 -0700
Subject: [PATCH 04/11] s3-winbindd: Use common helper function for connecting to ADS

---
 source3/winbindd/idmap_ad.c       |   41 ++-----------
 source3/winbindd/winbindd_ads.c   |  125 ++++++++++++++++++++++---------------
 source3/winbindd/winbindd_proto.h |    7 ++
 3 files changed, 87 insertions(+), 86 deletions(-)

diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index 0e00a34..5b9c377 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -58,13 +58,8 @@ struct idmap_ad_context {
 
 static ADS_STATUS ad_idmap_cached_connection_internal(struct idmap_domain *dom)
 {
-	ADS_STRUCT *ads;
-	ADS_STATUS status;
-	fstring dc_name;
-	struct sockaddr_storage dc_ip;
 	struct idmap_ad_context *ctx;
-	char *ldap_server = NULL;
-	char *realm = NULL;
+	char *ldap_server, *realm, *password;
 	struct winbindd_domain *wb_dom;
 
 	DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
@@ -77,9 +72,6 @@ static ADS_STATUS ad_idmap_cached_connection_internal(struct idmap_domain *dom)
 		return ADS_SUCCESS;
 	}
 
-	/* we don't want this to affect the users ccache */
-	setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
-
 	/*
 	 * At this point we only have the NetBIOS domain name.
 	 * Check if we can get server nam and realm from SAF cache
@@ -99,35 +91,12 @@ static ADS_STATUS ad_idmap_cached_connection_internal(struct idmap_domain *dom)
 		realm = wb_dom->alt_name;
 	}
 
-	if ( (ads = ads_init(realm, dom->name, ldap_server)) == NULL ) {
-		DEBUG(1,("ads_init failed\n"));
-		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-	}
-
 	/* the machine acct password might have change - fetch it every time */
-	SAFE_FREE(ads->auth.password);
-	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
-
-	SAFE_FREE(ads->auth.realm);
-	ads->auth.realm = SMB_STRDUP(lp_realm());
-
-	/* setup server affinity */
-
-	get_dc_name(dom->name, realm, dc_name, &dc_ip );
-
-	status = ads_connect(ads);
-	if (!ADS_ERR_OK(status)) {
-		DEBUG(1, ("ad_idmap_cached_connection_internal: failed to "
-			  "connect to AD\n"));
-		ads_destroy(&ads);
-		return status;
-	}
-
-	ads->is_mine = False;
-
-	ctx->ads = ads;
+	password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+	realm = SMB_STRDUP(lp_realm());
 
-	return ADS_SUCCESS;
+	return ads_cached_connection_connect(&ctx->ads, realm, dom->name,
+					     ldap_server, password, realm, 0);
 }
 
 /************************************************************************
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
index ea6170d..ffc62ba 100644
--- a/source3/winbindd/winbindd_ads.c
+++ b/source3/winbindd/winbindd_ads.c
@@ -72,16 +72,73 @@ void ads_cached_connection_reuse(ADS_STRUCT **adsp)
 	}
 }
 
+ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
+					 const char *dom_name_alt,
+					 const char *dom_name,
+					 const char *ldap_server,
+					 char *password,
+					 char *realm,
+					 time_t renewable)
+{
+	ADS_STRUCT *ads;
+	ADS_STATUS status;
+	struct sockaddr_storage dc_ss;
+	fstring dc_name;
+
+	/* we don't want this to affect the users ccache */
+	setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
+
+	ads = ads_init(dom_name_alt, dom_name, ldap_server);
+	if (!ads) {
+		DEBUG(1,("ads_init for domain %s failed\n", dom_name));
+		return ADS_ERROR(LDAP_NO_MEMORY);
+	}
+
+	SAFE_FREE(ads->auth.password);
+	SAFE_FREE(ads->auth.realm);
+
+	ads->auth.renewable = renewable;
+	ads->auth.password = password;
+	ads->auth.realm = realm;
+
+	ads->auth.realm = SMB_STRDUP(realm);
+	if (!strupper_m(ads->auth.realm)) {
+		ads_destroy(&ads);
+		return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+	}
+
+	/* Setup the server affinity cache.  We don't reaally care
+	   about the name.  Just setup affinity and the KRB5_CONFIG
+	   file. */
+	get_dc_name(ads->server.workgroup, ads->server.realm, dc_name, &dc_ss);
+
+	status = ads_connect(ads);
+	if (!ADS_ERR_OK(status)) {
+		DEBUG(1,("ads_connect for domain %s failed: %s\n",
+			 dom_name, ads_errstr(status)));
+		ads_destroy(&ads);
+		return status;
+	}
+
+	/* set the flag that says we don't own the memory even
+	   though we do so that ads_destroy() won't destroy the
+	   structure we pass back by reference */
+
+	ads->is_mine = False;
+
+	*adsp = ads;
+
+	return status;
+}
+
 /*
   return our ads connections structure for a domain. We keep the connection
   open to make things faster
 */
 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
 {
-	ADS_STRUCT *ads;
 	ADS_STATUS status;
-	fstring dc_name;
-	struct sockaddr_storage dc_ss;
+	char *password, *realm;
 
 	DEBUG(10,("ads_cached_connection\n"));
 	ads_cached_connection_reuse((ADS_STRUCT **)&domain->private_data);
@@ -90,37 +147,22 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
 		return (ADS_STRUCT *)domain->private_data;
 	}
 
-	ads = ads_init(domain->alt_name, domain->name, NULL);
-	if (!ads) {
-		DEBUG(1,("ads_init for domain %s failed\n", domain->name));
-		return NULL;
-	}
-
-	/* we don't want ads operations to affect the default ccache */
-	ads->auth.ccache_name = SMB_STRDUP("MEMORY:winbind_ccache");
-
 	/* the machine acct password might have change - fetch it every time */
 
-	SAFE_FREE(ads->auth.password);
-	SAFE_FREE(ads->auth.realm);
-
 	if ( IS_DC ) {
 
-		if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) {
-			ads_destroy( &ads );
-			return NULL;
-		}
-		ads->auth.realm = SMB_STRDUP( ads->server.realm );
-		if (!strupper_m( ads->auth.realm )) {
-			ads_destroy( &ads );
+		if ( !pdb_get_trusteddom_pw( domain->name, &password, NULL,
+					     NULL ) ) {
 			return NULL;
 		}
+		realm = NULL;
 	}
 	else {
 		struct winbindd_domain *our_domain = domain;
 
-		ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
 
+		password = secrets_fetch_machine_password(lp_workgroup(), NULL,
+							  NULL);
 		/* always give preference to the alt_name in our
 		   primary domain if possible */
 
@@ -128,30 +170,21 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
 			our_domain = find_our_domain();
 
 		if ( our_domain->alt_name[0] != '\0' ) {
-			ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
-			if (!strupper_m( ads->auth.realm )) {
-				ads_destroy( &ads );
-				return NULL;
-			}
+			realm = SMB_STRDUP( our_domain->alt_name );
 		}
 		else
-			ads->auth.realm = SMB_STRDUP( lp_realm() );
+			realm = SMB_STRDUP( lp_realm() );
 	}
 
-	ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
+	status = ads_cached_connection_connect(
+					(ADS_STRUCT **)&domain->private_data,
+					domain->alt_name,
+					domain->name, NULL,
+					password, realm,
+					WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
 
-	/* Setup the server affinity cache.  We don't reaally care
-	   about the name.  Just setup affinity and the KRB5_CONFIG
-	   file. */
-
-	get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
-
-	status = ads_connect(ads);
-	if (!ADS_ERR_OK(status) || !ads->config.realm) {
-		DEBUG(1,("ads_connect for domain %s failed: %s\n",
-			 domain->name, ads_errstr(status)));
-		ads_destroy(&ads);
 
+	if (!ADS_ERR_OK(status)) {
 		/* if we get ECONNREFUSED then it might be a NT4
                    server, fall back to MSRPC */
 		if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
@@ -163,17 +196,9 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
 		return NULL;
 	}
 
-	/* set the flag that says we don't own the memory even
-	   though we do so that ads_destroy() won't destroy the
-	   structure we pass back by reference */
-
-	ads->is_mine = False;
-
-	domain->private_data = (void *)ads;
-	return ads;
+	return (ADS_STRUCT *)domain->private_data;
 }
 
-
 /* Query display info for a realm. This is the basic user list fn */
 static NTSTATUS query_user_list(struct winbindd_domain *domain,
 			       TALLOC_CTX *mem_ctx,
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 0d75791..8bd7a39 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -895,5 +895,12 @@ NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
 /* The following definitions come from winbindd/winbindd_ads.c  */
 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
 void ads_cached_connection_reuse(ADS_STRUCT **ads);
+ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
+					 const char *dom_name_alt,
+					 const char *dom_name,
+					 const char *ldap_server,
+					 char *password,
+					 char *realm,
+					 time_t renewable);
 
 #endif /*  _WINBINDD_PROTO_H_  */
-- 
1.7.1


>From dad6b8ab1e518246e31bfc547089690f85c97971 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:31:41 -0700
Subject: [PATCH 05/11] s3-winbindd: Move connection to AD server from idmap_ad

Having this in a common place allows reuse by other idmap modules.
---
 source3/winbindd/idmap_ad.c       |   49 +++------------------------------
 source3/winbindd/winbindd_ads.c   |   55 +++++++++++++++++++++++++++++++-----
 source3/winbindd/winbindd_proto.h |   10 +------
 3 files changed, 52 insertions(+), 62 deletions(-)

diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index 5b9c377..5bafa90 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -56,58 +56,17 @@ struct idmap_ad_context {
 /************************************************************************
  ***********************************************************************/
 
-static ADS_STATUS ad_idmap_cached_connection_internal(struct idmap_domain *dom)
+static ADS_STATUS ad_idmap_cached_connection(struct idmap_domain *dom)
 {
-	struct idmap_ad_context *ctx;
-	char *ldap_server, *realm, *password;
-	struct winbindd_domain *wb_dom;
+	ADS_STATUS status;
+	struct idmap_ad_context * ctx;
 
 	DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
 		   dom->name));
 
 	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
 
-	ads_cached_connection_reuse(&ctx->ads);
-	if (ctx->ads != NULL) {
-		return ADS_SUCCESS;
-	}
-
-	/*
-	 * At this point we only have the NetBIOS domain name.
-	 * Check if we can get server nam and realm from SAF cache
-	 * and the domain list.
-	 */
-	ldap_server = saf_fetch(dom->name);
-	DEBUG(10, ("ldap_server from saf cache: '%s'\n", ldap_server?ldap_server:""));
-
-	wb_dom = find_domain_from_name_noinit(dom->name);
-	if (wb_dom == NULL) {
-		DEBUG(10, ("find_domain_from_name_noinit did not find domain '%s'\n",
-			   dom->name));
-		realm = NULL;
-	} else {
-		DEBUG(10, ("find_domain_from_name_noinit found realm '%s' for "
-			  " domain '%s'\n", wb_dom->alt_name, dom->name));
-		realm = wb_dom->alt_name;
-	}
-
-	/* the machine acct password might have change - fetch it every time */
-	password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
-	realm = SMB_STRDUP(lp_realm());
-
-	return ads_cached_connection_connect(&ctx->ads, realm, dom->name,
-					     ldap_server, password, realm, 0);
-}
-
-/************************************************************************
- ***********************************************************************/
-
-static ADS_STATUS ad_idmap_cached_connection(struct idmap_domain *dom)
-{
-	ADS_STATUS status;
-	struct idmap_ad_context * ctx;
-
-	status = ad_idmap_cached_connection_internal(dom);
+	status = ads_idmap_cached_connection(&ctx->ads, dom->name);
 	if (!ADS_ERR_OK(status)) {
 		return status;
 	}
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
index ffc62ba..2dcd07d 100644
--- a/source3/winbindd/winbindd_ads.c
+++ b/source3/winbindd/winbindd_ads.c
@@ -40,11 +40,13 @@
 
 extern struct winbindd_methods reconnect_methods;
 
+#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
+
 /**
  * Check if cached connection can be reused. If the connection cannot
  * be reused the ADS_STRUCT is freed and the pointer is set to NULL.
  */
-void ads_cached_connection_reuse(ADS_STRUCT **adsp)
+static void ads_cached_connection_reuse(ADS_STRUCT **adsp)
 {
 
 	ADS_STRUCT *ads = *adsp;
@@ -72,13 +74,13 @@ void ads_cached_connection_reuse(ADS_STRUCT **adsp)
 	}
 }
 
-ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
-					 const char *dom_name_alt,
-					 const char *dom_name,
-					 const char *ldap_server,
-					 char *password,
-					 char *realm,
-					 time_t renewable)
+static ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
+						const char *dom_name_alt,
+						const char *dom_name,
+						const char *ldap_server,
+						char *password,
+						char *realm,
+						time_t renewable)
 {
 	ADS_STRUCT *ads;
 	ADS_STATUS status;
@@ -131,6 +133,43 @@ ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
 	return status;
 }
 
+ADS_STATUS ads_idmap_cached_connection(ADS_STRUCT **adsp, const char *dom_name)
+{
+	char *ldap_server, *realm, *password;
+	struct winbindd_domain *wb_dom;
+
+	ads_cached_connection_reuse(adsp);
+	if (*adsp != NULL) {
+		return ADS_SUCCESS;
+	}
+
+	/*
+	 * At this point we only have the NetBIOS domain name.
+	 * Check if we can get server nam and realm from SAF cache
+	 * and the domain list.
+	 */
+	ldap_server = saf_fetch(dom_name);
+	DEBUG(10, ("ldap_server from saf cache: '%s'\n",
+		   ldap_server ? ldap_server : ""));
+
+	wb_dom = find_domain_from_name_noinit(dom_name);
+	if (wb_dom == NULL) {
+		DEBUG(10, ("could not find domain '%s'\n", dom_name));
+		realm = NULL;
+	} else {
+		DEBUG(10, ("find_domain_from_name_noinit found realm '%s' for "
+			  " domain '%s'\n", wb_dom->alt_name, dom_name));
+		realm = wb_dom->alt_name;
+	}
+
+	/* the machine acct password might have change - fetch it every time */
+	password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+	realm = SMB_STRDUP(lp_realm());
+
+	return ads_cached_connection_connect(adsp, realm, dom_name, ldap_server,
+					     password, realm, 0);
+}
+
 /*
   return our ads connections structure for a domain. We keep the connection
   open to make things faster
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 8bd7a39..b07f303 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -893,14 +893,6 @@ NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
 				 struct policy_handle *samr_domain_hnd);
 
 /* The following definitions come from winbindd/winbindd_ads.c  */
-#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
-void ads_cached_connection_reuse(ADS_STRUCT **ads);
-ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
-					 const char *dom_name_alt,
-					 const char *dom_name,
-					 const char *ldap_server,
-					 char *password,
-					 char *realm,
-					 time_t renewable);
+ADS_STATUS ads_idmap_cached_connection(ADS_STRUCT **adsp, const char *dom_name);
 
 #endif /*  _WINBINDD_PROTO_H_  */
-- 
1.7.1


>From 87fbb20f504235d691280d88aac053baee9623c3 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:32:37 -0700
Subject: [PATCH 06/11] s3-winbindd: Add new module idmap_rfc2307

This module allows querying id mappings from LDAP servers as described
in RFC 2307. The LDAP records can be queried from an Active Directory
Server or from a stand-alone LDAP server.
---
 source3/Makefile.in              |    4 +
 source3/configure.in             |    1 +
 source3/winbindd/idmap_rfc2307.c |  870 ++++++++++++++++++++++++++++++++++++++
 source3/winbindd/wscript_build   |   10 +
 source3/wscript                  |    2 +-
 5 files changed, 886 insertions(+), 1 deletions(-)
 create mode 100644 source3/winbindd/idmap_rfc2307.c

diff --git a/source3/Makefile.in b/source3/Makefile.in
index 90f12b7..194cba7 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -2694,6 +2694,10 @@ bin/ad. at SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ad.o
 	@echo "Building plugin $@"
 	@$(SHLD_MODULE) winbindd/idmap_ad.o
 
+bin/rfc2307. at SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_rfc2307.o
+	@echo "Building plugin $@"
+	@$(SHLD_MODULE) winbindd/idmap_rfc2307.o
+
 bin/hash. at SHLIBEXT@: $(BINARY_PREREQS) $(IDMAP_HASH_OBJ)
 	@echo "Building plugin $@"
 	@$(SHLD_MODULE) $(IDMAP_HASH_OBJ)
diff --git a/source3/configure.in b/source3/configure.in
index 56c9190..59eb087 100644
--- a/source3/configure.in
+++ b/source3/configure.in
@@ -6382,6 +6382,7 @@ SMB_MODULE(idmap_nss, winbindd/idmap_nss.o, "bin/nss.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_rid, winbindd/idmap_rid.o, "bin/rid.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_autorid, winbindd/idmap_autorid.o, "bin/autorid.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_ad, winbindd/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP)
+SMB_MODULE(idmap_rfc2307, winbindd/idmap_rfc2307.o, "bin/rfc2307.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_hash, \$(IDMAP_HASH_OBJ), "bin/hash.$SHLIBEXT", IDMAP)
 SMB_SUBSYSTEM(IDMAP, winbindd/idmap.o)
 
diff --git a/source3/winbindd/idmap_rfc2307.c b/source3/winbindd/idmap_rfc2307.c
new file mode 100644
index 0000000..2b7a593
--- /dev/null
+++ b/source3/winbindd/idmap_rfc2307.c
@@ -0,0 +1,870 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Id mapping using LDAP records as defined in RFC 2307
+ *
+ * The SID<->uid/gid mapping is performed in two steps: 1) Query the
+ * AD server for the name<->sid mapping. 2) Query an LDAP server
+ * according to RFC 2307 for the name<->uid/gid mapping.
+ *
+ * Copyright (C) Christof Schmitt 2012,2013
+ *
+ * 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/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "ads.h"
+#include "idmap.h"
+#include "smbldap.h"
+#include "nsswitch/winbind_client.h"
+#include "lib/winbind_util.h"
+
+/*
+ * Config and connection info per domain.
+ */
+struct idmap_rfc2307_context {
+	const char *bind_path_user;
+	const char *bind_path_group;
+	const char *ldap_domain;
+	bool cn_realm;
+	bool user_cn;
+	const char *realm;
+
+	/*
+	 * Pointer to ldap struct in ads or smbldap_state, has to be
+	 * updated after connecting to server
+	 */
+	LDAP *ldap;
+
+	/* Optional function to check connection to server */
+	NTSTATUS (*check_connection)(struct idmap_domain *dom);
+
+	/* Issue ldap query */
+	NTSTATUS (*search)(struct idmap_rfc2307_context *ctx,
+			   const char *bind_path, const char *expr,
+			   const char **attrs, LDAPMessage **res);
+
+	/* Access to LDAP in AD server */
+	ADS_STRUCT *ads;
+
+	/* Access to stand-alone LDAP server */
+	struct smbldap_state *smbldap_state;
+};
+
+/*
+ * backend functions for LDAP queries through ADS
+ */
+
+static NTSTATUS idmap_rfc2307_ads_check_connection(struct idmap_domain *dom)
+{
+	struct idmap_rfc2307_context *ctx;
+	const char *dom_name = dom->name;
+	ADS_STATUS status;
+
+	DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
+		   dom->name));
+
+	ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
+	dom_name = ctx->ldap_domain ? ctx->ldap_domain : dom->name;
+
+	status = ads_idmap_cached_connection(&ctx->ads, dom_name);
+	if (ADS_ERR_OK(status)) {
+		ctx->ldap = ctx->ads->ldap.ld;
+		if (ctx->cn_realm) {
+			ctx->realm = ctx->ads->server.realm;
+		}
+	} else {
+		DEBUG(1, ("Could not connect to domain %s: %s\n", dom->name,
+			  ads_errstr(status)));
+	}
+
+	return ads_ntstatus(status);
+}
+
+static NTSTATUS idmap_rfc2307_ads_search(struct idmap_rfc2307_context *ctx,
+					 const char *bind_path,
+					 const char *expr,
+					 const char **attrs,
+					 LDAPMessage **result)
+{
+	ADS_STATUS status;
+
+	status = ads_do_search_retry(ctx->ads, bind_path,
+				     LDAP_SCOPE_SUBTREE, expr, attrs, result);
+	return ads_ntstatus(status);
+}
+
+static NTSTATUS idmap_rfc2307_init_ads(struct idmap_rfc2307_context *ctx,
+				       const char *cfg_opt)
+{
+	const char *ldap_domain;
+
+	ctx->search = idmap_rfc2307_ads_search;
+	ctx->check_connection = idmap_rfc2307_ads_check_connection;
+
+	ldap_domain = lp_parm_const_string(-1, cfg_opt, "ldap_domain",
+					      NULL);
+	if (ldap_domain) {
+		ctx->ldap_domain = talloc_strdup(ctx, ldap_domain);
+		if (ctx->ldap_domain == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+
+	return NT_STATUS_OK;
+}
+
+/*
+ * backend function for LDAP queries through stand-alone LDAP server
+ */
+
+static NTSTATUS idmap_rfc2307_ldap_search(struct idmap_rfc2307_context *ctx,
+					  const char *bind_path,
+					  const char *expr,
+					  const char **attrs,
+					  LDAPMessage **result)
+{
+	int ret;
+
+	ret = smbldap_search(ctx->smbldap_state, bind_path, LDAP_SCOPE_SUBTREE,
+			     expr, attrs, 0, result);
+	ctx->ldap = ctx->smbldap_state->ldap_struct;
+
+	if (ret == LDAP_SUCCESS) {
+		return NT_STATUS_OK;
+	}
+
+	return NT_STATUS_LDAP(ret);
+}
+
+static bool idmap_rfc2307_get_uint32(LDAP *ldap, LDAPMessage *entry,
+				     const char *field, uint32 *value)
+{
+	bool b;
+	char str[20];
+
+	b = smbldap_get_single_attribute(ldap, entry, field, str, sizeof(str));
+
+	if (b) {
+		*value = atoi(str);
+	}
+
+	return b;
+}
+
+static NTSTATUS idmap_rfc2307_init_ldap(struct idmap_rfc2307_context *ctx,
+					struct idmap_domain *dom,
+					const char *config_option)
+{
+	NTSTATUS ret;
+	char *url;
+	char *secret = NULL;
+	const char *ldap_url, *user_dn, *ldap_realm;
+	TALLOC_CTX *mem_ctx = ctx;
+
+	ldap_url = lp_parm_const_string(-1, config_option, "ldap_url", NULL);
+	if (!ldap_url) {
+		DEBUG(1, ("ERROR: missing idmap ldap url\n"));
+		return NT_STATUS_UNSUCCESSFUL;
+	}
+
+	url = talloc_strdup(talloc_tos(), ldap_url);
+
+	user_dn = lp_parm_const_string(-1, config_option, "ldap_user_dn", NULL);
+	if (user_dn) {
+		secret = idmap_fetch_secret("ldap", dom->name, user_dn);
+		if (!secret) {
+			ret = NT_STATUS_ACCESS_DENIED;
+			goto done;
+		}
+	}
+
+	/* assume anonymous if we don't have a specified user */
+	ret = smbldap_init(mem_ctx, winbind_event_context(), url,
+			   (user_dn == NULL), user_dn, secret,
+			   &ctx->smbldap_state);
+	SAFE_FREE(secret);
+	if (!NT_STATUS_IS_OK(ret)) {
+		DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", url));
+		goto done;
+	}
+
+	ctx->search = idmap_rfc2307_ldap_search;
+
+	if (ctx->cn_realm) {
+		ldap_realm = lp_parm_const_string(-1, config_option,
+						  "ldap_realm", NULL);
+		if (!ldap_realm) {
+			DEBUG(1, ("ERROR: cn_realm set, "
+				  "but ldap_realm is missing\n"));
+			ret = NT_STATUS_UNSUCCESSFUL;
+			goto done;
+		}
+		ctx->realm = talloc_strdup(mem_ctx, ldap_realm);
+		if (!ctx->realm) {
+			ret = NT_STATUS_NO_MEMORY;
+		}
+	}
+
+done:
+	talloc_free(url);
+	return ret;
+}
+
+/*
+ * common code for stand-alone LDAP and ADS
+ */
+
+static void idmap_rfc2307_map_sid_results(struct idmap_rfc2307_context *ctx,
+					  TALLOC_CTX *mem_ctx,
+					  struct id_map **ids,
+					  LDAPMessage *result,
+					  const char *dom_name,
+					  const char **attrs, int type)
+{
+	int count, i;
+	LDAPMessage *entry;
+
+	count = ldap_count_entries(ctx->ldap, result);
+
+	for (i = 0; i < count; i++) {
+		char *name;
+		enum lsa_SidType lsa_type;
+		struct id_map *map;
+		uint32_t id;
+		bool b;
+
+		if (i == 0) {
+			entry = ldap_first_entry(ctx->ldap, result);
+		} else {
+			entry = ldap_next_entry(ctx->ldap, result);
+		}
+		if (!entry) {
+			DEBUG(2, ("Unable to fetch entry.\n"));
+			break;
+		}
+
+		name = smbldap_talloc_single_attribute(ctx->ldap, entry,
+						       attrs[0], mem_ctx);
+		if (!name) {
+			DEBUG(1, ("Could not get user name\n"));
+			continue;
+		}
+
+		b = idmap_rfc2307_get_uint32(ctx->ldap, entry, attrs[1], &id);
+		if (!b) {
+			DEBUG(1, ("Could not pull id for record %s\n", name));
+			continue;
+		}
+
+		map = idmap_find_map_by_id(ids, type, id);
+		if (!map) {
+			DEBUG(1, ("Could not find id %d, name %s\n", id, name));
+			continue;
+		}
+
+		if (ctx->cn_realm) {
+			/* Strip @realm from user or group name */
+			char *delim;
+
+			delim = strchr(name, '@');
+			if (delim) {
+				*delim = '\0';
+			}
+		}
+
+		/* by default calls to winbindd are disabled
+		   the following call will not recurse so this is safe */
+		(void)winbind_on();
+		/* Lookup name from PDC using lsa_lookup_names() */
+		b = winbind_lookup_name(dom_name, name, map->sid, &lsa_type);
+		(void)winbind_off();
+
+		if (!b) {
+			DEBUG(1, ("SID lookup failed for id %d, %s\n",
+				  id, name));
+			continue;
+		}
+
+		if (type == ID_TYPE_UID && lsa_type != SID_NAME_USER) {
+			DEBUG(1, ("Wrong type %d for user name %s\n",
+				  type, name));
+			continue;
+		}
+
+		if (type == ID_TYPE_GID && lsa_type != SID_NAME_DOM_GRP &&
+		    lsa_type != SID_NAME_ALIAS &&
+		    lsa_type != SID_NAME_WKN_GRP) {
+			DEBUG(1, ("Wrong type %d for group name %s\n",
+				  type, name));
+			continue;
+		}
+
+		map->status = ID_MAPPED;
+	}
+}
+
+/*
+ * Map unixids to names and then to sids.
+ */
+static NTSTATUS idmap_rfc2307_unixids_to_sids(struct idmap_domain *dom,
+					      struct id_map **ids)
+{
+	struct idmap_rfc2307_context *ctx;
+	char *fltr_usr = NULL, *fltr_grp = NULL;
+	TALLOC_CTX *mem_ctx;
+	int cnt_usr = 0, cnt_grp = 0, idx = 0, bidx = 0;
+	LDAPMessage *result = NULL;
+	NTSTATUS ret;
+
+	ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
+	mem_ctx = talloc_new(ctx);
+	if (!mem_ctx) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (ctx->check_connection) {
+		ret = ctx->check_connection(dom);
+		if (!NT_STATUS_IS_OK(ret)) {
+			goto out;
+		}
+	}
+
+again:
+	bidx = idx;
+
+	if (!fltr_usr) {
+		/* prepare new user query, see getpwuid() in RFC2307 */
+		fltr_usr = talloc_asprintf(mem_ctx,
+					     "(&(objectClass=posixAccount)(|");
+	}
+
+	if (!fltr_grp) {
+		/* prepare new group query, see getgrgid() in RFC2307 */
+		fltr_grp = talloc_asprintf(mem_ctx,
+					     "(&(objectClass=posixGroup)(|");
+	}
+
+	if (!fltr_usr || !fltr_grp) {
+		ret = NT_STATUS_NO_MEMORY;
+		goto out;
+	}
+
+	while (cnt_usr < IDMAP_LDAP_MAX_IDS &&
+	       cnt_grp < IDMAP_LDAP_MAX_IDS && ids[idx]) {
+
+		switch (ids[idx]->xid.type) {
+		case ID_TYPE_UID:
+			fltr_usr = talloc_asprintf_append_buffer(fltr_usr,
+					"(uidNumber=%d)", ids[idx]->xid.id);
+			cnt_usr++;
+			break;
+		case ID_TYPE_GID:
+			fltr_grp = talloc_asprintf_append_buffer(fltr_grp,
+					"(gidNumber=%d))", ids[idx]->xid.id);
+			cnt_grp++;
+			break;
+		default:
+			DEBUG(3, ("Error: unknown ID type %d\n",
+				  ids[idx]->xid.type));
+			ret = NT_STATUS_UNSUCCESSFUL;
+			goto out;
+		}
+
+		if (!fltr_usr || !fltr_grp) {
+			ret = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		idx++;
+	}
+
+	if (cnt_usr == IDMAP_LDAP_MAX_IDS || (cnt_usr != 0 && !ids[idx])) {
+		const char *attrs[] = { NULL, /* uid or cn */
+					"uidNumber",
+					NULL };
+
+		fltr_usr = talloc_strdup_append(fltr_usr, "))");
+		if (!fltr_usr) {
+			ret = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		attrs[0] = ctx->user_cn ? "cn" : "uid";
+		ret = ctx->search(ctx, ctx->bind_path_user, fltr_usr, attrs,
+				  &result);
+		if (!NT_STATUS_IS_OK(ret)) {
+			goto out;
+		}
+
+		idmap_rfc2307_map_sid_results(ctx, mem_ctx, &ids[bidx], result,
+					      dom->name, attrs, ID_TYPE_UID);
+		cnt_usr = 0;
+		TALLOC_FREE(fltr_usr);
+	}
+
+	if (cnt_grp == IDMAP_LDAP_MAX_IDS || (cnt_grp != 0 && !ids[idx])) {
+		const char *attrs[] = { "cn", "gidNumber", NULL };
+
+		fltr_grp = talloc_strdup_append(fltr_grp, "))");
+		if (!fltr_grp) {
+			ret = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+		ret = ctx->search(ctx, ctx->bind_path_group, fltr_grp, attrs,
+				  &result);
+		if (!NT_STATUS_IS_OK(ret)) {
+			goto out;
+		}
+
+		idmap_rfc2307_map_sid_results(ctx, mem_ctx, &ids[bidx], result,
+					      dom->name, attrs, ID_TYPE_GID);
+		cnt_grp = 0;
+		TALLOC_FREE(fltr_grp);
+	}
+
+	if (ids[idx]) {
+		goto again;
+	}
+
+	ret = NT_STATUS_OK;
+
+out:
+	talloc_free(mem_ctx);
+	return ret;
+}
+
+struct idmap_rfc2307_map {
+	struct id_map *map;
+	const char *name;
+	enum id_type type;
+};
+
+/*
+ * Lookup names for SIDS and store the data in the local mapping
+ * array.
+ */
+static NTSTATUS idmap_rfc_2307_sids_to_names(TALLOC_CTX *mem_ctx,
+					     struct id_map **ids,
+					     struct idmap_rfc2307_map *maps,
+					     struct idmap_rfc2307_context *ctx)
+{
+	int i;
+
+	for (i = 0; ids[i]; i++) {
+		const char *domain, *name;
+		enum lsa_SidType lsa_type;
+		struct id_map *id = ids[i];
+		struct idmap_rfc2307_map *map = &maps[i];
+		bool b;
+
+		/* by default calls to winbindd are disabled
+		   the following call will not recurse so this is safe */
+		(void)winbind_on();
+		b = winbind_lookup_sid(mem_ctx, ids[i]->sid, &domain, &name,
+				       &lsa_type);
+		(void)winbind_off();
+
+		if (!b) {
+			DEBUG(1, ("Lookup sid %s failed.\n",
+				  sid_string_dbg(ids[i]->sid)));
+			continue;
+		}
+
+		switch(lsa_type) {
+		case SID_NAME_USER:
+			id->xid.type = map->type = ID_TYPE_UID;
+			if (ctx->user_cn && ctx->cn_realm) {
+				name = talloc_asprintf(mem_ctx, "%s@%s",
+						       name, ctx->realm);
+			}
+			id->xid.type = map->type = ID_TYPE_UID;
+			break;
+
+		case SID_NAME_DOM_GRP:
+		case SID_NAME_ALIAS:
+		case SID_NAME_WKN_GRP:
+			if (ctx->cn_realm) {
+				name = talloc_asprintf(mem_ctx, "%s@%s",
+						       name, ctx->realm);
+			}
+			id->xid.type = map->type = ID_TYPE_GID;
+			break;
+
+		default:
+			DEBUG(1, ("Unknown lsa type %d for sid %s\n",
+				  lsa_type, sid_string_dbg(id->sid)));
+			id->status = ID_UNMAPPED;
+			continue;
+		}
+
+		map->map = id;
+		id->status = ID_UNKNOWN;
+		map->name = strupper_talloc(mem_ctx, name);
+
+		if (!map->name) {
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+
+	return NT_STATUS_OK;
+}
+
+/*
+ * Find id_map entry by looking up the name in the internal
+ * mapping array.
+ */
+static struct id_map* idmap_rfc2307_find_map(struct idmap_rfc2307_map *maps,
+					     enum id_type type,
+					     const char *name)
+{
+	int i;
+
+	DEBUG(10, ("Looking for name %s, type %d\n", name, type));
+
+	for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) {
+		if (maps[i].map == NULL) { /* end of the run */
+			return NULL;
+		}
+		DEBUG(10, ("Entry %d: name %s, type %d\n",
+			   i, maps[i].name, maps[i].type));
+		if (type == maps[i].type && strcmp(name, maps[i].name) == 0) {
+			return maps[i].map;
+		}
+	}
+
+	return NULL;
+}
+
+static void idmap_rfc2307_map_xid_results(struct idmap_rfc2307_context *ctx,
+					  TALLOC_CTX *mem_ctx,
+					  struct id_map **ids,
+					  struct idmap_rfc2307_map *maps,
+					  LDAPMessage *result,
+					  struct idmap_domain *dom,
+					  const char **attrs, enum id_type type)
+{
+	int count, i;
+	LDAPMessage *entry;
+
+	count = ldap_count_entries(ctx->ldap, result);
+
+	for (i = 0; i < count; i++) {
+		uint32_t id;
+		char *name;
+		bool b;
+		struct id_map *id_map;
+
+		if (i == 0) {
+			entry = ldap_first_entry(ctx->ldap, result);
+		} else {
+			entry = ldap_next_entry(ctx->ldap, result);
+		}
+		if (!entry) {
+			DEBUG(2, ("Unable to fetch entry.\n"));
+			break;
+		}
+
+		name = smbldap_talloc_single_attribute(ctx->ldap, entry,
+						       attrs[0], mem_ctx);
+		if (!name) {
+			DEBUG(1, ("Could not get user name\n"));
+			continue;
+		}
+
+		b = idmap_rfc2307_get_uint32(ctx->ldap, entry, attrs[1], &id);
+		if (!b) {
+			DEBUG(5, ("Could not pull id for record %s\n", name));
+			continue;
+		}
+
+		if (!idmap_unix_id_is_in_range(id, dom)) {
+			DEBUG(5, ("Requested id (%u) out of range (%u - %u).\n",
+				  id, dom->low_id, dom->high_id));
+			continue;
+		}
+
+		if (!strupper_m(name)) {
+			DEBUG(5, ("Could not convert %s to uppercase\n", name));
+			continue;
+		}
+		id_map = idmap_rfc2307_find_map(maps, type, name);
+		if (!id_map) {
+			DEBUG(0, ("Could not find mapping entry for name %s\n",
+				  name));
+			continue;
+		}
+
+		id_map->xid.id = id;
+		id_map->status = ID_MAPPED;
+	}
+}
+
+/*
+ * Map sids to names and then to unixids.
+ */
+static NTSTATUS idmap_rfc2307_sids_to_unixids(struct idmap_domain *dom,
+					      struct id_map **ids)
+{
+	struct idmap_rfc2307_context *ctx;
+	TALLOC_CTX *mem_ctx;
+	struct idmap_rfc2307_map *int_maps;
+	int cnt_usr = 0, cnt_grp = 0, idx = 0, bidx = 0;
+	char *fltr_usr = NULL, *fltr_grp = NULL;
+	NTSTATUS ret;
+	int i;
+
+	ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
+	mem_ctx = talloc_new(talloc_tos());
+	if (!mem_ctx) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (ctx->check_connection) {
+		ret = ctx->check_connection(dom);
+		if (!NT_STATUS_IS_OK(ret)) {
+			goto out;
+		}
+	}
+
+	for (i = 0; ids[i]; i++);
+	int_maps = talloc_zero_array(mem_ctx, struct idmap_rfc2307_map, i);
+	if (!int_maps) {
+		ret = NT_STATUS_NO_MEMORY;
+		goto out;
+	}
+
+	ret = idmap_rfc_2307_sids_to_names(mem_ctx, ids, int_maps, ctx);
+	if (!NT_STATUS_IS_OK(ret)) {
+		goto out;
+	}
+
+again:
+	if (!fltr_usr) {
+		/* prepare new user query, see getpwuid() in RFC2307 */
+		fltr_usr = talloc_asprintf(mem_ctx,
+					     "(&(objectClass=posixAccount)(|");
+	}
+
+	if (!fltr_grp) {
+		/* prepare new group query, see getgrgid() in RFC2307 */
+		fltr_grp = talloc_asprintf(mem_ctx,
+					     "(&(objectClass=posixGroup)(|");
+	}
+
+	if (!fltr_usr || !fltr_grp) {
+		ret = NT_STATUS_NO_MEMORY;
+		goto out;
+	}
+
+	while (cnt_usr < IDMAP_LDAP_MAX_IDS &&
+	       cnt_grp < IDMAP_LDAP_MAX_IDS && ids[idx]) {
+		struct id_map *id = ids[idx];
+		struct idmap_rfc2307_map *map = &int_maps[idx];
+
+		switch(id->xid.type) {
+		case ID_TYPE_UID:
+			fltr_usr = talloc_asprintf_append_buffer(fltr_usr,
+				     "(%s=%s)", (ctx->user_cn ? "cn" : "uid"),
+				      map->name);
+			cnt_usr++;
+			break;
+
+		case ID_TYPE_GID:
+			fltr_grp = talloc_asprintf_append_buffer(fltr_grp,
+					 "(cn=%s)", map->name);
+			cnt_grp++;
+			break;
+
+		default:
+			DEBUG(10, ("Nothing to do for SID %s, "
+				   "previous name lookup failed\n",
+				   sid_string_dbg(map->map->sid)));
+		}
+
+		if (!fltr_usr || !fltr_grp) {
+			ret = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		idx++;
+	}
+
+	if (cnt_usr == IDMAP_LDAP_MAX_IDS || (cnt_usr != 0 && !ids[idx])) {
+		const char *attrs[] = { NULL, /* uid or cn */
+					"uidNumber",
+					NULL };
+		LDAPMessage *result;
+
+		fltr_usr = talloc_strdup_append(fltr_usr, "))");
+		if (!fltr_usr) {
+			ret = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		attrs[0] = ctx->user_cn ? "cn" : "uid";
+		ret = ctx->search(ctx, ctx->bind_path_user, fltr_usr, attrs,
+				  &result);
+		if (!NT_STATUS_IS_OK(ret)) {
+			goto out;
+		}
+
+		idmap_rfc2307_map_xid_results(ctx, mem_ctx, &ids[bidx],
+					      int_maps, result, dom,
+					      attrs, ID_TYPE_UID);
+
+		cnt_usr = 0;
+		TALLOC_FREE(fltr_usr);
+	}
+
+	if (cnt_grp == IDMAP_LDAP_MAX_IDS || (cnt_grp != 0 && !ids[idx])) {
+		const char *attrs[] = {"cn", "gidNumber", NULL };
+		LDAPMessage *result;
+
+		fltr_grp = talloc_strdup_append(fltr_grp, "))");
+		if (!fltr_grp) {
+			ret = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		ret = ctx->search(ctx, ctx->bind_path_group, fltr_grp, attrs,
+				  &result);
+		if (!NT_STATUS_IS_OK(ret)) {
+			goto out;
+		}
+
+		idmap_rfc2307_map_xid_results(ctx, mem_ctx, &ids[bidx],
+					      int_maps, result, dom,
+					      attrs, ID_TYPE_GID);
+		cnt_grp = 0;
+		TALLOC_FREE(fltr_grp);
+	}
+
+	if (ids[idx]) {
+		goto again;
+	}
+
+	ret = NT_STATUS_OK;
+
+out:
+	talloc_free(mem_ctx);
+	return ret;
+}
+
+static int idmap_rfc2307_context_destructor(struct idmap_rfc2307_context *ctx)
+{
+	if (ctx->ads != NULL) {
+		/* we own this ADS_STRUCT so make sure it goes away */
+		ctx->ads->is_mine = True;
+		ads_destroy( &ctx->ads );
+		ctx->ads = NULL;
+	}
+
+	if (ctx->smbldap_state != NULL) {
+		smbldap_free_struct(&ctx->smbldap_state);
+	}
+
+	return 0;
+}
+
+static NTSTATUS idmap_rfc2307_initialize(struct idmap_domain *domain)
+{
+	struct idmap_rfc2307_context *ctx;
+	char *cfg_opt;
+	const char *bind_path_user, *bind_path_group, *ldap_server;
+	NTSTATUS status;
+
+	ctx = talloc_zero(domain, struct idmap_rfc2307_context);
+	if (ctx == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	talloc_set_destructor(ctx, idmap_rfc2307_context_destructor);
+
+	cfg_opt = talloc_asprintf(ctx, "idmap config %s", domain->name);
+	if (cfg_opt == NULL) {
+		status = NT_STATUS_NO_MEMORY;
+		goto err;
+	}
+
+	bind_path_user = lp_parm_const_string(-1, cfg_opt, "bind_path_user",
+					      NULL);
+	if (bind_path_user) {
+		ctx->bind_path_user = talloc_strdup(ctx, bind_path_user);
+		if (ctx->bind_path_user == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto err;
+		}
+	} else {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto err;
+	}
+
+	bind_path_group = lp_parm_const_string(-1, cfg_opt, "bind_path_group",
+					       NULL);
+	if (bind_path_group) {
+		ctx->bind_path_group = talloc_strdup(ctx, bind_path_group);
+		if (ctx->bind_path_group == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto err;
+		}
+	} else {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto err;
+	}
+
+	ldap_server = lp_parm_const_string(-1, cfg_opt, "ldap_server", NULL);
+	if (!ldap_server) {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto err;
+	}
+
+	if (strcmp(ldap_server, "stand-alone") == 0) {
+		status = idmap_rfc2307_init_ldap(ctx, domain, cfg_opt);
+
+	} else if (strcmp(ldap_server, "ad") == 0) {
+		status = idmap_rfc2307_init_ads(ctx, cfg_opt);
+
+	} else {
+		status = NT_STATUS_INVALID_PARAMETER;
+	}
+
+	if (!NT_STATUS_IS_OK(status)) {
+		goto err;
+	}
+
+	ctx->cn_realm = lp_parm_bool(-1, cfg_opt, "cn_realm", false);
+	ctx->user_cn = lp_parm_bool(-1, cfg_opt, "user_cn", false);
+
+	domain->private_data = ctx;
+	talloc_free(cfg_opt);
+	return NT_STATUS_OK;
+
+err:
+	talloc_free(cfg_opt);
+	talloc_free(ctx);
+	return status;
+}
+
+static struct idmap_methods rfc2307_methods = {
+	.init = idmap_rfc2307_initialize,
+	.unixids_to_sids = idmap_rfc2307_unixids_to_sids,
+	.sids_to_unixids = idmap_rfc2307_sids_to_unixids,
+};
+
+NTSTATUS idmap_rfc2307_init(void)
+{
+	return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "rfc2307",
+				  &rfc2307_methods);
+}
diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
index 7e80727..aacd859 100644
--- a/source3/winbindd/wscript_build
+++ b/source3/winbindd/wscript_build
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 IDMAP_AD_SRC = '''idmap_ad.c'''
+IDMAP_RFC2307_SRC = '''idmap_rfc2307.c'''
 IDMAP_RID_SRC = '''idmap_rid.c'''
 IDMAP_PASSDB_SRC = '''idmap_passdb.c'''
 IDMAP_LDAP_SRC = '''idmap_ldap.c'''
@@ -43,6 +44,15 @@ bld.SAMBA3_MODULE('idmap_ad',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad') and bld.CONFIG_SET("HAVE_LDAP"))
 
+bld.SAMBA3_MODULE('idmap_rfc2307',
+                 subsystem='idmap',
+                 allow_undefined_symbols=True,
+                 source=IDMAP_RFC2307_SRC,
+                 init_function='',
+                 deps='ads',
+                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_rfc2307'),
+                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_rfc2307') and bld.CONFIG_SET("HAVE_LDAP"))
+
 bld.SAMBA3_MODULE('idmap_rid',
                  subsystem='idmap',
                  allow_undefined_symbols=True,
diff --git a/source3/wscript b/source3/wscript
index 7a99dc1..9424b1c 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1693,7 +1693,7 @@ main() {
 				      vfs_commit
                                       vfs_crossrename vfs_linux_xfs_sgid
                                       vfs_time_audit idmap_autorid idmap_tdb2
-                                      idmap_rid idmap_hash'''))
+                                      idmap_rid idmap_hash idmap_rfc2307'''))
 
     if Options.options.developer:
         default_static_modules.extend(TO_LIST('charset_weird'))
-- 
1.7.1


>From 893fee568105fc442b5973c3e748409ddc1c329d Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:32:54 -0700
Subject: [PATCH 07/11] s3-docs: Add manpage for idmap_rfc2307 module

---
 docs-xml/manpages/idmap_rfc2307.8.xml |  165 +++++++++++++++++++++++++++++++++
 docs-xml/wscript_build                |    1 +
 2 files changed, 166 insertions(+), 0 deletions(-)
 create mode 100644 docs-xml/manpages/idmap_rfc2307.8.xml

diff --git a/docs-xml/manpages/idmap_rfc2307.8.xml b/docs-xml/manpages/idmap_rfc2307.8.xml
new file mode 100644
index 0000000..f680945
--- /dev/null
+++ b/docs-xml/manpages/idmap_rfc2307.8.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="idmap_rfc2307.8">
+
+<refmeta>
+	<refentrytitle>idmap_rfc2307</refentrytitle>
+	<manvolnum>8</manvolnum>
+	<refmiscinfo class="source">Samba</refmiscinfo>
+	<refmiscinfo class="manual">System Administration tools</refmiscinfo>
+	<refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+	<refname>idmap_rfc2307</refname>
+	<refpurpose>Samba's idmap_rfc2307 Backend for Winbind</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+	<title>DESCRIPTION</title>
+
+	<para>The idmap_rfc2307 plugin provides a way for winbind to
+	read id mappings from records in an LDAP server as defined in
+	RFC 2307. The LDAP server can be stand-alone or the LDAP
+	server provided by the AD server. An AD server is always
+	required to provide the mapping between name and SID, and the
+	LDAP server is queried for the mapping between name and
+	uid/gid. This module implements only the "idmap"
+	API, and is READONLY.</para>
+
+	<para>Mappings must be provided in advance by the
+	administrator by creating the user accounts in the Active
+	Directory server and the posixAccount and posixGroup objects
+	in the LDAP server. The names in the Active Directory server
+	and in the LDAP server have to be the same.</para>
+
+	<para>This id mapping approach allows the reuse of existing
+	LDAP authentication servers that store records in the RFC 2307
+	format.</para>
+</refsynopsisdiv>
+
+<refsect1>
+	<title>IDMAP OPTIONS</title>
+
+	<variablelist>
+		<varlistentry>
+			<term>range = low - high</term>
+			<listitem><para> Defines the available
+			matching UID and GID range for which the
+			backend is authoritative. Note that the range
+			acts as a filter. If specified any UID or GID
+			stored in AD that fall outside the range is
+			ignored and the corresponding map is
+			discarded. It is intended as a way to avoid
+			accidental UID/GID overlaps between local and
+			remotely defined IDs.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>ldap_server = <ad | stand-alone ></term>
+			<listitem><para>Defines the type of LDAP
+			server to use. This can either be the LDAP
+			server provided by the Active Directory server
+			(ad) or a stand-alone LDAP
+			server.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>bind_path_user</term>
+			<listitem><para>Specifies the bind path where
+			user objects can be found in the LDAP
+			server.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>bind_path_group</term>
+			<listitem><para>Specifies the bind path where
+			group objects can be found in the LDAP
+			server.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>user_cn = <yes | no></term>
+			<listitem><para>Query cn attribute instead of
+			uid attribute for the user name in LDAP. This
+			option is not required, the default is
+			no.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>cn_realm = <yes | no></term>
+			<listitem><para>Append @realm to cn for groups
+			(and users if user_cn is set) in
+			LDAP. This option is not required, the default
+			is no.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>ldap_domain</term>
+			<listitem><para>When using the LDAP server in
+			the Active Directory server, this allows to
+			specify the domain where to access the Active
+			Directory server. This allows using trust
+			relationships while keeping all RFC 2307
+			records in one place. This parameter is
+			optional, the default is to access the AD
+			server in the current domain to query LDAP
+			records.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>ldap_url</term>
+			<listitem><para>When using a stand-alone LDAP
+			server, this parameter specifies the ldap URL
+			for accessing the LDAP
+			server.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>ldap_user_dn</term>
+			<listitem><para>Defines the user DN to be used
+			for authentication.  The secret for
+			authenticating this user should be stored with
+			net idmap secret (see
+			<citerefentry><refentrytitle>net</refentrytitle>
+			<manvolnum>8</manvolnum></citerefentry>). If
+			absent, an anonymous bind will be
+			performed.</para></listitem>
+		</varlistentry>
+		<varlistentry>
+			<term>ldap_realm</term>
+			<listitem><para>Defines the realm to use in
+			the user and group names. This is only
+			required when using cn_realm together with a
+			stand-alone ldap server.</para></listitem>
+		</varlistentry>
+	</variablelist>
+</refsect1>
+
+<refsect1>
+	<title>EXAMPLES</title>
+
+	<para>The following example shows how to retrieve id mappings
+	from a stand-alone LDAP server.  This example also shows how
+	to leave a small non conflicting range for local id allocation
+	that may be used in internal backends like BUILTIN.</para>
+
+	<programlisting>
+	[global]
+	idmap config * : backend = tdb
+	idmap config * : range = 1000000-1999999
+
+	idmap config DOMAIN : backend = rfc2307
+	idmap config DOMAIN : range = 2000000-2999999
+	idmap config DOMAIN : ldap_server = stand-alone
+	idmap config DOMAIN : ldap_url = ldap://ldap1.example.com
+	idmap config DOMAIN : ldap_user_dn = cn=ldapmanager,dc=example,dc=com
+	idmap config DOMAIN : bind_path_user = ou=People,dc=example,dc=com
+	idmap config DOMAIN : bind_path_group = ou=Group,dc=example,dc=com
+	</programlisting>
+</refsect1>
+
+<refsect1>
+	<title>AUTHOR</title>
+
+	<para>
+	The original Samba software and related utilities
+	were created by Andrew Tridgell. Samba is now developed
+	by the Samba Team as an Open Source project similar
+	to the way the Linux kernel is developed.
+	</para>
+</refsect1>
+
+</refentry>
diff --git a/docs-xml/wscript_build b/docs-xml/wscript_build
index 25e381e..0c6d4a4 100644
--- a/docs-xml/wscript_build
+++ b/docs-xml/wscript_build
@@ -12,6 +12,7 @@ manpages='''
          manpages/idmap_hash.8
          manpages/idmap_ldap.8
          manpages/idmap_nss.8
+         manpages/idmap_rfc2307.8
          manpages/idmap_rid.8
          manpages/idmap_tdb.8
          manpages/idmap_tdb2.8
-- 
1.7.1


>From aca7966413ed488bc5976ecec0d2e30e17e04697 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:33:13 -0700
Subject: [PATCH 08/11] s3-net: Allow setting the ldap password for idmap_rfc2307

---
 source3/utils/net_idmap.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/source3/utils/net_idmap.c b/source3/utils/net_idmap.c
index bdc4879..fbeca3e 100644
--- a/source3/utils/net_idmap.c
+++ b/source3/utils/net_idmap.c
@@ -634,9 +634,11 @@ static int net_idmap_secret(struct net_context *c, int argc, const char **argv)
 	backend = talloc_strdup(ctx, lp_parm_const_string(-1, opt, "backend", "tdb"));
 	ALLOC_CHECK(backend);
 
-	if ( ( ! backend) || ( ! strequal(backend, "ldap"))) {
+	if ((!backend) || (!strequal(backend, "ldap") &&
+			   !strequal(backend, "rfc2307"))) {
 		d_fprintf(stderr,
-			  _("The only currently supported backend is LDAP\n"));
+			  _("The only currently supported backend are LDAP "
+			    "and rfc2307\n"));
 		talloc_free(ctx);
 		return -1;
 	}
-- 
1.7.1


>From 8b5505f458e426501fc7c86ce3c4bfaa082852ff Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:33:19 -0700
Subject: [PATCH 09/11] packaging(RHEL-CTDB): Add idmap_rfc2307 module

---
 packaging/RHEL-CTDB/configure.rpm |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/packaging/RHEL-CTDB/configure.rpm b/packaging/RHEL-CTDB/configure.rpm
index f16b352..398a3f9 100755
--- a/packaging/RHEL-CTDB/configure.rpm
+++ b/packaging/RHEL-CTDB/configure.rpm
@@ -24,7 +24,7 @@ else
 	CC="gcc"
 fi
 
-shared_modules="idmap_rid,idmap_ad,idmap_tdb2"
+shared_modules="idmap_rid,idmap_ad,idmap_tdb2,idmap_rfc2307"
 
 if test "x$BUILD_GPFS" != "xno"; then
 	shared_modules="${shared_modules},vfs_gpfs,vfs_tsmsm"
-- 
1.7.1


>From b2b1f3825c6cbbad586a39b2f6497c9077c7dbc8 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:33:28 -0700
Subject: [PATCH 10/11] Add LD_LIBRARY_PATH to load idmap modules in testenv

---
 selftest/wscript |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/selftest/wscript b/selftest/wscript
index c7637a1..d0b54d5 100644
--- a/selftest/wscript
+++ b/selftest/wscript
@@ -173,6 +173,9 @@ def cmd_testonly(opt):
     ADD_LD_LIBRARY_PATH('bin/shared')
     ADD_LD_LIBRARY_PATH('bin/shared/private')
 
+    # Allow loading of shared idmap modules
+    ADD_LD_LIBRARY_PATH('bin/modules/idmap')
+
     # if we are using a system version of ldb then we need to tell it to
     # load modules from our modules path
     if env.USING_SYSTEM_LDB:
-- 
1.7.1


>From ba848c70139ab6a0a2bf406aa89cf79b3aaa689c Mon Sep 17 00:00:00 2001
From: Christof Schmitt <christof.schmitt at us.ibm.com>
Date: Thu, 21 Feb 2013 12:33:23 -0700
Subject: [PATCH 11/11] Add testcase for idmap_rfc2307 module

Create a new test environment with 'idmap config DOMAIN : backend =
rfc2307'. A new test script adds LDAP records and queries them again for
the mapped uid and gid.
---
 nsswitch/tests/test_idmap_rfc2307.sh |   80 ++++++++++++++++++++++++++++++++++
 selftest/target/Samba.pm             |    1 +
 selftest/target/Samba3.pm            |   79 +++++++++++++++++++++++++++++++++
 selftest/target/Samba4.pm            |    6 +++
 source3/selftest/tests.py            |    6 ++-
 5 files changed, 171 insertions(+), 1 deletions(-)
 create mode 100755 nsswitch/tests/test_idmap_rfc2307.sh

diff --git a/nsswitch/tests/test_idmap_rfc2307.sh b/nsswitch/tests/test_idmap_rfc2307.sh
new file mode 100755
index 0000000..1bed10a
--- /dev/null
+++ b/nsswitch/tests/test_idmap_rfc2307.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+# Test id mapping through idmap_rfc2307 module
+if [ $# -lt 6 ]; then
+	echo Usage: $0 DOMAIN USERNAME UID GROUPNAME GID LDAPPREFIX
+	exit 1
+fi
+
+DOMAIN="$1"
+USERNAME="$2"
+USERUID="$3"
+GROUPNAME="$4"
+GROUPGID="$5"
+LDAPPREFIX="$6"
+
+echo called with: $1 $2 $3 $4 $5 $6
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+ldbadd="$BINDIR/ldbadd"
+failed=0
+
+. `dirname $0`/../../testprogs/blackbox/subunit.sh
+
+# Add id mapping information
+
+cat > $PREFIX/tmpldb <<EOF
+dn: $LDAPPREFIX
+objectclass: organizationalUnit
+EOF
+
+testit "add ldap prefix" $VALGRIND $ldbadd -H ./st/dc/private/sam.ldb $PREFIX/tmpldb
+
+cat > $PREFIX/tmpldb <<EOF
+dn: cn=$USERNAME,$LDAPPREFIX
+objectClass: organizationalPerson
+objectClass: posixAccount
+ou: People
+cn: $USERNAME
+uid: $USERNAME
+uidNumber: $USERUID
+gidNumber: 1
+homeDirectory: /home/admin
+EOF
+
+testit "add ldap user mapping record" $VALGRIND $ldbadd -H ./st/dc/private/sam.ldb $PREFIX/tmpldb
+
+cat > $PREFIX/tmpldb <<EOF
+dn: cn=$GROUPNAME,$LDAPPREFIX
+objectClass: posixGroup
+objectClass: groupOfNames
+cn: $GROUPNAME
+gidNumber: $GROUPGID
+member: cn=$USERNAME,$LDAPPREFIX
+EOF
+
+testit "add ldap group mapping record" $VALGRIND $ldbadd -H ./st/dc/private/sam.ldb $PREFIX/tmpldb
+
+rm -f $PREFIX/tmpldbmodify
+
+testit "wbinfo --name-to-sid" $wbinfo --name-to-sid "$DOMAIN\\$USERNAME" || failed=$(expr $failed + 1)
+user_sid=$($wbinfo -n "$DOMAIN\\$USERNAME" | cut -d " " -f1)
+echo "$DOMAIN\\$USERNAME resolved to $user_sid"
+
+testit "wbinfo --sid-to-uid=$user_sid" $wbinfo --sid-to-uid=$user_sid || failed=$(expr $failed + 1)
+user_uid=$($wbinfo --sid-to-uid=$user_sid | cut -d " " -f1)
+echo "$DOMAIN\\$USERNAME resolved to $user_uid"
+
+testit "test $user_uid -eq $USERUID" test $user_uid -eq $USERUID || failed=$(expr $failed + 1)
+
+# Not sure how to get group names with spaces to resolve through testit
+#testit "wbinfo --name-to-sid" $wbinfo --name-to-sid="$DOMAIN\\$GROUPNAME" || failed=$(expr $failed + 1)
+group_sid=$($wbinfo --name-to-sid="$DOMAIN\\$GROUPNAME" | cut -d " " -f1)
+echo "$DOMAIN\\$GROUPNAME resolved to $group_sid"
+
+testit "wbinfo --sid-to-gid=$group_sid" $wbinfo --sid-to-gid=$group_sid || failed=$(expr $failed + 1)
+group_gid=$($wbinfo --sid-to-gid=$group_sid | cut -d " " -f1)
+echo "$DOMAIN\\$GROUPNAME resolved to $group_gid"
+
+testit "test $group_gid -eq $GROUPGID" test $group_gid -eq $GROUPGID || failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm
index d811053..fc81629 100644
--- a/selftest/target/Samba.pm
+++ b/selftest/target/Samba.pm
@@ -171,6 +171,7 @@ sub get_interface($)
     $interfaces{"localsubdc"} = 31;
     $interfaces{"chgdcpass"} = 32;
     $interfaces{"promotedvdc"} = 33;
+    $interfaces{"s3member_rfc2307"} = 34;
 
     # update lib/socket_wrapper/socket_wrapper.c
     #  #define MAX_WRAPPED_INTERFACES 32
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index c71419d..31a6f0c 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -331,6 +331,85 @@ sub setup_admember($$$$)
 	return $ret;
 }
 
+sub setup_admember_rfc2307($$$$)
+{
+	my ($self, $prefix, $dcvars) = @_;
+
+	# If we didn't build with ADS, pretend this env was never available
+	if (not $self->have_ads()) {
+	        return "UNKNOWN";
+	}
+
+	print "PROVISIONING S3 AD MEMBER...";
+
+	my $member_options = "
+	security = ads
+	server signing = on
+        workgroup = $dcvars->{DOMAIN}
+        realm = $dcvars->{REALM}
+        idmap config $dcvars->{DOMAIN} : backend = rfc2307
+        idmap config $dcvars->{DOMAIN} : range = 2000000-2999999
+        idmap config $dcvars->{DOMAIN} : ldap_server = ad
+        idmap config $dcvars->{DOMAIN} : bind_path_user = ou=idmap,dc=samba,dc=example,dc=com
+        idmap config $dcvars->{DOMAIN} : bind_path_group = ou=idmap,dc=samba,dc=example,dc=com
+";
+
+	my $ret = $self->provision($prefix,
+				   "LOCALADMEMBER",
+				   "loCalMemberPass",
+				   $member_options);
+
+	$ret or return undef;
+
+	close(USERMAP);
+	$ret->{DOMAIN} = $dcvars->{DOMAIN};
+	$ret->{REALM} = $dcvars->{REALM};
+
+	my $ctx;
+	my $prefix_abs = abs_path($prefix);
+	$ctx = {};
+	$ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf";
+	$ctx->{domain} = $dcvars->{DOMAIN};
+	$ctx->{realm} = $dcvars->{REALM};
+	$ctx->{dnsname} = lc($dcvars->{REALM});
+	$ctx->{kdc_ipv4} = $dcvars->{SERVER_IP};
+	Samba::mk_krb5_conf($ctx, "");
+
+	$ret->{KRB5_CONFIG} = $ctx->{krb5_conf};
+
+	my $net = Samba::bindir_path($self, "net");
+	my $cmd = "";
+	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
+	$cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
+	$cmd .= "$net join $ret->{CONFIGURATION}";
+	$cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}";
+
+	if (system($cmd) != 0) {
+	    warn("Join failed\n$cmd");
+	    return undef;
+	}
+
+	# We need world access to this share, as otherwise the domain
+	# administrator from the AD domain provided by Samba4 can't
+	# access the share for tests.
+	chmod 0777, "$prefix/share";
+
+	if (not $self->check_or_start($ret, "yes", "yes", "yes")) {
+		return undef;
+	}
+
+	$ret->{DC_SERVER} = $dcvars->{SERVER};
+	$ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP};
+	$ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME};
+	$ret->{DC_USERNAME} = $dcvars->{USERNAME};
+	$ret->{DC_PASSWORD} = $dcvars->{PASSWORD};
+
+	# Special case, this is called from Samba4.pm but needs to use the Samba3 check_env and get_log_env
+	$ret->{target} = $self;
+
+	return $ret;
+}
+
 sub setup_simpleserver($$)
 {
 	my ($self, $path) = @_;
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index e9e0037..090be04 100644
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1661,6 +1661,12 @@ sub setup_env($$$)
 		return $target3->setup_admember("$path/s3member", $self->{vars}->{dc}, 29);
 	} elsif ($envname eq "plugin_s4_dc") {
 		return $self->setup_plugin_s4_dc("$path/plugin_s4_dc");
+	} elsif ($envname eq "s3member_rfc2307") {
+		if (not defined($self->{vars}->{dc})) {
+			$self->setup_dc("$path/dc");
+		}
+		return $target3->setup_admember_rfc2307("$path/s3member",
+							$self->{vars}->{dc}, 34);
 	} else {
 		return "UNKNOWN";
 	}
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 44efe18..a3fa352 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -278,6 +278,8 @@ local = ["local.nss-wrapper", "local.ndr"]
 
 winbind = ["winbind.struct", "winbind.wbclient", "winbind.pac"]
 
+idmap = [ "idmap.rfc2307" ]
+
 rap = ["rap.basic", "rap.rpc", "rap.printing", "rap.sam"]
 
 unix = ["unix.info2", "unix.whoami"]
@@ -286,7 +288,7 @@ nbt = ["nbt.dgram" ]
 
 libsmbclient = ["libsmbclient"]
 
-tests= base + raw + smb2 + rpc + unix + local + winbind + rap + nbt + libsmbclient
+tests= base + raw + smb2 + rpc + unix + local + winbind + rap + nbt + libsmbclient + idmap
 
 for t in tests:
     if t == "base.delaywrite":
@@ -338,6 +340,8 @@ for t in tests:
         plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/valid-users-tmp -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/write-list-tmp -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
+    elif t == "idmap.rfc2307":
+        plantestsuite(t, "s3member_rfc2307", [os.path.join(samba3srcdir, "../nsswitch/tests/test_idmap_rfc2307.sh"), '$DOMAIN', 'Administrator', '2000000', '"Domain Users"', '2000001', 'ou=idmap,dc=samba,dc=example,dc=com'])
     else:
         plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
-- 
1.7.1



More information about the samba-technical mailing list