idmap_adex: Add new idmap plugin for support RFC2307 enabled AD forests.
authorGerald (Jerry) Carter <jerry@samba.org>
Fri, 19 Sep 2008 17:27:15 +0000 (12:27 -0500)
committerGerald W. Carter <jerry@samba.org>
Mon, 22 Sep 2008 22:46:19 +0000 (15:46 -0700)
The adex idmap/nss_info plugin is an adapation of the Likewise
Enterprise plugin with support for OU based cells removed
(since the Windows pieces to manage the cells are not available).

This plugin supports

  * The RFC2307 schema for users and groups.
  * Connections to trusted domains
  * Global catalog searches
  * Cross forest trusts
  * User and group aliases

Prerequiste: Add the following attributes to the Partial Attribute
Set in global catalog:

  * uidNumber
  * uid
  * gidNumber

A basic config using the current trunk code would look like

 [global]
      idmap backend = adex
      idmap uid = 10000 - 19999
      idmap gid = 20000 - 29999
      idmap config US:backend = adex
      idmap config US:range = 20000 - 29999
      winbind nss info = adex

       winbind normalize names = yes
       winbind refresh tickets = yes
       template homedir = /home/%D/%U
       template shell = /bin/bash

source3/Makefile.in
source3/configure.in
source3/winbindd/idmap_adex/cell_util.c [new file with mode: 0644]
source3/winbindd/idmap_adex/domain_util.c [new file with mode: 0644]
source3/winbindd/idmap_adex/gc_util.c [new file with mode: 0644]
source3/winbindd/idmap_adex/idmap_adex.c [new file with mode: 0644]
source3/winbindd/idmap_adex/idmap_adex.h [new file with mode: 0644]
source3/winbindd/idmap_adex/likewise_cell.c [new file with mode: 0644]
source3/winbindd/idmap_adex/provider_unified.c [new file with mode: 0644]

index 8304981a1f72671a384c61da6243a10d7f90317b..1ce0ce9067918c02028336448a3d970fc28564ca 100644 (file)
@@ -985,6 +985,14 @@ IDMAP_HASH_OBJ = \
                winbindd/idmap_hash/idmap_hash.o \
                winbindd/idmap_hash/mapfile.o
 
+IDMAP_ADEX_OBJ = \
+               winbindd/idmap_adex/idmap_adex.o \
+               winbindd/idmap_adex/cell_util.o \
+               winbindd/idmap_adex/likewise_cell.o \
+               winbindd/idmap_adex/provider_unified.o \
+               winbindd/idmap_adex/gc_util.o \
+               winbindd/idmap_adex/domain_util.o
+
 WINBINDD_OBJ1 = \
                winbindd/winbindd.o       \
                winbindd/winbindd_user.o  \
@@ -2218,6 +2226,10 @@ bin/hash.@SHLIBEXT@: $(BINARY_PREREQS) $(IDMAP_HASH_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(IDMAP_HASH_OBJ)
 
+bin/adex.@SHLIBEXT@: $(BINARY_PREREQS) $(IDMAP_ADEX_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) $(IDMAP_ADEX_OBJ)
+
 bin/tdb2.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_tdb2.o
        @echo "Building plugin $@"
        @$(SHLD_MODULE) winbindd/idmap_tdb2.o
index 640afc47fb8123ef129fb6002ec364688f4cec99..d1000d9a525e45c136edd4ab9225feb8d9c4e8ef 100644 (file)
@@ -6058,6 +6058,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_ad, winbindd/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP)
 SMB_MODULE(idmap_hash, \$(IDMAP_HASH_OBJ), "bin/hash.$SHLIBEXT", IDMAP)
+SMB_MODULE(idmap_adex, \$(IDMAP_ADEX_OBJ), "bin/adex.$SHLIBEXT", IDMAP)
 SMB_SUBSYSTEM(IDMAP, winbindd/idmap.o)
 
 SMB_MODULE(nss_info_template, winbindd/nss_info_template.o, "bin/template.$SHLIBEXT", NSS_INFO)
