diff options
-rw-r--r-- | source3/Makefile.in | 12 | ||||
-rw-r--r-- | source3/configure.in | 1 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/cell_util.c | 292 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/domain_util.c | 278 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/gc_util.c | 848 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/idmap_adex.c | 460 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/idmap_adex.h | 257 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/likewise_cell.c | 425 | ||||
-rw-r--r-- | source3/winbindd/idmap_adex/provider_unified.c | 1180 |
9 files changed, 3753 insertions, 0 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 8304981a1f..1ce0ce9067 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 diff --git a/source3/configure.in b/source3/configure.in index 640afc47fb..d1000d9a52 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -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 index 0000000000..f5c08a0454 --- /dev/null +++ b/source3/winbindd/idmap_adex/cell_util.c @@ -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 index 0000000000..ab31ccef7a --- /dev/null +++ b/source3/winbindd/idmap_adex/domain_util.c @@ -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 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; +} diff --git a/source3/winbindd/idmap_adex/idmap_adex.c b/source3/winbindd/idmap_adex/idmap_adex.c new file mode 100644 index 0000000000..23ab843e95 --- /dev/null +++ b/source3/winbindd/idmap_adex/idmap_adex.c @@ -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 index 0000000000..f91bba8d07 --- /dev/null +++ b/source3/winbindd/idmap_adex/idmap_adex.h @@ -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 index 0000000000..77eeee406b --- /dev/null +++ b/source3/winbindd/idmap_adex/likewise_cell.c @@ -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 index 0000000000..f18534797e --- /dev/null +++ b/source3/winbindd/idmap_adex/provider_unified.c @@ -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 +}; |