summaryrefslogtreecommitdiff
path: root/source3/winbindd/idmap_adex/gc_util.c
diff options
context:
space:
mode:
authorGerald (Jerry) Carter <jerry@samba.org>2008-09-19 12:27:15 -0500
committerGerald W. Carter <jerry@samba.org>2008-09-22 15:46:19 -0700
commit7d5fb989ac9942a6f3394853f6930d34ef6adf7b (patch)
treec35d3bed0d589d607cc4c9d1ba75dd9f89f85c0b /source3/winbindd/idmap_adex/gc_util.c
parentb18449dbd587e978a65d8de3b8df96371d4bcdcb (diff)
downloadsamba-7d5fb989ac9942a6f3394853f6930d34ef6adf7b.tar.gz
samba-7d5fb989ac9942a6f3394853f6930d34ef6adf7b.tar.bz2
samba-7d5fb989ac9942a6f3394853f6930d34ef6adf7b.zip
idmap_adex: Add new idmap plugin for support RFC2307 enabled AD forests.
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
Diffstat (limited to 'source3/winbindd/idmap_adex/gc_util.c')
-rw-r--r--source3/winbindd/idmap_adex/gc_util.c848
1 files changed, 848 insertions, 0 deletions
diff --git a/source3/winbindd/idmap_adex/gc_util.c b/source3/winbindd/idmap_adex/gc_util.c
new file mode 100644
index 0000000000..87dd3c058d
--- /dev/null
+++ b/source3/winbindd/idmap_adex/gc_util.c
@@ -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;
+}