diff --git a/source3/winbindd/idmap_adex/cell_util.c b/source3/winbindd/idmap_adex/cell_util.c
new file mode 100644 (file)
index 0000000..f5c08a0
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * idmap_adex: Support for AD Forests
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2006-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "idmap_adex.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/**********************************************************************
+**********************************************************************/
+
+ char *find_attr_string(char **list, size_t num_lines, const char *substr)
+{
+       int i;
+       int cmplen = strlen(substr);
+
+       for (i = 0; i < num_lines; i++) {
+               /* make sure to avoid substring matches like uid
+                  and uidNumber */
+               if ((StrnCaseCmp(list[i], substr, cmplen) == 0) &&
+                   (list[i][cmplen] == '=')) {
+                       /* Don't return an empty string */
+                       if (list[i][cmplen + 1] != '\0')
+                               return &(list[i][cmplen + 1]);
+
+                       return NULL;
+               }
+       }
+
+       return NULL;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+ bool is_object_class(char **list, size_t num_lines, const char *substr)
+{
+       int i;
+
+       for (i = 0; i < num_lines; i++) {
+               if (strequal(list[i], substr)) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/**********************************************************************
+ Find out about the cell (e.g. use2307Attrs, etc...)
+**********************************************************************/
+
+ NTSTATUS cell_lookup_settings(struct likewise_cell * cell)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       /* Parameter check */
+
+       if (!cell) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Only supporting Forest-wide, schema based searches */
+
+       cell_set_flags(cell, LWCELL_FLAG_USE_RFC2307_ATTRS);
+       cell_set_flags(cell, LWCELL_FLAG_SEARCH_FOREST);
+
+       cell->provider = &ccp_unified;
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(1,("LWI: Failed to obtain cell settings (%s)\n",
+                        nt_errstr(nt_status)));
+       }
+
+       return nt_status;
+}
+
+
+static NTSTATUS cell_lookup_forest(struct likewise_cell *c)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct gc_info *gc = NULL;
+
+       if (!c) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
+               nt_status = NT_STATUS_NO_MEMORY;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Query the rootDSE for the forest root naming conect first.
+           Check that the a GC server for the forest has not already
+          been added */
+
+       nt_status = gc_find_forest_root(gc, cell_dns_domain(c));
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       c->forest_name = talloc_strdup(c, gc->forest_name);
+       BAIL_ON_PTR_ERROR(c->forest_name, nt_status);
+
+done:
+       if (gc) {
+               talloc_free(gc);
+       }
+
+       return nt_status;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+ NTSTATUS cell_locate_membership(ADS_STRUCT * ads)
+{
+       ADS_STATUS status;
+       char *domain_dn = ads_build_dn(lp_realm());
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID sid;
+       struct likewise_cell *cell = NULL;
+
+       /* In the Likewise plugin, I had to support the concept of cells
+          based on the machine's membership in an OU.  However, now I'll
+          just assume our membership in the forest cell */
+
+       DEBUG(2, ("locate_cell_membership: Located membership "
+                 "in cell \"%s\"\n", domain_dn));
+
+       if ((cell = cell_new()) == NULL) {
+               nt_status = NT_STATUS_NO_MEMORY;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       status = ads_domain_sid(ads, &sid);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(3,("locate_cell_membership: Failed to find "
+                        "domain SID for %s\n", domain_dn));
+       }
+
+       /* save the SID and search base for our domain */
+
+       cell_set_dns_domain(cell, lp_realm());
+       cell_set_connection(cell, ads);
+       cell_set_dn(cell, domain_dn);
+       cell_set_domain_sid(cell, &sid);
+
+       /* Now save our forest root */
+
+       cell_lookup_forest(cell);
+
+       /* Add the cell to the list */
+
+       if (!cell_list_add(cell)) {
+               nt_status = NT_STATUS_INSUFFICIENT_RESOURCES;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Done! */
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0,("LWI: Failed to locate cell membership (%s)\n",
+                        nt_errstr(nt_status)));
+       }
+
+       SAFE_FREE(domain_dn);
+
+       return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+ int min_id_value(void)
+{
+       int id_val;
+
+       id_val = lp_parm_int(-1, "lwidentity", "min_id_value", MIN_ID_VALUE);
+
+       /* Still don't let it go below 50 */
+
+       return MAX(50, id_val);
+}
+
+/********************************************************************
+ *******************************************************************/
+
+ char *cell_dn_to_dns(const char *dn)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char *domain = NULL;
+       char *dns_name = NULL;
+       const char *tmp_dn;
+       char *buffer = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (!dn || !*dn) {
+               goto done;
+       }
+
+       tmp_dn = talloc_strdup(frame, dn);
+       BAIL_ON_PTR_ERROR(tmp_dn, nt_status);
+
+       while (next_token_talloc(frame, &tmp_dn, &buffer, ",")) {
+
+               /* skip everything up the where DC=... begins */
+               if (StrnCaseCmp(buffer, "DC=", 3) != 0)
+                       continue;
+
+               if (!domain) {
+                       domain = talloc_strdup(frame, &buffer[3]);
+               } else {
+                       domain = talloc_asprintf_append(domain, ".%s",
+                                                       &buffer[3]);
+               }
+               BAIL_ON_PTR_ERROR(domain, nt_status);
+       }
+
+       dns_name = SMB_STRDUP(domain);
+       BAIL_ON_PTR_ERROR(dns_name, nt_status);
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "cell_dn_to_dns", 1);
+
+       talloc_destroy(frame);
+
+       return dns_name;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+ NTSTATUS get_sid_type(ADS_STRUCT *ads,
+                      LDAPMessage *msg,
+                      enum lsa_SidType *type)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32_t atype;
+
+       if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
+               nt_status = NT_STATUS_INVALID_USER_BUFFER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       switch (atype &0xF0000000) {
+       case ATYPE_SECURITY_GLOBAL_GROUP:
+               *type = SID_NAME_DOM_GRP;
+               break;
+       case ATYPE_SECURITY_LOCAL_GROUP:
+               *type = SID_NAME_ALIAS;
+               break;
+       case ATYPE_NORMAL_ACCOUNT:
+       case ATYPE_WORKSTATION_TRUST:
+       case ATYPE_INTERDOMAIN_TRUST:
+               *type = SID_NAME_USER;
+               break;
+       default:
+               *type = SID_NAME_USE_NONE;
+               nt_status = NT_STATUS_INVALID_ACCOUNT_NAME;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       return nt_status;
+}
diff --git a/source3/winbindd/idmap_adex/domain_util.c b/source3/winbindd/idmap_adex/domain_util.c
new file mode 100644 (file)
index 0000000..ab31cce
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * idmap_adex: Domain search interface
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2007-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "idmap_adex.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct dc_info {
+       struct dc_info *prev, *next;
+       char *dns_name;
+       struct likewise_cell *domain_cell;
+};
+
+static struct dc_info *_dc_server_list = NULL;
+
+
+/**********************************************************************
+ *********************************************************************/
+
+static struct dc_info *dc_list_head(void)
+{
+       return _dc_server_list;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS dc_add_domain(const char *domain)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct dc_info *dc = NULL;
+
+       /* Check for duplicates */
+
+       dc = dc_list_head();
+       while (dc) {
+               if (strequal (dc->dns_name, domain))
+                       break;
+               dc = dc->next;
+       }
+
+       if (dc) {
+               DEBUG(10,("dc_add_domain: %s already in list\n", domain));
+               return NT_STATUS_OK;
+       }
+
+       dc = TALLOC_ZERO_P(NULL, struct dc_info);
+       BAIL_ON_PTR_ERROR(dc, nt_status);
+
+       dc->dns_name = talloc_strdup(dc, domain);
+       BAIL_ON_PTR_ERROR(dc->dns_name, nt_status);
+
+       DLIST_ADD_END(_dc_server_list, dc, struct dc_info*);
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_destroy(dc);
+               DEBUG(0,("LWI: Failed to add new DC connection for %s (%s)\n",
+                        domain, nt_errstr(nt_status)));
+       }
+
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static void dc_server_list_destroy(void)
+{
+       struct dc_info *dc = dc_list_head();
+
+       while (dc) {
+               struct dc_info *p = dc->next;
+
+               cell_destroy(dc->domain_cell);
+               talloc_destroy(dc);
+
+               dc = p;
+       }
+
+       return;
+}
+
+
+/**********************************************************************
+ *********************************************************************/
+
+ NTSTATUS domain_init_list(void)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct winbindd_tdc_domain *domains = NULL;
+       size_t num_domains = 0;
+       int i;
+
+       if (_dc_server_list != NULL) {
+               dc_server_list_destroy();
+       }
+
+       /* Add our domain */
+
+       nt_status = dc_add_domain(lp_realm());
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
+               nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Add all domains with an incoming trust path */
+
+       for (i=0; i<num_domains; i++) {
+               uint32_t flags = (NETR_TRUST_FLAG_INBOUND|NETR_TRUST_FLAG_IN_FOREST);
+
+               /* We just require one of the flags to be set here */
+
+               if (domains[i].trust_flags & flags) {
+                       nt_status = dc_add_domain(domains[i].dns_name);
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(2,("LWI: Failed to initialize DC list (%s)\n",
+                        nt_errstr(nt_status)));
+       }
+
+       TALLOC_FREE(domains);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS dc_do_search(struct dc_info *dc,
+                            const char *search_base,
+                            int scope,
+                            const char *expr,
+                            const char **attrs,
+                            LDAPMessage ** msg)
+{
+       ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       status = cell_do_search(dc->domain_cell, search_base,
+                               scope, expr, attrs, msg);
+       nt_status = ads_ntstatus(status);
+
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static struct dc_info *dc_find_domain(const char *dns_domain)
+{
+       struct dc_info *dc = dc_list_head();
+
+       if (!dc)
+               return NULL;
+
+       while (dc) {
+               if (strequal(dc->dns_name, dns_domain)) {
+                       return dc;
+               }
+
+               dc = dc->next;
+       }
+
+       return NULL;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ NTSTATUS dc_search_domains(struct likewise_cell **cell,
+                           LDAPMessage **msg,
+                           const char *dn,
+                           const DOM_SID *sid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *dns_domain;
+       const char *attrs[] = { "*", NULL };
+       struct dc_info *dc = NULL;
+       const char *base = NULL;
+
+       if (!dn || !*dn) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       dns_domain = cell_dn_to_dns(dn);
+       BAIL_ON_PTR_ERROR(dns_domain, nt_status);
+
+       if ((dc = dc_find_domain(dns_domain)) == NULL) {
+               nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Reparse the cell settings for the domain if necessary */
+
+       if (!dc->domain_cell) {
+               char *base_dn;
+
+               base_dn = ads_build_dn(dc->dns_name);
+               BAIL_ON_PTR_ERROR(base_dn, nt_status);
+
+               nt_status = cell_connect_dn(&dc->domain_cell, base_dn);
+               SAFE_FREE(base_dn);
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               nt_status = cell_lookup_settings(dc->domain_cell);
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               /* By definition this is already part of a larger
+                  forest-wide search scope */
+
+               cell_set_flags(dc->domain_cell, LWCELL_FLAG_SEARCH_FOREST);
+       }
+
+       /* Check whether we are operating in non-schema or RFC2307
+          mode */
+
+       if (cell_flags(dc->domain_cell) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
+               nt_status = dc_do_search(dc, dn, LDAP_SCOPE_BASE,
+                                        "(objectclass=*)", attrs, msg);
+       } else {
+               const char *sid_str = NULL;
+               char *filter = NULL;
+
+               sid_str = sid_string_talloc(frame, sid);
+               BAIL_ON_PTR_ERROR(sid_str, nt_status);
+
+               filter = talloc_asprintf(frame, "(keywords=backLink=%s)",
+                                        sid_str);
+               BAIL_ON_PTR_ERROR(filter, nt_status);
+
+               base = cell_search_base(dc->domain_cell);
+               BAIL_ON_PTR_ERROR(base, nt_status);
+
+               nt_status = dc_do_search(dc, base, LDAP_SCOPE_SUBTREE,
+                                        filter, attrs, msg);
+       }
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       *cell = dc->domain_cell;
+
+done:
+       talloc_destroy(CONST_DISCARD(char*, base));
+       talloc_destroy(frame);
+
+       return nt_status;
+}
diff --git a/source3/winbindd/idmap_adex/gc_util.c b/source3/winbindd/idmap_adex/gc_util.c
new file mode 100644 (file)
index 0000000..87dd3c0
--- /dev/null
@@ -0,0 +1,848 @@
+/*
+ * idmap_adex: Global Catalog search interface
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2007-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "idmap_adex.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+static struct gc_info *_gc_server_list = NULL;
+
+
+/**********************************************************************
+ *********************************************************************/
+
+static struct gc_info *gc_list_head(void)
+{
+       return _gc_server_list;
+}
+
+/**********************************************************************
+ Checks if either of the domains is a subdomain of the other
+ *********************************************************************/
+
+static bool is_subdomain(const char* a, const char *b)
+{
+       char *s;
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *x, *y;
+       bool ret = false;
+
+       /* Trivial cases */
+
+       if (!a && !b)
+               return true;
+
+       if (!a || !b)
+               return false;
+
+       /* Normalize the case */
+
+       x = talloc_strdup(frame, a);
+       y = talloc_strdup(frame, b);
+       if (!x || !y) {
+               ret = false;
+               goto done;
+       }
+
+       strupper_m(x);
+       strupper_m(y);
+
+       /* Exact match */
+
+       if (strcmp(x, y) == 0) {
+               ret = true;
+               goto done;
+       }
+
+       /* Check for trailing substrings */
+
+       s = strstr_m(x, y);
+       if (s && (strlen(s) == strlen(y))) {
+               ret = true;
+               goto done;
+       }
+
+       s = strstr_m(y, x);
+       if (s && (strlen(s) == strlen(x))) {
+               ret = true;
+               goto done;
+       }
+
+done:
+       talloc_destroy(frame);
+
+       return ret;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
+{
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS ads_status;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct nbt_cldap_netlogon_5 cldap_reply;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (!gc || !domain) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       ZERO_STRUCT(cldap_reply);
+
+       ads = ads_init(domain, NULL, NULL);
+       BAIL_ON_PTR_ERROR(ads, nt_status);
+
+       ads->auth.flags = ADS_AUTH_NO_BIND;
+       ads_status = ads_connect(ads);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
+                         domain, ads_errstr(ads_status)));
+       }
+       nt_status = ads_ntstatus(ads_status);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if (!ads_cldap_netlogon_5(frame,
+                                 ads->config.ldap_server_name,
+                                 ads->config.realm,
+                                 &cldap_reply))
+       {
+               DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
+                        ads->server.ldap_server));
+               nt_status = NT_STATUS_IO_TIMEOUT;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
+       BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
+
+done:
+       if (ads) {
+               ads_destroy(&ads);
+       }
+
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS gc_add_forest(const char *domain)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct gc_info *gc = NULL;
+       struct gc_info *find_gc = NULL;
+       char *dn;
+       ADS_STRUCT *ads = NULL;
+       struct likewise_cell *primary_cell = NULL;
+
+       primary_cell = cell_list_head();
+       if (!primary_cell) {
+               nt_status = NT_STATUS_INVALID_SERVER_STATE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Check for duplicates based on domain name first as this
+           requires no connection */
+
+       find_gc = gc_list_head();
+       while (find_gc) {
+               if (strequal (find_gc->forest_name, domain))
+                       break;
+               find_gc = find_gc->next;
+       }
+
+       if (find_gc) {
+               DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
+               return NT_STATUS_OK;
+       }
+
+       if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
+               nt_status = NT_STATUS_NO_MEMORY;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Query the rootDSE for the forest root naming conect first.
+           Check that the a GC server for the forest has not already
+          been added */
+
+       nt_status = gc_find_forest_root(gc, domain);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       find_gc = gc_list_head();
+       while (find_gc) {
+               if (strequal (find_gc->forest_name, gc->forest_name))
+                       break;
+               find_gc = find_gc->next;
+       }
+
+       if (find_gc) {
+               DEBUG(10,("gc_add_forest: Forest %s already in list\n",
+                         find_gc->forest_name));
+               return NT_STATUS_OK;
+       }
+
+       /* Not found, so add it here.  Make sure we connect to
+          a DC in _this_ domain and not the forest root. */
+
+       dn = ads_build_dn(gc->forest_name);
+       BAIL_ON_PTR_ERROR(dn, nt_status);
+
+       gc->search_base = talloc_strdup(gc, dn);
+       SAFE_FREE(dn);
+       BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
+
+#if 0
+       /* Can't use cell_connect_dn() here as there is no way to
+          specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
+
+       nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+#else
+
+       gc->forest_cell = cell_new();
+       BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
+
+       /* Set the DNS domain, dn, etc ... and add it to the list */
+
+       cell_set_dns_domain(gc->forest_cell, gc->forest_name);
+       cell_set_dn(gc->forest_cell, gc->search_base);
+       cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
+#endif
+
+       /* It is possible to belong to a non-forest cell and a
+          non-provisioned forest (at our domain levele). In that
+          case, we should just inherit the flags from our primary
+          cell since the GC searches will match our own schema
+          model. */
+
+       if (strequal(primary_cell->forest_name, gc->forest_name)
+           || is_subdomain(primary_cell->dns_domain, gc->forest_name))
+       {
+               cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
+       } else {
+               /* outside of our domain */
+
+               nt_status = cell_connect(gc->forest_cell);
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               nt_status = cell_lookup_settings(gc->forest_cell);
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               /* Drop the connection now that we have the settings */
+
+               ads = cell_connection(gc->forest_cell);
+               ads_destroy(&ads);
+               cell_set_connection(gc->forest_cell, NULL);
+       }
+
+       DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
+
+       DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
+                 gc->forest_name));
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_destroy(gc);
+               DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
+                        domain, nt_errstr(nt_status)));
+       }
+
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static void gc_server_list_destroy(void)
+{
+       struct gc_info *gc = gc_list_head();
+
+       while (gc) {
+               struct gc_info *p = gc->next;
+
+               cell_destroy(gc->forest_cell);
+               talloc_destroy(gc);
+
+               gc = p;
+       }
+
+       _gc_server_list = NULL;
+
+       return;
+}
+
+/**********************************************************************
+ Setup the initial list of forests and initial the forest cell
+ settings for each.  FIXME!!!
+ *********************************************************************/
+
+ NTSTATUS gc_init_list(void)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct winbindd_tdc_domain *domains = NULL;
+       size_t num_domains = 0;
+       int i;
+
+       if (_gc_server_list != NULL) {
+               gc_server_list_destroy();
+       }
+
+       if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
+               nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Find our forest first.  Have to try all domains here starting
+          with our own.  gc_add_forest() filters duplicates */
+
+       nt_status = gc_add_forest(lp_realm());
+       WARN_ON_NTSTATUS_ERROR(nt_status);
+
+       for (i=0; i<num_domains; i++) {
+               uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
+
+               /* I think we should be able to break out of loop once
+                  we add a GC for our forest and not have to test every one.
+                  In fact, this entire loop is probably irrelevant since
+                  the GC location code should always find a GC given lp_realm().
+                  Will have to spend time testing before making the change.
+                  --jerry */
+
+               if ((domains[i].trust_flags & flags) == flags) {
+                       nt_status = gc_add_forest(domains[i].dns_name);
+                       WARN_ON_NTSTATUS_ERROR(nt_status);
+                       /* Don't BAIL here since not every domain may
+                          have a GC server */
+               }
+       }
+
+       /* Now add trusted forests.  gc_add_forest() will filter out
+          duplicates. Check everything with an incoming trust path
+          that is not in our own forest.  */
+
+       for (i=0; i<num_domains; i++) {
+               uint32_t flags = domains[i].trust_flags;
+               uint32_t attribs = domains[i].trust_attribs;
+
+               /* Skip non_AD domains */
+
+               if (strlen(domains[i].dns_name) == 0) {
+                       continue;
+               }
+
+               /* Only add a GC for a forest outside of our own.
+                  Ignore QUARANTINED/EXTERNAL trusts */
+
+               if ((flags & NETR_TRUST_FLAG_INBOUND)
+                   && !(flags & NETR_TRUST_FLAG_IN_FOREST)
+                   && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
+               {
+                       nt_status = gc_add_forest(domains[i].dns_name);
+                       WARN_ON_NTSTATUS_ERROR(nt_status);
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
+                        nt_errstr(nt_status)));
+       }
+
+       TALLOC_FREE(domains);
+
+       return nt_status;
+}
+
+
+/**********************************************************************
+ *********************************************************************/
+
+ struct gc_info *gc_search_start(void)
+{
+       NTSTATUS nt_status = NT_STATUS_OK;
+       struct gc_info *gc = gc_list_head();
+
+       if (!gc) {
+               nt_status = gc_init_list();
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               gc = gc_list_head();
+       }
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
+                        nt_errstr(nt_status)));
+       }
+
+       return gc;
+}
+
+/**********************************************************************
+ Search Global Catalog.  Always search our own forest.  The flags set
+ controls whether or not we search cross forest.  Assume that the
+ resulting set is always returned from one GC so that we don't have to
+ both combining the LDAPMessage * results
+ *********************************************************************/
+
+ NTSTATUS gc_search_forest(struct gc_info *gc,
+                          LDAPMessage **msg,
+                          const char *filter)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       const char *attrs[] = {"*", NULL};
+       LDAPMessage *m = NULL;
+
+       if (!gc || !msg || !filter) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* When you have multiple domain trees in a forest, the
+          GC will search all naming contexts when you send it
+          and empty ("") base search suffix.   Tested against
+          Windows 2003.  */
+
+       ads_status = cell_do_search(gc->forest_cell, "",
+                                  LDAP_SCOPE_SUBTREE, filter, attrs, &m);
+       nt_status = ads_ntstatus(ads_status);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       *msg = m;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
+                        filter, nt_errstr(nt_status)));
+       }
+
+       return nt_status;
+}
+
+/**********************************************************************
+ Search all forests via GC and return the results in an array of
+ ADS_STRUCT/LDAPMessage pairs.
+ *********************************************************************/
+
+ NTSTATUS gc_search_all_forests(const char *filter,
+                               ADS_STRUCT ***ads_list,
+                               LDAPMessage ***msg_list,
+                               int *num_resp, uint32_t flags)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct gc_info *gc = NULL;
+       uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
+
+       *ads_list = NULL;
+       *msg_list = NULL;
+       *num_resp = 0;
+
+       if ((gc = gc_search_start()) == NULL) {
+               nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       while (gc) {
+               LDAPMessage *m = NULL;
+
+               nt_status = gc_search_forest(gc, &m, filter);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       gc = gc->next;
+                       continue;
+               }
+
+               nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
+                                                   m, ads_list, msg_list,
+                                                   num_resp);
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               /* If there can only be one match, then we are done */
+
+               if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
+                       break;
+               }
+
+               gc = gc->next;
+       }
+
+       if (*num_resp == 0) {
+               nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       return nt_status;
+}
+
+/**********************************************************************
+ Search all forests via GC and return the results in an array of
+ ADS_STRUCT/LDAPMessage pairs.
+ *********************************************************************/
+
+ NTSTATUS gc_search_all_forests_unique(const char *filter,
+                                      ADS_STRUCT **ads,
+                                      LDAPMessage **msg)
+{
+       ADS_STRUCT **ads_list = NULL;
+       LDAPMessage **msg_list = NULL;
+       int num_resp;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       nt_status = gc_search_all_forests(filter, &ads_list,
+                                         &msg_list, &num_resp,
+                                         ADEX_GC_SEARCH_CHECK_UNIQUE);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = check_result_unique(ads_list[0], msg_list[0]);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       *ads = ads_list[0];
+       *msg = msg_list[0];
+
+done:
+       /* Be care that we don't free the msg result being returned */
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               free_result_array(ads_list, msg_list, num_resp);
+       } else {
+               talloc_destroy(ads_list);
+               talloc_destroy(msg_list);
+       }
+
+       return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+ NTSTATUS gc_name_to_sid(const char *domain,
+                        const char *name,
+                        DOM_SID *sid,
+                        enum lsa_SidType *sid_type)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *p, *name_user;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char *name_filter;
+       ADS_STRUCT *ads = NULL;
+       LDAPMessage *msg = NULL;
+       LDAPMessage *e = NULL;
+       char *dn = NULL;
+       char *dns_domain = NULL;
+       ADS_STRUCT **ads_list = NULL;
+       LDAPMessage **msg_list = NULL;
+       int num_resp = 0;
+       int i;
+
+       /* Strip the "DOMAIN\" prefix if necessary and search for
+          a matching sAMAccountName in the forest */
+
+       if ((p = strchr_m( name, '\\' )) == NULL)
+               name_user = talloc_strdup( frame, name );
+       else
+               name_user = talloc_strdup( frame, p+1 );
+       BAIL_ON_PTR_ERROR(name_user, nt_status);
+
+       name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
+       BAIL_ON_PTR_ERROR(name_filter, nt_status);
+
+       nt_status = gc_search_all_forests(name_filter, &ads_list,
+                                         &msg_list, &num_resp, 0);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Assume failure until we know otherwise*/
+
+       nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       /* Match the domain name from the DN */
+
+       for (i=0; i<num_resp; i++) {
+               ads = ads_list[i];
+               msg = msg_list[i];
+
+               e = ads_first_entry(ads, msg);
+               while (e) {
+                       struct winbindd_tdc_domain *domain_rec;
+
+                       dn = ads_get_dn(ads, e);
+                       BAIL_ON_PTR_ERROR(dn, nt_status);
+
+                       dns_domain = cell_dn_to_dns(dn);
+                       SAFE_FREE(dn);
+                       BAIL_ON_PTR_ERROR(dns_domain, nt_status);
+
+                       domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
+                       SAFE_FREE(dns_domain);
+
+                       /* Ignore failures and continue the search */
+
+                       if (!domain_rec) {
+                               e = ads_next_entry(ads, e);
+                               continue;
+                       }
+
+                       /* Check for a match on the domain name */
+
+                       if (strequal(domain, domain_rec->domain_name)) {
+                               if (!ads_pull_sid(ads, e, "objectSid", sid)) {
+                                       nt_status = NT_STATUS_INVALID_SID;
+                                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+                               }
+
+                               talloc_destroy(domain_rec);
+
+                               nt_status = get_sid_type(ads, msg, sid_type);
+                               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+                               /* We're done! */
+                               nt_status = NT_STATUS_OK;
+                               break;
+                       }
+
+                       /* once more around thew merry-go-round */
+
+                       talloc_destroy(domain_rec);
+                       e = ads_next_entry(ads, e);
+               }
+       }
+
+done:
+       free_result_array(ads_list, msg_list, num_resp);
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ Pull an attribute string value
+ *******************************************************************/
+
+static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
+                                       LDAPMessage *msg,
+                                       char **name)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char *sam_name = NULL;
+       struct winbindd_tdc_domain *domain_rec = NULL;
+       char *dns_domain = NULL;
+       char *dn = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       int len;
+
+       /* Check parameters */
+
+       if (!ads || !msg || !name) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* get the name and domain */
+
+       dn = ads_get_dn(ads, msg);
+       BAIL_ON_PTR_ERROR(dn, nt_status);
+
+       DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
+
+       dns_domain = cell_dn_to_dns(dn);
+       SAFE_FREE(dn);
+       BAIL_ON_PTR_ERROR(dns_domain, nt_status);
+
+       domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
+       SAFE_FREE(dns_domain);
+
+       if (!domain_rec) {
+               nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
+       BAIL_ON_PTR_ERROR(sam_name, nt_status);
+
+       len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
+       if (len == -1) {
+               *name = NULL;
+               BAIL_ON_PTR_ERROR((*name), nt_status);
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+ NTSTATUS gc_sid_to_name(const DOM_SID *sid,
+                        char **name,
+                        enum lsa_SidType *sid_type)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char *filter;
+       ADS_STRUCT *ads = NULL;
+       LDAPMessage *msg = NULL;
+       char *sid_string;
+
+       *name = NULL;
+
+       sid_string = sid_binstring(sid);
+       BAIL_ON_PTR_ERROR(sid_string, nt_status);
+
+       filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
+       SAFE_FREE(sid_string);
+       BAIL_ON_PTR_ERROR(filter, nt_status);
+
+       nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = get_object_account_name(ads, msg, name);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = get_sid_type(ads, msg, sid_type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       ads_msgfree(ads, msg);
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
+                                 LDAPMessage *msg,
+                                 ADS_STRUCT ***ads_list,
+                                 LDAPMessage ***msg_list,
+                                 int *size)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       ADS_STRUCT **ads_tmp = NULL;
+       LDAPMessage **msg_tmp = NULL;
+       int count = *size;
+
+       if (!ads || !msg) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+#if 0
+       /* Don't add a response with no entries */
+
+       if (ads_count_replies(ads, msg) == 0) {
+               return NT_STATUS_OK;
+       }
+#endif
+
+       if (count == 0) {
+               ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
+               BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
+
+               msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
+               BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
+       } else {
+               ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
+                                              count+1);
+               BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
+
+               msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
+                                              count+1);
+               BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
+       }
+
+       ads_tmp[count] = ads;
+       msg_tmp[count] = msg;
+       count++;
+
+       *ads_list = ads_tmp;
+       *msg_list = msg_tmp;
+       *size = count;
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_destroy(ads_tmp);
+               talloc_destroy(msg_tmp);
+       }
+
+       return nt_status;
+}
+
+/**********************************************************************
+ Frees search results.  Do not free the ads_list as these are
+ references back to the GC search structures.
+ *********************************************************************/
+
+ void free_result_array(ADS_STRUCT **ads_list,
+                       LDAPMessage **msg_list,
+                       int num_resp)
+{
+       int i;
+
+       for (i=0; i<num_resp; i++) {
+               ads_msgfree(ads_list[i], msg_list[i]);
+       }
+
+       talloc_destroy(ads_list);
+       talloc_destroy(msg_list);
+}
+
+/**********************************************************************
+ Check that we have exactly one entry from the search
+ *********************************************************************/
+
+ NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
+{
+       NTSTATUS nt_status;
+       int count;
+
+       count = ads_count_replies(ads, msg);
+
+       if (count <= 0) {
+               nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       if (count > 1) {
+               nt_status = NT_STATUS_DUPLICATE_NAME;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       return nt_status;
+}
diff --git a/source3/winbindd/idmap_adex/idmap_adex.c b/source3/winbindd/idmap_adex/idmap_adex.c
new file mode 100644 (file)
index 0000000..23ab843
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * idmap_adex: Support for D Forests
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2006-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "idmap_adex.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
+
+NTSTATUS init_module(void);
+
+/*
+ * IdMap backend
+ */
+
+/********************************************************************
+ Basic init function responsible for determining our current mode
+ (standalone or using Centeris Cells).  This must return success or
+ it will be dropped from the idmap backend list.
+ *******************************************************************/
+
+static NTSTATUS _idmap_adex_init(struct idmap_domain *dom,
+                                    const char *params)
+{
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS status;
+       static NTSTATUS init_status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+       DOM_SID domain_sid;
+       fstring dcname;
+       struct sockaddr_storage ip;
+       struct likewise_cell *lwcell;
+
+       if (NT_STATUS_IS_OK(init_status))
+               return NT_STATUS_OK;
+
+       /* Silently fail if we are not a member server in security = ads */
+
+       if ((lp_server_role() != ROLE_DOMAIN_MEMBER) ||
+           (lp_security() != SEC_ADS)) {
+               init_status = NT_STATUS_INVALID_SERVER_STATE;
+               BAIL_ON_NTSTATUS_ERROR(init_status);
+       }
+
+       /* fetch our domain SID first */
+
+       if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+               init_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               BAIL_ON_NTSTATUS_ERROR(init_status);
+       }
+
+       /* reuse the same ticket cache as winbindd */
+
+       setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
+
+       /* Establish a connection to a DC */
+
+       if ((ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL) {
+               init_status = NT_STATUS_NO_MEMORY;
+               BAIL_ON_NTSTATUS_ERROR(init_status);
+       }
+
+       ads->auth.password =
+           secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+       ads->auth.realm = SMB_STRDUP(lp_realm());
+
+       /* get the DC name here to setup the server affinity cache and
+          local krb5.conf */
+
+       get_dc_name(lp_workgroup(), lp_realm(), dcname, &ip);
+
+       status = ads_connect(ads);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(0, ("_idmap_adex_init: ads_connect() failed! (%s)\n",
+                         ads_errstr(status)));
+       }
+       init_status = ads_ntstatus(status);
+       BAIL_ON_NTSTATUS_ERROR(init_status);
+
+
+       /* Find out cell membership */
+
+       init_status = cell_locate_membership(ads);
+       if (!NT_STATUS_IS_OK(init_status)) {
+               DEBUG(0,("LWI: Fail to locate cell membership (%s).",
+                        nt_errstr(init_status)));
+               goto done;
+       }
+
+       /* Fill in the cell information */
+
+       lwcell = cell_list_head();
+
+       init_status = cell_lookup_settings(lwcell);
+       BAIL_ON_NTSTATUS_ERROR(init_status);
+
+       /* Miscellaneous setup.  E.g. set up the list of GC
+          servers and domain list for our forest (does not actually
+          connect). */
+
+       init_status = gc_init_list();
+       BAIL_ON_NTSTATUS_ERROR(init_status);
+
+       init_status = domain_init_list();
+       BAIL_ON_NTSTATUS_ERROR(init_status);
+
+done:
+       if (!NT_STATUS_IS_OK(init_status)) {
+               DEBUG(1,("Likewise initialization failed (%s)\n",
+                        nt_errstr(init_status)));
+       }
+
+       /* cleanup */
+
+       if (!NT_STATUS_IS_OK(init_status)) {
+               cell_list_destroy();
+
+               /* init_status stores the failure reason but we need to
+                  return success or else idmap_init() will drop us from the
+                  backend list */
+               return NT_STATUS_OK;
+       }
+
+       init_status = NT_STATUS_OK;
+
+       return init_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _idmap_adex_get_sid_from_id(struct
+                                               idmap_domain
+                                               *dom, struct
+                                               id_map
+                                               **ids)
+{
+       int i;
+       bool one_mapped = false;
+       bool all_mapped = true;
+       NTSTATUS nt_status;
+        struct likewise_cell *cell;
+
+       nt_status = _idmap_adex_init(dom, NULL);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+       if ((cell = cell_list_head()) == NULL) {
+               return NT_STATUS_INVALID_SERVER_STATE;
+       }
+
+       /* have to work through these one by one */
+       for (i = 0; ids[i]; i++) {
+               NTSTATUS status;
+               status = cell->provider->get_sid_from_id(ids[i]->sid,
+                                                        ids[i]->xid.id,
+                                                        ids[i]->xid.type);
+               /* Fail if we cannot find any DC */
+               if (NT_STATUS_EQUAL
+                   (status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+                       return status;
+               }
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       ids[i]->status = ID_UNMAPPED;
+                       all_mapped = false;
+                       continue;
+               }
+
+               ids[i]->status = ID_MAPPED;
+               one_mapped = true;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _idmap_adex_get_id_from_sid(struct
+                                               idmap_domain
+                                               *dom, struct
+                                               id_map
+                                               **ids)
+{
+       int i;
+       bool one_mapped = false;
+       bool all_mapped = true;
+       NTSTATUS nt_status;
+        struct likewise_cell *cell;
+
+       nt_status = _idmap_adex_init(dom, NULL);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+       if ((cell = cell_list_head()) == NULL) {
+               return NT_STATUS_INVALID_SERVER_STATE;
+       }
+
+       /* have to work through these one by one */
+       for (i = 0; ids[i]; i++) {
+               NTSTATUS status;
+               status = cell->provider->get_id_from_sid(&ids[i]->xid.id,
+                                                        &ids[i]->xid.
+                                                        type, ids[i]->sid);
+               /* Fail if we cannot find any DC */
+               if (NT_STATUS_EQUAL
+                   (status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+                       return status;
+               }
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       ids[i]->status = ID_UNMAPPED;
+                       all_mapped = false;
+                       continue;
+               }
+
+               ids[i]->status = ID_MAPPED;
+               one_mapped = true;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _idmap_adex_set_mapping(struct
+                                           idmap_domain
+                                           *dom, const struct
+                                           id_map *map)
+{
+       DEBUG(0, ("_idmap_adex_set_mapping: not implemented\n"));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _idmap_adex_remove_mapping(struct
+                                              idmap_domain
+                                              *dom, const
+                                              struct
+                                              id_map
+                                              *map)
+{
+       DEBUG(0, ("_idmap_adex_remove_mapping: not implemented\n"));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _idmap_adex_dump(struct idmap_domain
+                                    *dom, struct id_map **maps, int *num_map)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _idmap_adex_close(struct idmap_domain
+                                     *dom)
+{
+       /* FIXME!  need to do cleanup here */
+
+       return NT_STATUS_OK;
+}
+
+/*
+ * IdMap NSS plugin
+ */
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _nss_adex_init(struct nss_domain_entry
+                                 *e)
+{
+       return _idmap_adex_init(NULL, NULL);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _nss_adex_get_info(struct
+                                     nss_domain_entry *e,
+                                     const DOM_SID * sid,
+                                     TALLOC_CTX * ctx,
+                                     ADS_STRUCT * ads,
+                                     LDAPMessage * msg,
+                                     char **homedir,
+                                     char **shell, char **gecos, gid_t * p_gid)
+{
+       NTSTATUS nt_status;
+        struct likewise_cell *cell;
+
+       nt_status = _idmap_adex_init(NULL, NULL);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+       if ((cell = cell_list_head()) == NULL) {
+               return NT_STATUS_INVALID_SERVER_STATE;
+       }
+
+       return cell->provider->get_nss_info(sid, ctx, homedir,
+                                           shell, gecos, p_gid);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _nss_adex_map_to_alias(TALLOC_CTX * mem_ctx, const char
+                                         *domain, const char
+                                         *name, char **alias)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+        struct likewise_cell *cell = NULL;
+
+       nt_status = _idmap_adex_init(NULL, NULL);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if ((cell = cell_list_head()) == NULL) {
+               nt_status = NT_STATUS_INVALID_SERVER_STATE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = cell->provider->map_to_alias(mem_ctx, domain,
+                                                name, alias);
+
+       /* go ahead and allow the cache mgr to mark this in
+          negative cache */
+
+       if (!NT_STATUS_IS_OK(nt_status))
+               nt_status = NT_STATUS_NONE_MAPPED;
+
+done:
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _nss_adex_map_from_alias(TALLOC_CTX * mem_ctx, const char
+                                           *domain, const char
+                                           *alias, char **name)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+        struct likewise_cell *cell = NULL;
+
+       nt_status = _idmap_adex_init(NULL, NULL);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if ((cell = cell_list_head()) == NULL) {
+               nt_status = NT_STATUS_INVALID_SERVER_STATE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+
+       nt_status = cell->provider->map_from_alias(mem_ctx, domain,
+                                                  alias, name);
+
+       /* go ahead and allow the cache mgr to mark this in
+          negative cache */
+
+       if (!NT_STATUS_IS_OK(nt_status))
+               nt_status = NT_STATUS_NONE_MAPPED;
+
+done:
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _nss_adex_close(void)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static struct idmap_methods adex_idmap_methods = {
+
+       .init             = _idmap_adex_init,
+       .unixids_to_sids  = _idmap_adex_get_sid_from_id,
+       .sids_to_unixids  = _idmap_adex_get_id_from_sid,
+       .set_mapping      = _idmap_adex_set_mapping,
+       .remove_mapping   = _idmap_adex_remove_mapping,
+       .dump_data        = _idmap_adex_dump,
+       .close_fn         = _idmap_adex_close
+};
+static struct nss_info_methods adex_nss_methods = {
+       .init           = _nss_adex_init,
+       .get_nss_info   = _nss_adex_get_info,
+       .map_to_alias   = _nss_adex_map_to_alias,
+       .map_from_alias = _nss_adex_map_from_alias,
+       .close_fn       = _nss_adex_close
+};
+
+/**********************************************************************
+ Register with the idmap and idmap_nss subsystems. We have to protect
+ against the idmap and nss_info interfaces being in a half-registered
+ state.
+ **********************************************************************/
+NTSTATUS idmap_adex_init(void)
+{
+       static NTSTATUS idmap_status = NT_STATUS_UNSUCCESSFUL;
+       static NTSTATUS nss_status = NT_STATUS_UNSUCCESSFUL;
+       if (!NT_STATUS_IS_OK(idmap_status)) {
+               idmap_status =
+                   smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
+                                      "adex", &adex_idmap_methods);
+               if (!NT_STATUS_IS_OK(idmap_status)) {
+                       DEBUG(0,
+                             ("idmap_centeris_init: Failed to register the adex"
+                              "idmap plugin.\n"));
+                       return idmap_status;
+               }
+       }
+
+       if (!NT_STATUS_IS_OK(nss_status)) {
+               nss_status =
+                   smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+                                          "adex", &adex_nss_methods);
+               if (!NT_STATUS_IS_OK(nss_status)) {
+                       DEBUG(0,
+                             ("idmap_adex_init: Failed to register the adex"
+                              "nss plugin.\n"));
+                       return nss_status;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS nss_info_adex_init(void)
+{
+       return idmap_adex_init();
+}
diff --git a/source3/winbindd/idmap_adex/idmap_adex.h b/source3/winbindd/idmap_adex/idmap_adex.h
new file mode 100644 (file)
index 0000000..f91bba8
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * idmap_centeris: Support for Local IDs and Centeris Cell Structure
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2006-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _IDMAP_ADEX_H
+#define _IDMAP_ADEX_H
+
+#include "winbindd/winbindd.h"
+
+#define ADEX_CELL_RDN             "$LikewiseIdentityCell"
+
+#define ADEX_OC_USER              "centerisLikewiseUser"
+#define ADEX_OC_GROUP             "centerisLikewiseGroup"
+
+#define AD_USER                 "User"
+#define AD_GROUP                "Group"
+
+#define ADEX_OC_POSIX_USER        "posixAccount"
+#define ADEX_OC_POSIX_GROUP       "posixGroup"
+
+#define ADEX_ATTR_UIDNUM          "uidNumber"
+#define ADEX_ATTR_GIDNUM          "gidNUmber"
+#define ADEX_ATTR_HOMEDIR         "unixHomeDirectory"
+#define ADEX_ATTR_USERPW          "unixUserPassword"
+#define ADEX_ATTR_GROUPALIAS      "groupAlias" /* Not part of RFC2307 */
+#define ADEX_ATTR_SHELL           "loginShell"
+#define ADEX_ATTR_GECOS           "gecos"
+#define ADEX_ATTR_UID             "uid"
+#define ADEX_ATTR_DISPLAYNAME     "displayName"
+
+#define MIN_ID_VALUE            100
+
+#define BAIL_ON_NTSTATUS_ERROR(x)         \
+       do {                               \
+               if (!NT_STATUS_IS_OK(x)) { \
+                       DEBUG(10,("Failed! (%s)\n", nt_errstr(x)));     \
+                       goto done;         \
+               }                          \
+       }                                  \
+       while (0);                         \
+
+#define WARN_ON_NTSTATUS_ERROR(x)         \
+       do {                               \
+               if (!NT_STATUS_IS_OK(x)) { \
+                       DEBUG(10,("Failure ignored! (%s)\n", nt_errstr(x)));    \
+               }                          \
+       }                                  \
+       while (0);                         \
+
+#define BAIL_ON_ADS_ERROR(x)              \
+       do {                               \
+               if (!ADS_ERR_OK(x)) {      \
+                       goto done;         \
+               }                          \
+       }                                  \
+       while (0);
+
+#define BAIL_ON_PTR_ERROR(p, x)                                \
+       do {                                            \
+               if ((p) == NULL ) {                     \
+                       DEBUG(10,("NULL pointer!\n"));  \
+                       x = NT_STATUS_NO_MEMORY;        \
+                       goto done;                      \
+               }                                       \
+       } while (0);
+
+#define PRINT_NTSTATUS_ERROR(x, hdr, level)                            \
+       do {                                                            \
+               if (!NT_STATUS_IS_OK(x)) {                              \
+                       DEBUG(level,("LWI ("hdr"): %s\n", nt_errstr(x))); \
+               }                                                       \
+       } while(0);
+/*
+ * Cell Provider API
+ */
+
+struct cell_provider_api {
+       NTSTATUS(*get_sid_from_id) (DOM_SID * sid,
+                                   uint32_t id, enum id_type type);
+       NTSTATUS(*get_id_from_sid) (uint32_t * id,
+                                   enum id_type * type, const DOM_SID * sid);
+       NTSTATUS(*get_nss_info) (const DOM_SID * sid,
+                                TALLOC_CTX * ctx,
+                                char **homedir,
+                                char **shell, char **gecos, gid_t * p_gid);
+       NTSTATUS(*map_to_alias) (TALLOC_CTX * mem_ctx,
+                                const char *domain,
+                                const char *name, char **alias);
+       NTSTATUS(*map_from_alias) (TALLOC_CTX * mem_ctx,
+                                  const char *domain,
+                                  const char *alias, char **name);
+};
+
+/* registered providers */
+
+extern struct cell_provider_api ccp_unified;
+extern struct cell_provider_api ccp_local;
+
+#define LWCELL_FLAG_USE_RFC2307_ATTRS     0x00000001
+#define LWCELL_FLAG_SEARCH_FOREST         0x00000002
+#define LWCELL_FLAG_GC_CELL               0x00000004
+#define LWCELL_FLAG_LOCAL_MODE            0x00000008
+
+struct likewise_cell {
+       struct likewise_cell *prev, *next;
+       ADS_STRUCT *conn;
+       struct likewise_cell *gc_search_cell;
+       DOM_SID domain_sid;
+       char *dns_domain;
+       char *forest_name;
+       char *dn;
+       struct GUID *links;        /* only held by owning cell */
+       size_t num_links;
+       uint32_t flags;
+       struct cell_provider_api *provider;
+};
+
+/* Search flags used for Global Catalog API */
+
+#define ADEX_GC_SEARCH_CHECK_UNIQUE        0x00000001
+
+struct gc_info {
+       struct gc_info *prev, *next;
+       char *forest_name;
+       char *search_base;
+       struct likewise_cell *forest_cell;
+};
+
+/* Available functions outside of idmap_lwidentity.c */
+
+/* cell_util.c */
+
+char *find_attr_string(char **list, size_t num_lines, const char *substr);
+bool is_object_class(char **list, size_t num_lines, const char *substr);
+int min_id_value(void);
+char *cell_dn_to_dns(const char *dn);
+NTSTATUS get_sid_type(ADS_STRUCT *ads,
+                     LDAPMessage *msg,
+                     enum lsa_SidType *type);
+
+NTSTATUS cell_locate_membership(ADS_STRUCT * ads);
+NTSTATUS cell_lookup_settings(struct likewise_cell * cell);
+NTSTATUS cell_follow_links(struct likewise_cell *cell);
+NTSTATUS cell_set_local_provider(void);
+
+/* likewise_cell.c */
+
+struct likewise_cell *cell_new(void);
+struct likewise_cell *cell_list_head(void);
+
+bool cell_list_add(struct likewise_cell *cell);
+bool cell_list_remove(struct likewise_cell * cell);
+
+void cell_list_destroy();
+void cell_destroy(struct likewise_cell *c);
+void cell_set_forest_searches(struct likewise_cell *c,
+                               bool search);
+void cell_set_dns_domain(struct likewise_cell *c,
+                          const char *dns_domain);
+void cell_set_connection(struct likewise_cell *c,
+                          ADS_STRUCT *ads);
+void cell_set_dn(struct likewise_cell *c,
+                  const char *dn);
+void cell_set_domain_sid(struct likewise_cell *c,
+                          DOM_SID *sid);
+void cell_set_flags(struct likewise_cell *c, uint32_t flags);
+void cell_clear_flags(struct likewise_cell *c, uint32_t flags);
+
+const char* cell_search_base(struct likewise_cell *c);
+const char *cell_dns_domain(struct likewise_cell *c);
+ADS_STRUCT *cell_connection(struct likewise_cell *c);
+bool cell_search_forest(struct likewise_cell *c);
+ADS_STATUS cell_do_search(struct likewise_cell *c,
+                         const char *search_base,
+                         int scope,
+                         const char *expr,
+                         const char **attrs,
+                         LDAPMessage ** msg);
+uint32_t cell_flags(struct likewise_cell *c);
+
+NTSTATUS cell_connect_dn(struct likewise_cell **c,
+                        const char *dn);
+NTSTATUS cell_connect(struct likewise_cell *c);
+
+
+/* gc_util.c */
+
+NTSTATUS gc_init_list(void);
+
+NTSTATUS gc_find_forest_root(struct gc_info *gc,
+                            const char *domain);
+
+struct gc_info *gc_search_start(void);
+
+NTSTATUS gc_search_forest(struct gc_info *gc,
+                         LDAPMessage **msg,
+                         const char *filter);
+
+NTSTATUS gc_search_all_forests(const char *filter,
+                              ADS_STRUCT ***ads_list,
+                              LDAPMessage ***msg_list,
+                              int *num_resp, uint32_t flags);
+
+NTSTATUS gc_search_all_forests_unique(const char *filter,
+                                     ADS_STRUCT **ads,
+                                     LDAPMessage **msg);
+
+NTSTATUS gc_name_to_sid(const char *domain,
+                       const char *name,
+                       DOM_SID *sid,
+                       enum lsa_SidType *sid_type);
+
+NTSTATUS gc_sid_to_name(const DOM_SID *sid,
+                       char **name,
+                       enum lsa_SidType *sid_type);
+
+NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
+                                LDAPMessage *msg,
+                                ADS_STRUCT ***ads_list,
+                                LDAPMessage ***msg_list,
+                                int *size);
+
+void free_result_array(ADS_STRUCT **ads_list,
+                      LDAPMessage **msg_list,
+                      int num_resp);
+
+NTSTATUS check_result_unique(ADS_STRUCT *ads,
+                            LDAPMessage *msg);
+
+
+/* domain_util.c */
+
+NTSTATUS domain_init_list(void);
+
+NTSTATUS dc_search_domains(struct likewise_cell **cell,
+                          LDAPMessage **msg,
+                          const char *dn,
+                          const DOM_SID *user_sid);
+
+
+#endif /* _IDMAP_ADEX_H */
diff --git a/source3/winbindd/idmap_adex/likewise_cell.c b/source3/winbindd/idmap_adex/likewise_cell.c
new file mode 100644 (file)
index 0000000..77eeee4
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * idmap_adex: Support for AD Forests
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2006-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "idmap_adex.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+static struct likewise_cell *_lw_cell_list = NULL;
+
+/**********************************************************************
+ Return the current HEAD of the list
+ *********************************************************************/
+
+ struct likewise_cell *cell_list_head(void)
+{
+       return _lw_cell_list;
+}
+
+
+/**********************************************************************
+ *********************************************************************/
+
+ void cell_destroy(struct likewise_cell *c)
+{
+       if (!c)
+               return;
+
+       if (c->conn)
+               ads_destroy(&c->conn);
+
+       talloc_destroy(c);
+}
+
+/**********************************************************************
+ Free all cell entries and reset the list head to NULL
+ *********************************************************************/
+
+ void cell_list_destroy(void)
+{
+       struct likewise_cell *p = _lw_cell_list;
+
+       while (p) {
+               struct likewise_cell *q = p->next;
+
+               cell_destroy(p);
+
+               p = q;
+       }
+
+       _lw_cell_list = NULL;
+
+       return;
+}
+
+/**********************************************************************
+ Add a new cell structure to the list
+ *********************************************************************/
+
+ struct likewise_cell* cell_new(void)
+{
+       struct likewise_cell *c;
+
+       /* Each cell struct is a TALLOC_CTX* */
+
+       c = TALLOC_ZERO_P(NULL, struct likewise_cell);
+       if (!c) {
+               DEBUG(0,("cell_new: memory allocation failure!\n"));
+               return NULL;
+       }
+
+       return c;
+}
+
+/**********************************************************************
+ Add a new cell structure to the list
+ *********************************************************************/
+
+ bool cell_list_add(struct likewise_cell * cell)
+{
+       if (!cell) {
+               return false;
+       }
+
+       /* Always add to the end */
+
+       DLIST_ADD_END(_lw_cell_list, cell, struct likewise_cell *);
+
+       return true;
+}
+
+/**********************************************************************
+ Add a new cell structure to the list
+ *********************************************************************/
+
+ bool cell_list_remove(struct likewise_cell * cell)
+{
+       if (!cell) {
+               return false;
+       }
+
+       /* Remove and drop the cell structure */
+
+       DLIST_REMOVE(_lw_cell_list, cell);
+       talloc_destroy(cell);
+
+       return true;
+}
+
+/**********************************************************************
+ Set the containing DNS domain for a cell
+ *********************************************************************/
+
+ void cell_set_dns_domain(struct likewise_cell *c, const char *dns_domain)
+{
+       c->dns_domain = talloc_strdup(c, dns_domain);
+}
+
+/**********************************************************************
+ Set ADS connection for a cell
+ *********************************************************************/
+
+ void cell_set_connection(struct likewise_cell *c, ADS_STRUCT *ads)
+{
+       c->conn = ads;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ void cell_set_flags(struct likewise_cell *c, uint32_t flags)
+{
+       c->flags |= flags;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ void cell_clear_flags(struct likewise_cell *c, uint32_t flags)
+{
+       c->flags &= ~flags;
+}
+
+/**********************************************************************
+ Set the Cell's DN
+ *********************************************************************/
+
+ void cell_set_dn(struct likewise_cell *c, const char *dn)
+{
+       if ( c->dn) {
+               talloc_free(c->dn);
+               c->dn = NULL;
+       }
+
+       c->dn = talloc_strdup(c, dn);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ void cell_set_domain_sid(struct likewise_cell *c, DOM_SID *sid)
+{
+       sid_copy(&c->domain_sid, sid);
+}
+
+/*
+ * Query Routines
+ */
+
+/**********************************************************************
+ *********************************************************************/
+
+ const char* cell_search_base(struct likewise_cell *c)
+{
+       if (!c)
+               return NULL;
+
+       return talloc_asprintf(c, "cn=%s,%s", ADEX_CELL_RDN, c->dn);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ bool cell_search_forest(struct likewise_cell *c)
+{
+       uint32_t test_flags = LWCELL_FLAG_SEARCH_FOREST;
+
+       return ((c->flags & test_flags) == test_flags);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ uint32_t cell_flags(struct likewise_cell *c)
+{
+       if (!c)
+               return 0;
+
+       return c->flags;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ const char *cell_dns_domain(struct likewise_cell *c)
+{
+       if (!c)
+               return NULL;
+
+       return c->dns_domain;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+ ADS_STRUCT *cell_connection(struct likewise_cell *c)
+{
+       if (!c)
+               return NULL;
+
+       return c->conn;
+}
+
+/*
+ * Connection functions
+ */
+
+/********************************************************************
+ *******************************************************************/
+
+ NTSTATUS cell_connect(struct likewise_cell *c)
+{
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS ads_status;
+       fstring dc_name;
+       struct sockaddr_storage dcip;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       /* have to at least have the AD domain name */
+
+       if (!c->dns_domain) {
+               nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* clear out any old information */
+
+       if (c->conn) {
+               ads_destroy(&c->conn);
+               c->conn = NULL;
+       }
+
+       /* now setup the new connection */
+
+       ads = ads_init(c->dns_domain, NULL, NULL);
+       BAIL_ON_PTR_ERROR(ads, nt_status);
+
+       ads->auth.password =
+           secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+       ads->auth.realm = SMB_STRDUP(lp_realm());
+
+       /* Make the connection.  We should already have an initial
+          TGT using the machine creds */
+
+       if (cell_flags(c) & LWCELL_FLAG_GC_CELL) {
+               ads_status = ads_connect_gc(ads);
+       } else {
+         /* Set up server affinity for normal cells and the client
+            site name cache */
+
+         if (!get_dc_name("", c->dns_domain, dc_name, &dcip)) {
+           nt_status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+           BAIL_ON_NTSTATUS_ERROR(nt_status);
+         }
+
+         ads_status = ads_connect(ads);
+       }
+
+
+       c->conn = ads;
+
+       nt_status = ads_ntstatus(ads_status);
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               ads_destroy(&ads);
+               c->conn = NULL;
+       }
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+ NTSTATUS cell_connect_dn(struct likewise_cell **c, const char *dn)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct likewise_cell *new_cell = NULL;
+       char *dns_domain = NULL;
+
+       if (*c || !dn) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       if ((new_cell = cell_new()) == NULL) {
+               nt_status = NT_STATUS_NO_MEMORY;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Set the DNS domain, dn, etc ... and add it to the list */
+
+       dns_domain = cell_dn_to_dns(dn);
+       cell_set_dns_domain(new_cell, dns_domain);
+       SAFE_FREE(dns_domain);
+
+       cell_set_dn(new_cell, dn);
+
+       nt_status = cell_connect(new_cell);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       *c = new_cell;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(1,("LWI: Failled to connect to cell \"%s\" (%s)\n",
+                        dn ? dn : "NULL", nt_errstr(nt_status)));
+               talloc_destroy(new_cell);
+       }
+
+       return nt_status;
+}
+
+
+/********************************************************************
+ *******************************************************************/
+
+#define MAX_SEARCH_COUNT    2
+
+ ADS_STATUS cell_do_search(struct likewise_cell *c,
+                         const char *search_base,
+                         int scope,
+                         const char *expr,
+                         const char **attrs,
+                         LDAPMessage ** msg)
+{
+       int search_count = 0;
+       ADS_STATUS status;
+       NTSTATUS nt_status;
+
+       /* check for a NULL connection */
+
+       if (!c->conn) {
+               nt_status = cell_connect(c);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       status = ADS_ERROR_NT(nt_status);
+                       return status;
+               }
+       }
+
+       DEBUG(10, ("cell_do_search: Base = %s,  Filter = %s, Scope = %d, GC = %s\n",
+                  search_base, expr, scope,
+                  c->conn->server.gc ? "yes" : "no"));
+
+       /* we try multiple times in case the ADS_STRUCT is bad
+          and we need to reconnect */
+
+       while (search_count < MAX_SEARCH_COUNT) {
+               *msg = NULL;
+               status = ads_do_search(c->conn, search_base,
+                                      scope, expr, attrs, msg);
+               if (ADS_ERR_OK(status)) {
+                       return status;
+               }
+
+
+               DEBUG(5, ("cell_do_search: search[%d] failed (%s)\n",
+                         search_count, ads_errstr(status)));
+
+               search_count++;
+
+               /* Houston, we have a problem */
+
+               if (status.error_type == ENUM_ADS_ERROR_LDAP) {
+                       switch (status.err.rc) {
+                       case LDAP_TIMELIMIT_EXCEEDED:
+                       case LDAP_TIMEOUT:
+                       case -1:        /* we get this error if we cannot contact
+                                          the LDAP server */
+                               nt_status = cell_connect(c);
+                               if (!NT_STATUS_IS_OK(nt_status)) {
+                                       status = ADS_ERROR_NT(nt_status);
+                                       return status;
+                               }
+                               break;
+                       default:
+                               /* we're all done here */
+                               return status;
+                       }
+               }
+       }
+
+       DEBUG(5, ("cell_do_search: exceeded maximum search count!\n"));
+
+       return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+}
diff --git a/source3/winbindd/idmap_adex/provider_unified.c b/source3/winbindd/idmap_adex/provider_unified.c
new file mode 100644 (file)
index 0000000..f185347
--- /dev/null
@@ -0,0 +1,1180 @@
+/*
+ * idmap_adex
+ *
+ * Provider for RFC2307 and SFU AD Forests
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2006-2008
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "idmap_adex.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* Information needed by the LDAP search filters */
+
+enum filterType { SidFilter, IdFilter, AliasFilter };
+
+struct lwcell_filter
+{
+       enum filterType ftype;
+       bool use2307;
+       union {
+               DOM_SID sid;
+               struct {
+                       uint32_t id;
+                       enum id_type type;
+               } id;
+               fstring alias;
+       } filter;
+};
+
+/********************************************************************
+ *******************************************************************/
+
+static char* build_id_filter(uint32_t id,
+                            enum id_type type,
+                            uint32_t search_flags)
+{
+       char *filter = NULL;
+       char *oc_filter, *attr_filter;
+       NTSTATUS nt_status;
+       TALLOC_CTX *frame = talloc_stackframe();
+       bool use2307 = ((search_flags & LWCELL_FLAG_USE_RFC2307_ATTRS)
+                       == LWCELL_FLAG_USE_RFC2307_ATTRS);
+       bool use_gc = ((search_flags & LWCELL_FLAG_SEARCH_FOREST)
+                      == LWCELL_FLAG_SEARCH_FOREST);
+       const char *oc;
+
+       /* Construct search filter for objectclass and attributes */
+
+       switch (type) {
+       case ID_TYPE_UID:
+               oc = ADEX_OC_USER;
+               if (use2307) {
+                       oc = ADEX_OC_POSIX_USER;
+                       if (use_gc) {
+                               oc = AD_USER;
+                       }
+               }
+               oc_filter = talloc_asprintf(frame, "objectclass=%s", oc);
+               attr_filter = talloc_asprintf(frame, "%s=%u",
+                                             ADEX_ATTR_UIDNUM, id);
+               break;
+
+       case ID_TYPE_GID:
+               oc = ADEX_OC_GROUP;
+               if (use2307) {
+                       oc = ADEX_OC_POSIX_GROUP;
+                       if (use_gc) {
+                               oc = AD_GROUP;
+                       }
+               }
+               oc_filter = talloc_asprintf(frame, "objectclass=%s", oc);
+               attr_filter = talloc_asprintf(frame, "%s=%u",
+                                             ADEX_ATTR_GIDNUM, id);
+               break;
+       default:
+               return NULL;
+       }
+
+       BAIL_ON_PTR_ERROR(oc_filter, nt_status);
+       BAIL_ON_PTR_ERROR(attr_filter, nt_status);
+
+       /* Use "keywords=%s" for non-schema cells */
+
+       if (use2307) {
+               filter = talloc_asprintf(frame, "(&(%s)(%s))",
+                                        oc_filter, attr_filter);
+       } else {
+               filter = talloc_asprintf(frame, "(&(keywords=%s)(keywords=%s))",
+                                        oc_filter, attr_filter);
+       }
+
+       talloc_destroy(oc_filter);
+       talloc_destroy(attr_filter);
+
+done:
+       /* Don't destroy the stackframe CTX since we are returning
+          memory from it */
+
+       return filter;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static char* build_alias_filter(const char *alias, uint32_t search_flags)
+{
+       char *filter = NULL;
+       char *user_attr_filter, *group_attr_filter;
+       NTSTATUS nt_status;
+       TALLOC_CTX *frame = talloc_stackframe();
+       bool use2307 = ((search_flags & LWCELL_FLAG_USE_RFC2307_ATTRS)
+                       == LWCELL_FLAG_USE_RFC2307_ATTRS);
+       bool search_forest = ((search_flags & LWCELL_FLAG_SEARCH_FOREST)
+                             == LWCELL_FLAG_SEARCH_FOREST);
+
+       /* Construct search filter for objectclass and attributes */
+
+       user_attr_filter = talloc_asprintf(frame, "%s=%s",
+                                          ADEX_ATTR_UID, alias);
+       group_attr_filter = talloc_asprintf(frame, "%s=%s",
+                                           ADEX_ATTR_DISPLAYNAME, alias);
+       BAIL_ON_PTR_ERROR(user_attr_filter, nt_status);
+       BAIL_ON_PTR_ERROR(group_attr_filter, nt_status);
+
+       /* Use "keywords=%s" for non-schema cells */
+
+       if (use2307) {
+               filter = talloc_asprintf(frame,
+                                        "(|(&(%s)(objectclass=%s))(&(%s)(objectclass=%s)))",
+                                        user_attr_filter,
+                                        search_forest ? AD_USER : ADEX_OC_POSIX_USER,
+                                        group_attr_filter,
+                                        search_forest ? AD_GROUP : ADEX_OC_POSIX_GROUP);
+       } else {
+               filter = talloc_asprintf(frame,
+                                        "(|(keywords=%s)(keywords=%s))",
+                                        user_attr_filter,
+                                        group_attr_filter);
+       }
+
+       talloc_destroy(user_attr_filter);
+       talloc_destroy(group_attr_filter);
+
+done:
+       /* Don't destroy the stackframe CTX since we are returning
+          memory from it */
+
+       return filter;
+}
+
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS search_cell(struct likewise_cell *c,
+                           LDAPMessage **msg,
+                           const struct lwcell_filter *fdata)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX* frame = talloc_stackframe();
+       char *filter = NULL;
+       const char *base = NULL;
+       ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+        const char *attrs[] = { "*", NULL };
+       int count;
+       char *sid_str;
+
+       /* get the filter and other search parameters */
+
+       switch (fdata->ftype) {
+       case SidFilter:
+               sid_str = sid_string_talloc(frame, &fdata->filter.sid);
+               BAIL_ON_PTR_ERROR(sid_str, nt_status);
+
+               filter = talloc_asprintf(frame, "(keywords=backLink=%s)",
+                                        sid_str);
+               break;
+       case IdFilter:
+               filter = build_id_filter(fdata->filter.id.id,
+                                        fdata->filter.id.type,
+                                        cell_flags(c));
+               break;
+       case AliasFilter:
+               filter = build_alias_filter(fdata->filter.alias,
+                                           cell_flags(c));
+               break;
+       default:
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               break;
+       }
+       BAIL_ON_PTR_ERROR(filter, nt_status);
+
+       base = cell_search_base(c);
+       BAIL_ON_PTR_ERROR(base, nt_status);
+
+       ads_status = cell_do_search(c, base, LDAP_SCOPE_SUBTREE,
+                                   filter, attrs, msg);
+
+       nt_status = ads_ntstatus(ads_status);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Now check that we got only one reply */
+
+       count = ads_count_replies(c->conn, *msg);
+       if (count < 1) {
+               nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       if ( count > 1) {
+               nt_status = NT_STATUS_DUPLICATE_NAME;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "search_cell", 4);
+
+       talloc_destroy(CONST_DISCARD(char*, base));
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS search_domain(struct likewise_cell **cell,
+                             LDAPMessage **msg,
+                             const char *dn,
+                             const DOM_SID *sid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX* frame = talloc_stackframe();
+       int count;
+
+       nt_status = dc_search_domains(cell, msg, dn, sid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Now check that we got only one reply */
+
+       count = ads_count_replies(cell_connection(*cell), *msg);
+       if (count < 1) {
+               nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       if ( count > 1) {
+               nt_status = NT_STATUS_DUPLICATE_NAME;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "search_domain", 4);
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+
+/********************************************************************
+ Check that a DN is within the forest scope.
+ *******************************************************************/
+
+static bool check_forest_scope(const char *dn)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *p = NULL;
+       char *q = NULL;
+       char *dns_domain = NULL;
+       struct winbindd_tdc_domain *domain;
+
+       /* If the DN does *not* contain "$LikewiseIdentityCell",
+          assume this is a schema mode forest and it is in the
+          forest scope by definition. */
+
+       if ((p = strstr_m(dn, ADEX_CELL_RDN)) == NULL) {
+               nt_status = NT_STATUS_OK;
+               goto done;
+       }
+
+       /* If this is a non-schema forest, then make sure that the DN
+          is in the form "...,cn=$LikewiseIdentityCell,DC=..." */
+
+       if ((q = strchr_m(p, ',')) == NULL) {
+               nt_status = NT_STATUS_OBJECT_NAME_INVALID;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       q++;
+       if (StrnCaseCmp(q, "dc=", 3) != 0) {
+               nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+
+       dns_domain = cell_dn_to_dns(q);
+       BAIL_ON_PTR_ERROR(dns_domain, nt_status);
+
+       domain = wcache_tdc_fetch_domain(frame, dns_domain);
+       if (!domain) {
+               nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       talloc_destroy(frame);
+       SAFE_FREE(dns_domain);
+
+       return NT_STATUS_IS_OK(nt_status);
+}
+
+
+
+/********************************************************************
+ Check that only one result was returned within the forest cell
+ scope.
+ *******************************************************************/
+
+static NTSTATUS check_result_unique_scoped(ADS_STRUCT **ads_list,
+                                          LDAPMessage **msg_list,
+                                          int num_resp,
+                                          char **dn,
+                                          DOM_SID *user_sid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       int i;
+       ADS_STRUCT *ads = NULL;
+       LDAPMessage *msg = NULL;
+       int count = 0;
+       char *entry_dn = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (!dn || !user_sid) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       *dn = NULL;
+
+       if (!ads_list || !msg_list || (num_resp == 0)) {
+               nt_status = NT_STATUS_NO_SUCH_FILE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Loop over all msgs */
+
+       for (i=0; i<num_resp; i++) {
+               LDAPMessage *e = ads_first_entry(ads_list[i], msg_list[i]);
+
+               while (e) {
+                       entry_dn = ads_get_dn(ads_list[i], e);
+                       BAIL_ON_PTR_ERROR(entry_dn, nt_status);
+
+                       if (check_forest_scope(entry_dn)) {
+                               count++;
+
+                               /* If we've already broken the condition, no
+                                  need to continue */
+
+                               if (count > 1) {
+                                       nt_status = NT_STATUS_DUPLICATE_NAME;
+                                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+                               }
+
+                               ads = ads_list[i];
+                               msg = e;
+                               *dn = SMB_STRDUP(entry_dn);
+                               BAIL_ON_PTR_ERROR((*dn), nt_status);
+                       }
+
+                       e = ads_next_entry(ads_list[i], e);
+                       SAFE_FREE(entry_dn);
+               }
+       }
+
+       if (!ads || !msg) {
+               nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* If we made is through the loop, then grab the user_sid and
+          run home to base */
+
+       /*
+          Try and get the SID from either objectSid or keywords.
+          We cannot use pull_sid() here since we want to try
+          both methods and not only one or the other (and we
+          have no full likewise_cell struct.
+
+          Fail if both are unavailable
+       */
+
+       if (!ads_pull_sid(ads, msg, "objectSid", user_sid)) {
+               char **keywords;
+               char *s;
+               size_t num_lines = 0;
+
+               keywords = ads_pull_strings(ads, frame, msg, "keywords",
+                                           &num_lines);
+               BAIL_ON_PTR_ERROR(keywords, nt_status);
+
+               s = find_attr_string(keywords, num_lines, "backLink");
+               if (!s) {
+                       nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+
+               if (!string_to_sid(user_sid, s)) {
+                       nt_status = NT_STATUS_INVALID_SID;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               SAFE_FREE(*dn);
+       }
+
+       talloc_destroy(frame);
+       SAFE_FREE(entry_dn);
+
+       return nt_status;
+}
+
+/********************************************************************
+ Search all forests.  Each forest can have it's own forest-cell
+ settings so we have to generate the filter for each search.
+ We don't use gc_search_all_forests() since we may have a different
+ schema model in each forest and need to construct the search
+ filter for each GC search.
+ *******************************************************************/
+
+static NTSTATUS search_forest(struct likewise_cell *forest_cell,
+                             LDAPMessage **msg,
+                             const struct lwcell_filter *fdata)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *filter = NULL;
+       char *dn = NULL;
+       struct gc_info *gc = NULL;
+       ADS_STRUCT **ads_list = NULL;
+       LDAPMessage **msg_list = NULL;
+       int num_resp = 0;
+       LDAPMessage *m;
+       DOM_SID user_sid;
+       struct likewise_cell *domain_cell = NULL;
+
+       if ((gc = gc_search_start()) == NULL) {
+               nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       while (gc) {
+               char *sid_binstr = NULL;
+               uint32_t flags = LWCELL_FLAG_SEARCH_FOREST;
+
+               m = NULL;
+
+               flags |= cell_flags(gc->forest_cell);
+
+               switch (fdata->ftype) {
+               case SidFilter:
+                       sid_binstr = sid_binstring(&fdata->filter.sid);
+                       BAIL_ON_PTR_ERROR(sid_binstr, nt_status);
+
+                       filter = talloc_asprintf(frame, "(objectSid=%s)", sid_binstr);
+                       SAFE_FREE(sid_binstr);
+                       break;
+               case IdFilter:
+                       filter = build_id_filter(fdata->filter.id.id,
+                                                fdata->filter.id.type, flags);
+                       break;
+               case AliasFilter:
+                       filter = build_alias_filter(fdata->filter.alias, flags);
+                       break;
+               }
+
+               /* First find the sparse object in GC */
+               nt_status = gc_search_forest(gc, &m, filter);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       gc = gc->next;
+                       continue;
+               }
+
+               nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
+                                                   m, &ads_list, &msg_list,
+                                                   &num_resp);
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+               gc = gc->next;
+       }
+
+       /* Uniqueness check across forests */
+
+       nt_status = check_result_unique_scoped(ads_list, msg_list, num_resp,
+                                              &dn, &user_sid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = search_domain(&domain_cell, &m, dn, &user_sid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Save the connection and results in the return parameters */
+
+       forest_cell->gc_search_cell = domain_cell;
+       *msg = m;
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "search_forest", 4);
+
+       SAFE_FREE(dn);
+
+       free_result_array(ads_list, msg_list, num_resp);
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS search_cell_list(struct likewise_cell **c,
+                                LDAPMessage **m,
+                                const struct lwcell_filter *fdata)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct likewise_cell *cell = NULL;
+       LDAPMessage *msg = NULL;
+       struct likewise_cell *result_cell = NULL;
+
+       if ((cell = cell_list_head()) == NULL) {
+               nt_status = NT_STATUS_INVALID_SERVER_STATE;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       while (cell) {
+               /* Clear any previous GC search results */
+
+               cell->gc_search_cell = NULL;
+
+               if (cell_search_forest(cell)) {
+                       nt_status = search_forest(cell, &msg, fdata);
+               } else {
+                       nt_status = search_cell(cell, &msg, fdata);
+               }
+
+               /* Always point to the search result cell.
+                  In forests this might be for another domain
+                  which means the schema model may be different */
+
+               result_cell = cell->gc_search_cell ?
+                       cell->gc_search_cell : cell;
+
+               /* Check if we are done */
+
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       break;
+               }
+
+               /* No luck.  Free memory and hit the next cell.
+                  Forest searches always set the gc_search_cell
+                  so give preference to that connection if possible. */
+
+               ads_msgfree(cell_connection(result_cell), msg);
+               msg = NULL;
+
+               cell = cell->next;
+       }
+
+       /* This might be assigning NULL but that is ok as long as we
+          give back the proper error code */
+
+       *c = result_cell;
+       *m = msg;
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "search_cell_list", 3);
+
+       return nt_status;
+}
+
+/********************************************************************
+ Pull the SID from an object which is always stored in the keywords
+ attribute as "backLink=S-1-5-21-..."
+ *******************************************************************/
+
+static NTSTATUS pull_sid(struct likewise_cell *c,
+                        LDAPMessage *msg,
+                        DOM_SID *sid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       ADS_STRUCT *ads = NULL;
+
+       ads = cell_connection(c);
+
+       /*
+          We have two ways of getting the sid:
+          (a) from the objectSID in case of a GC search,
+          (b) from backLink in the case of a cell search.
+          Pull the keywords attributes and grab the backLink.
+       */
+
+       if (!ads_pull_sid(ads, msg, "objectSid", sid)) {
+               char **keywords;
+               char *s;
+               size_t num_lines = 0;
+
+               keywords = ads_pull_strings(ads, frame, msg,
+                                           "keywords", &num_lines);
+               BAIL_ON_PTR_ERROR(keywords, nt_status);
+
+               s = find_attr_string(keywords, num_lines, "backLink");
+               if (!s) {
+                       nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+
+               if (!string_to_sid(sid, s)) {
+                       nt_status = NT_STATUS_INVALID_SID;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS get_object_type(struct likewise_cell *c,
+                               LDAPMessage *msg,
+                               enum id_type *type)
+{
+       TALLOC_CTX *ctx = talloc_stackframe();
+       char **oc_list = NULL;
+       NTSTATUS nt_status = NT_STATUS_OK;
+       size_t list_size = 0;
+       char *s = NULL;
+       ADS_STRUCT *ads = NULL;
+
+       ads = cell_connection(c);
+
+       /* Deal with RFC 2307 support first */
+
+       if (cell_flags(c) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
+               oc_list = ads_pull_strings(ads, ctx, msg,
+                                          "objectClass", &list_size);
+               if (!oc_list) {
+                       nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       goto done;
+               }
+
+               /* Check for posix classes and AD classes */
+
+               if (is_object_class(oc_list, list_size, ADEX_OC_POSIX_USER)
+                   || is_object_class(oc_list, list_size, AD_USER)) {
+                       *type = ID_TYPE_UID;
+               } else if (is_object_class(oc_list, list_size, ADEX_OC_POSIX_GROUP)
+                          || is_object_class(oc_list, list_size, AD_GROUP)) {
+                       *type = ID_TYPE_GID;
+               } else {
+                       *type = ID_TYPE_NOT_SPECIFIED;
+                       nt_status = NT_STATUS_INVALID_PARAMETER;
+               }
+       } else {
+               /* Default to non-schema mode */
+
+               oc_list = ads_pull_strings(ads, ctx, msg,
+                                          "keywords", &list_size);
+               if (!oc_list) {
+                       nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       goto done;
+               }
+
+               s = find_attr_string(oc_list, list_size, "objectClass");
+               if (!s) {
+                       nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       goto done;
+               }
+
+               if (strequal(s, ADEX_OC_USER)) {
+                       *type = ID_TYPE_UID;
+               } else if (strequal(s, ADEX_OC_GROUP)) {
+                       *type = ID_TYPE_GID;
+               } else {
+                       *type = ID_TYPE_NOT_SPECIFIED;
+                       nt_status = NT_STATUS_INVALID_PARAMETER;
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       talloc_destroy(ctx);
+
+       return nt_status;
+}
+
+/********************************************************************
+ Pull an attribute uint32_t  value
+ *******************************************************************/
+
+static NTSTATUS get_object_uint32(struct likewise_cell *c,
+                                 LDAPMessage *msg,
+                                 const char *attrib,
+                                 uint32_t *x)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char **keywords = NULL;
+       size_t list_size = 0;
+       TALLOC_CTX *frame = talloc_stackframe();
+       ADS_STRUCT *ads = NULL;
+
+       ads = cell_connection(c);
+
+       /* Deal with RFC2307 schema */
+
+       if (cell_flags(c) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
+               if (!ads_pull_uint32(ads, msg, attrib, x)) {
+                       nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+       } else {
+               /* Non-schema mode */
+               char *s = NULL;
+               uint32_t num;
+
+               keywords = ads_pull_strings(ads, frame, msg, "keywords",
+                                           &list_size);
+               BAIL_ON_PTR_ERROR(keywords, nt_status);
+
+               s = find_attr_string(keywords, list_size, attrib);
+               if (!s) {
+                       nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+
+               num = strtoll(s, NULL, 10);
+               if (errno == ERANGE) {
+                       nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+               *x = num;
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS get_object_id(struct likewise_cell *c,
+                             LDAPMessage *msg,
+                             enum id_type type,
+                             uint32_t *id)
+{
+       NTSTATUS nt_status = NT_STATUS_OK;
+       const char *id_attr;
+
+       /* Figure out which attribute we need to pull */
+
+       switch (type) {
+       case ID_TYPE_UID:
+               id_attr = ADEX_ATTR_UIDNUM;
+               break;
+       case ID_TYPE_GID:
+               id_attr = ADEX_ATTR_GIDNUM;
+               break;
+       default:
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+               break;
+       }
+
+       nt_status = get_object_uint32(c, msg, id_attr, id);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       return nt_status;
+}
+
+/********************************************************************
+ Pull the uid/gid and type from an object.  This differs depending on
+ the cell flags.
+ *******************************************************************/
+
+static NTSTATUS pull_id(struct likewise_cell *c,
+                       LDAPMessage *msg,
+                       uint32_t *id,
+                       enum id_type *type)
+{
+       NTSTATUS nt_status;
+
+       nt_status = get_object_type(c, msg, type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = get_object_id(c, msg, *type, id);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       return nt_status;
+}
+
+/********************************************************************
+ Pull an attribute string value
+ *******************************************************************/
+
+static NTSTATUS get_object_string(struct likewise_cell *c,
+                                 LDAPMessage *msg,
+                                 TALLOC_CTX *ctx,
+                                 const char *attrib,
+                                 char **string)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char **keywords = NULL;
+       size_t list_size = 0;
+       TALLOC_CTX *frame = talloc_stackframe();
+       ADS_STRUCT *ads = NULL;
+
+       *string = NULL;
+
+       ads = cell_connection(c);
+
+       /* Deal with RFC2307 schema */
+
+       if (cell_flags(c) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
+               *string = ads_pull_string(ads, ctx, msg, attrib);
+       } else {
+               /* Non-schema mode */
+
+               char *s = NULL;
+
+               keywords = ads_pull_strings(ads, frame, msg,
+                                           "keywords", &list_size);
+               if (!keywords) {
+                       nt_status = NT_STATUS_NO_MEMORY;
+                       BAIL_ON_NTSTATUS_ERROR(nt_status);
+               }
+               s = find_attr_string(keywords, list_size, attrib);
+               if (s) {
+                       *string = talloc_strdup(ctx, s);
+               }
+       }
+
+       if (!*string) {
+               nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ Pull the struct passwd fields for a user
+ *******************************************************************/
+
+static NTSTATUS pull_nss_info(struct likewise_cell *c,
+                             LDAPMessage *msg,
+                             TALLOC_CTX *ctx,
+                             char **homedir,
+                             char **shell,
+                             char **gecos,
+                             gid_t *p_gid)
+{
+       NTSTATUS nt_status;
+
+       nt_status = get_object_string(c, msg, ctx, ADEX_ATTR_HOMEDIR, homedir);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = get_object_string(c, msg, ctx, ADEX_ATTR_SHELL, shell);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = get_object_string(c, msg, ctx, ADEX_ATTR_GECOS, gecos);
+       /* Gecos is often not set so ignore failures */
+
+       nt_status = get_object_uint32(c, msg, ADEX_ATTR_GIDNUM, p_gid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       return nt_status;
+}
+
+/********************************************************************
+ Pull the struct passwd fields for a user
+ *******************************************************************/
+
+static NTSTATUS pull_alias(struct likewise_cell *c,
+                          LDAPMessage *msg,
+                          TALLOC_CTX *ctx,
+                          char **alias)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       enum id_type type;
+       const char *attr = NULL;
+
+       /* Figure out if this is a user or a group */
+
+       nt_status = get_object_type(c, msg, &type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       switch (type) {
+       case ID_TYPE_UID:
+               attr = ADEX_ATTR_UID;
+               break;
+       case ID_TYPE_GID:
+               /* What is the group attr for RFC2307 Forests? */
+               attr = ADEX_ATTR_DISPLAYNAME;
+               break;
+       default:
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+               break;
+       }
+
+       nt_status = get_object_string(c, msg, ctx, attr, alias);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS _ccp_get_sid_from_id(DOM_SID * sid,
+                                    uint32_t id, enum id_type type)
+{
+       struct likewise_cell *cell = NULL;
+       LDAPMessage *msg = NULL;
+       NTSTATUS nt_status;
+       struct lwcell_filter filter;
+
+       filter.ftype = IdFilter;
+       filter.filter.id.id = id;
+       filter.filter.id.type = type;
+
+       nt_status = search_cell_list(&cell, &msg, &filter);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = pull_sid(cell, msg, sid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       ads_msgfree(cell->conn, msg);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS _ccp_get_id_from_sid(uint32_t * id,
+                                    enum id_type *type,
+                                    const DOM_SID * sid)
+{
+       struct likewise_cell *cell = NULL;
+       LDAPMessage *msg = NULL;
+       NTSTATUS nt_status;
+       struct lwcell_filter filter;
+
+       filter.ftype = SidFilter;
+       sid_copy(&filter.filter.sid, sid);
+
+       nt_status = search_cell_list(&cell, &msg, &filter);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = pull_id(cell, msg, id, type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if (*id < min_id_value()) {
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+done:
+       ads_msgfree(cell->conn, msg);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static NTSTATUS _ccp_nss_get_info(const DOM_SID * sid,
+                                 TALLOC_CTX * ctx,
+                                 char **homedir,
+                                 char **shell,
+                                 char **gecos, gid_t * p_gid)
+{
+       struct likewise_cell *cell = NULL;
+       LDAPMessage *msg = NULL;
+       NTSTATUS nt_status;
+       struct lwcell_filter filter;
+       enum id_type type;
+
+       filter.ftype = SidFilter;
+       sid_copy(&filter.filter.sid, sid);
+
+       nt_status = search_cell_list(&cell, &msg, &filter);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = get_object_type(cell, msg, &type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if (type != ID_TYPE_UID) {
+               nt_status = NT_STATUS_NO_SUCH_USER;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       nt_status = pull_nss_info(cell, msg, ctx, homedir, shell, gecos,
+                                 (uint32_t*) p_gid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       ads_msgfree(cell->conn, msg);
+
+       return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS _ccp_map_to_alias(TALLOC_CTX *ctx,
+                                 const char *domain,
+                                 const char *name, char **alias)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID sid;
+       struct likewise_cell *cell = NULL;
+       LDAPMessage *msg = NULL;
+       struct lwcell_filter filter;
+       enum lsa_SidType sid_type;
+
+       /* Convert the name to a SID */
+
+       nt_status = gc_name_to_sid(domain, name, &sid, &sid_type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Find the user/group */
+
+       filter.ftype = SidFilter;
+       sid_copy(&filter.filter.sid, &sid);
+
+       nt_status = search_cell_list(&cell, &msg, &filter);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Pull the alias and return */
+
+       nt_status = pull_alias(cell, msg, ctx, alias);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "map_to_alias", 3);
+
+       talloc_destroy(frame);
+       ads_msgfree(cell_connection(cell), msg);
+
+       return nt_status;
+}
+
+/**********************************************************************
+ Map from an alias name to the canonical, qualified name.
+ Ensure that the alias is only pull from the closest in which
+ the user or gorup is enabled in
+ *********************************************************************/
+
+static NTSTATUS _ccp_map_from_alias(TALLOC_CTX *mem_ctx,
+                                   const char *domain,
+                                   const char *alias, char **name)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID sid;
+       struct likewise_cell *cell_alias = NULL;
+       LDAPMessage *msg_alias = NULL;
+       struct likewise_cell *cell_sid = NULL;
+       LDAPMessage *msg_sid = NULL;
+       struct lwcell_filter filter;
+       char *canonical_name = NULL;
+       enum lsa_SidType type;
+
+       /* Find the user/group */
+
+       filter.ftype = AliasFilter;
+       fstrcpy(filter.filter.alias, alias);
+
+       nt_status = search_cell_list(&cell_alias, &msg_alias, &filter);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       nt_status = pull_sid(cell_alias, msg_alias, &sid);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       /* Now search again for the SID according to the cell list.
+          Verify that the cell of both search results is the same
+          so that we only match an alias from the closest cell
+          in which a user/group has been instantied. */
+
+       filter.ftype = SidFilter;
+       sid_copy(&filter.filter.sid, &sid);
+
+       nt_status = search_cell_list(&cell_sid, &msg_sid, &filter);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       if (cell_alias != cell_sid) {
+               nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+               BAIL_ON_NTSTATUS_ERROR(nt_status);
+       }
+
+       /* Finally do the GC sid/name conversion */
+
+       nt_status = gc_sid_to_name(&sid, &canonical_name, &type);
+       BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+       *name = talloc_strdup(mem_ctx, canonical_name);
+       BAIL_ON_PTR_ERROR((*name), nt_status);
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       PRINT_NTSTATUS_ERROR(nt_status, "map_from_alias", 3);
+
+       ads_msgfree(cell_connection(cell_alias), msg_alias);
+       ads_msgfree(cell_connection(cell_sid), msg_sid);
+
+       SAFE_FREE(canonical_name);
+
+       talloc_destroy(frame);
+
+       return nt_status;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+struct cell_provider_api ccp_unified = {
+       .get_sid_from_id = _ccp_get_sid_from_id,
+       .get_id_from_sid = _ccp_get_id_from_sid,
+       .get_nss_info    = _ccp_nss_get_info,
+       .map_to_alias    = _ccp_map_to_alias,
+       .map_from_alias  = _ccp_map_from_alias
+};