summaryrefslogtreecommitdiff
path: root/source3/winbindd
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2008-10-21 14:51:13 +0200
committerJelmer Vernooij <jelmer@samba.org>2008-10-21 14:51:13 +0200
commit5209a846a9157e649fcdcb561f7eaf19c8c0e465 (patch)
treeb0a7e52b5646c8eec182dbc391e7934b6804488c /source3/winbindd
parent625359b2e266105022309df8985720108ecd6f67 (diff)
parent2ee8d29d22bcb1c350ab59d71b0aee548489bc9c (diff)
downloadsamba-5209a846a9157e649fcdcb561f7eaf19c8c0e465.tar.gz
samba-5209a846a9157e649fcdcb561f7eaf19c8c0e465.tar.bz2
samba-5209a846a9157e649fcdcb561f7eaf19c8c0e465.zip
Merge branch 'master' of ssh://git.samba.org/data/git/samba into regsrv
Conflicts: source4/lib/registry/ldb.c source4/rpc_server/winreg/rpc_winreg.c
Diffstat (limited to 'source3/winbindd')
-rw-r--r--source3/winbindd/idmap_ad.c260
-rw-r--r--source3/winbindd/idmap_adex/cell_util.c292
-rw-r--r--source3/winbindd/idmap_adex/domain_util.c286
-rw-r--r--source3/winbindd/idmap_adex/gc_util.c848
-rw-r--r--source3/winbindd/idmap_adex/idmap_adex.c460
-rw-r--r--source3/winbindd/idmap_adex/idmap_adex.h257
-rw-r--r--source3/winbindd/idmap_adex/likewise_cell.c443
-rw-r--r--source3/winbindd/idmap_adex/provider_unified.c1180
-rw-r--r--source3/winbindd/idmap_hash/idmap_hash.c393
-rw-r--r--source3/winbindd/idmap_hash/idmap_hash.h60
-rw-r--r--source3/winbindd/idmap_hash/mapfile.c175
-rw-r--r--source3/winbindd/idmap_tdb.c2
-rw-r--r--source3/winbindd/idmap_tdb2.c19
-rw-r--r--source3/winbindd/idmap_util.c2
-rw-r--r--source3/winbindd/nss_info.c41
-rw-r--r--source3/winbindd/nss_info_template.c32
-rw-r--r--source3/winbindd/winbindd.c4
-rw-r--r--source3/winbindd/winbindd_ads.c20
-rw-r--r--source3/winbindd/winbindd_async.c2
-rw-r--r--source3/winbindd/winbindd_cache.c249
-rw-r--r--source3/winbindd/winbindd_cm.c2
-rw-r--r--source3/winbindd/winbindd_dual.c43
-rw-r--r--source3/winbindd/winbindd_group.c190
-rw-r--r--source3/winbindd/winbindd_locator.c61
-rw-r--r--source3/winbindd/winbindd_pam.c62
-rw-r--r--source3/winbindd/winbindd_proto.h24
-rw-r--r--source3/winbindd/winbindd_rpc.c51
-rw-r--r--source3/winbindd/winbindd_user.c74
-rw-r--r--source3/winbindd/winbindd_util.c156
29 files changed, 5545 insertions, 143 deletions
diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index 9fefb1bba7..60a2d8642a 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -517,6 +517,8 @@ again:
bidx = idx;
for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
+ ids[idx]->status = ID_UNKNOWN;
+
sidstr = sid_binstring(ids[idx]->sid);
filter = talloc_asprintf_append_buffer(filter, "(objectSid=%s)", sidstr);
@@ -732,6 +734,16 @@ static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e,
uint32 *gid )
{
ADS_STRUCT *ads_internal = NULL;
+ const char *attrs[] = {NULL, /* attr_homedir */
+ NULL, /* attr_shell */
+ NULL, /* attr_gecos */
+ NULL, /* attr_gidnumber */
+ NULL };
+ char *filter = NULL;
+ LDAPMessage *msg_internal = NULL;
+ ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ char *sidstr = NULL;
/* Only do query if we are online */
if (idmap_is_offline()) {
@@ -743,24 +755,224 @@ static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e,
ads_internal = ad_idmap_cached_connection();
- if ( !ads_internal || !ad_schema )
+ if ( !ads_internal || !ad_schema ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-
- if ( !homedir || !shell || !gecos )
+ }
+
+ if (!sid || !homedir || !shell || !gecos) {
return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* See if we can use the ADS connection struct swe were given */
- *homedir = ads_pull_string( ads, ctx, msg, ad_schema->posix_homedir_attr );
- *shell = ads_pull_string( ads, ctx, msg, ad_schema->posix_shell_attr );
- *gecos = ads_pull_string( ads, ctx, msg, ad_schema->posix_gecos_attr );
-
- if ( gid ) {
- if ( !ads_pull_uint32(ads, msg, ad_schema->posix_gidnumber_attr, gid ) )
- *gid = (uint32)-1;
+ if (ads) {
+ *homedir = ads_pull_string( ads, ctx, msg, ad_schema->posix_homedir_attr );
+ *shell = ads_pull_string( ads, ctx, msg, ad_schema->posix_shell_attr );
+ *gecos = ads_pull_string( ads, ctx, msg, ad_schema->posix_gecos_attr );
+
+ if (gid) {
+ if ( !ads_pull_uint32(ads, msg, ad_schema->posix_gidnumber_attr, gid ) )
+ *gid = (uint32)-1;
+ }
+
+ nt_status = NT_STATUS_OK;
+ goto done;
}
-
- return NT_STATUS_OK;
+
+ /* Have to do our own query */
+
+ attrs[0] = ad_schema->posix_homedir_attr;
+ attrs[1] = ad_schema->posix_shell_attr;
+ attrs[2] = ad_schema->posix_gecos_attr;
+ attrs[3] = ad_schema->posix_gidnumber_attr;
+
+ sidstr = sid_binstring(sid);
+ filter = talloc_asprintf(ctx, "(objectSid=%s)", sidstr);
+ SAFE_FREE(sidstr);
+
+ if (!filter) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ads_status = ads_search_retry(ads_internal, &msg_internal, filter, attrs);
+ if (!ADS_ERR_OK(ads_status)) {
+ nt_status = ads_ntstatus(ads_status);
+ goto done;
+ }
+
+ *homedir = ads_pull_string(ads_internal, ctx, msg_internal, ad_schema->posix_homedir_attr);
+ *shell = ads_pull_string(ads_internal, ctx, msg_internal, ad_schema->posix_shell_attr);
+ *gecos = ads_pull_string(ads_internal, ctx, msg_internal, ad_schema->posix_gecos_attr);
+
+ if (gid) {
+ if (!ads_pull_uint32(ads_internal, msg_internal, ad_schema->posix_gidnumber_attr, gid))
+ *gid = (uint32)-1;
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (msg_internal) {
+ ads_msgfree(ads_internal, msg_internal);
+ }
+
+ return nt_status;
}
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *name,
+ char **alias)
+{
+ ADS_STRUCT *ads_internal = NULL;
+ const char *attrs[] = {NULL, /* attr_uid */
+ NULL };
+ char *filter = NULL;
+ LDAPMessage *msg = NULL;
+ ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ /* Check incoming parameters */
+
+ if ( !domain || !name || !*alias) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Only do query if we are online */
+
+ if (idmap_is_offline()) {
+ nt_status = NT_STATUS_FILE_IS_OFFLINE;
+ goto done;
+ }
+
+ ads_internal = ad_idmap_cached_connection();
+
+ if (!ads_internal || !ad_schema) {
+ nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ attrs[0] = ad_schema->posix_uid_attr;
+
+ filter = talloc_asprintf(mem_ctx,
+ "(sAMAccountName=%s)",
+ name);
+ if (!filter) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ads_status = ads_search_retry(ads_internal, &msg, filter, attrs);
+ if (!ADS_ERR_OK(ads_status)) {
+ nt_status = ads_ntstatus(ads_status);
+ goto done;
+ }
+
+ *alias = ads_pull_string(ads_internal, mem_ctx, msg, ad_schema->posix_uid_attr );
+
+ if (!*alias) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (filter) {
+ talloc_destroy(filter);
+ }
+ if (msg) {
+ ads_msgfree(ads_internal, msg);
+ }
+
+ return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *alias,
+ char **name )
+{
+ ADS_STRUCT *ads_internal = NULL;
+ const char *attrs[] = {"sAMAccountName",
+ NULL };
+ char *filter = NULL;
+ LDAPMessage *msg = NULL;
+ ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ char *username;
+
+ /* Check incoming parameters */
+
+ if ( !alias || !name) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Only do query if we are online */
+
+ if (idmap_is_offline()) {
+ nt_status = NT_STATUS_FILE_IS_OFFLINE;
+ goto done;
+ }
+
+ ads_internal = ad_idmap_cached_connection();
+
+ if (!ads_internal || !ad_schema) {
+ nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(%s=%s)",
+ ad_schema->posix_uid_attr,
+ alias);
+ if (!filter) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ads_status = ads_search_retry(ads_internal, &msg, filter, attrs);
+ if (!ADS_ERR_OK(ads_status)) {
+ nt_status = ads_ntstatus(ads_status);
+ goto done;
+ }
+
+ username = ads_pull_string(ads_internal, mem_ctx, msg,
+ "sAMAccountName");
+ if (!username) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ *name = talloc_asprintf(mem_ctx, "%s\\%s",
+ lp_workgroup(),
+ username);
+ if (!*name) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (filter) {
+ talloc_destroy(filter);
+ }
+ if (msg) {
+ ads_msgfree(ads_internal, msg);
+ }
+
+ return nt_status;
+}
+
+
/************************************************************************
***********************************************************************/
@@ -786,21 +998,27 @@ static struct idmap_methods ad_methods = {
function which sets the intended schema model to use */
static struct nss_info_methods nss_rfc2307_methods = {
- .init = nss_rfc2307_init,
- .get_nss_info = nss_ad_get_info,
- .close_fn = nss_ad_close
+ .init = nss_rfc2307_init,
+ .get_nss_info = nss_ad_get_info,
+ .map_to_alias = nss_ad_map_to_alias,
+ .map_from_alias = nss_ad_map_from_alias,
+ .close_fn = nss_ad_close
};
static struct nss_info_methods nss_sfu_methods = {
- .init = nss_sfu_init,
- .get_nss_info = nss_ad_get_info,
- .close_fn = nss_ad_close
+ .init = nss_sfu_init,
+ .get_nss_info = nss_ad_get_info,
+ .map_to_alias = nss_ad_map_to_alias,
+ .map_from_alias = nss_ad_map_from_alias,
+ .close_fn = nss_ad_close
};
static struct nss_info_methods nss_sfu20_methods = {
- .init = nss_sfu20_init,
- .get_nss_info = nss_ad_get_info,
- .close_fn = nss_ad_close
+ .init = nss_sfu20_init,
+ .get_nss_info = nss_ad_get_info,
+ .map_to_alias = nss_ad_map_to_alias,
+ .map_from_alias = nss_ad_map_from_alias,
+ .close_fn = nss_ad_close
};
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..6851503cc8
--- /dev/null
+++ b/source3/winbindd/idmap_adex/domain_util.c
@@ -0,0 +1,286 @@
+/*
+ * 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;
+
+ if (!domain) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10,("dc_add_domain: Attempting to add domain %s\n", domain));
+
+ /* 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;
+
+ DEBUG(5,("dc_add_domain: Successfully added %s\n", domain));
+
+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..6dc02336d5
--- /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 NETLOGON_SAM_LOGON_RESPONSE_EX 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..7596b1cbd8
--- /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;
+}
+
+static 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..e068d5c340
--- /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);
+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..7723b3e015
--- /dev/null
+++ b/source3/winbindd/idmap_adex/likewise_cell.c
@@ -0,0 +1,443 @@
+/*
+ * 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)) {
+ if (DEBUGLEVEL >= 10) {
+ LDAPMessage *e = NULL;
+
+ int n = ads_count_replies(c->conn, *msg);
+
+ DEBUG(10,("cell_do_search: Located %d entries\n", n));
+
+ for (e=ads_first_entry(c->conn, *msg);
+ e!=NULL;
+ e = ads_next_entry(c->conn, e))
+ {
+ char *dn = ads_get_dn(c->conn, e);
+
+ DEBUGADD(10,(" dn: %s\n", dn ? dn : "<NULL>"));
+ SAFE_FREE(dn);
+ }
+ }
+
+ 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
+};
diff --git a/source3/winbindd/idmap_hash/idmap_hash.c b/source3/winbindd/idmap_hash/idmap_hash.c
new file mode 100644
index 0000000000..a050f99bc8
--- /dev/null
+++ b/source3/winbindd/idmap_hash/idmap_hash.c
@@ -0,0 +1,393 @@
+/*
+ * idmap_hash.c
+ *
+ * Copyright (C) Gerald Carter <jerry@samba.org> 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "includes.h"
+#include "winbindd/winbindd.h"
+#include "idmap_hash.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct sid_hash_table {
+ DOM_SID *sid;
+};
+
+struct sid_hash_table *hashed_domains = NULL;
+
+/*********************************************************************
+ Hash a domain SID (S-1-5-12-aaa-bbb-ccc) to a 12bit number
+ ********************************************************************/
+
+static uint32_t hash_domain_sid(const DOM_SID *sid)
+{
+ uint32_t hash;
+
+ if (sid->num_auths != 4)
+ return 0;
+
+ /* XOR the last three subauths */
+
+ hash = ((sid->sub_auths[1] ^ sid->sub_auths[2]) ^ sid->sub_auths[3]);
+
+ /* Take all 32-bits into account when generating the 12-bit
+ hash value */
+ hash = (((hash & 0xFFF00000) >> 20)
+ + ((hash & 0x000FFF00) >> 8)
+ + (hash & 0x000000FF)) & 0x0000FFF;
+
+ /* return a 12-bit hash value */
+
+ return hash;
+}
+
+/*********************************************************************
+ Hash a Relative ID to a 20 bit number
+ ********************************************************************/
+
+static uint32_t hash_rid(uint32_t rid)
+{
+ /* 20 bits for the rid which allows us to support
+ the first 100K users/groups in a domain */
+
+ return (rid & 0x0007FFFF);
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static uint32_t combine_hashes(uint32_t h_domain,
+ uint32_t h_rid)
+{
+ uint32_t return_id = 0;
+
+ /* shift the hash_domain 19 bits to the left and OR with the
+ hash_rid */
+
+ return_id = ((h_domain<<19) | h_rid);
+
+ return return_id;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static void separate_hashes(uint32_t id,
+ uint32_t *h_domain,
+ uint32_t *h_rid)
+{
+ *h_rid = id & 0x0007FFFF;
+ *h_domain = (id & 0x7FF80000) >> 19;
+
+ return;
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS be_init(struct idmap_domain *dom,
+ const char *params)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ int i;
+
+ /* If the domain SID hash talbe has been initialized, assume
+ that we completed this function previously */
+
+ if ( hashed_domains ) {
+ nt_status = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (!wcache_tdc_fetch_list(&dom_list, &num_domains)) {
+ nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+ }
+
+ /* Create the hash table of domain SIDs */
+
+ hashed_domains = TALLOC_ZERO_ARRAY(NULL, struct sid_hash_table, 4096);
+ BAIL_ON_PTR_NT_ERROR(hashed_domains, nt_status);
+
+ /* create the hash table of domain SIDs */
+
+ for (i=0; i<num_domains; i++) {
+ uint32_t hash;
+
+ if (is_null_sid(&dom_list[i].sid))
+ continue;
+ if ((hash = hash_domain_sid(&dom_list[i].sid)) == 0)
+ continue;
+
+ DEBUG(5,("hash:be_init() Adding %s (%s) -> %d\n",
+ dom_list[i].domain_name,
+ sid_string_dbg(&dom_list[i].sid),
+ hash));
+
+ hashed_domains[hash].sid = talloc(hashed_domains, DOM_SID);
+ sid_copy(hashed_domains[hash].sid, &dom_list[i].sid);
+ }
+
+done:
+ return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ nt_status = be_init(dom, NULL);
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+ if (!ids) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+ }
+
+ for (i=0; ids[i]; i++) {
+ uint32_t h_domain, h_rid;
+
+ ids[i]->status = ID_UNMAPPED;
+
+ separate_hashes(ids[i]->xid.id, &h_domain, &h_rid);
+
+ /* Make sure the caller allocated memor for us */
+
+ if (!ids[i]->sid) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+ }
+
+ /* If the domain hash doesn't find a SID in the table,
+ skip it */
+
+ if (!hashed_domains[h_domain].sid)
+ continue;
+
+ sid_copy(ids[i]->sid, hashed_domains[h_domain].sid);
+ sid_append_rid(ids[i]->sid, h_rid);
+ ids[i]->status = ID_MAPPED;
+ }
+
+done:
+ return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ nt_status = be_init(dom, NULL);
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+ if (!ids) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+ }
+
+ for (i=0; ids[i]; i++) {
+ DOM_SID sid;
+ uint32_t rid;
+ uint32_t h_domain, h_rid;
+
+ ids[i]->status = ID_UNMAPPED;
+
+ sid_copy(&sid, ids[i]->sid);
+ sid_split_rid(&sid, &rid);
+
+ h_domain = hash_domain_sid(&sid);
+ h_rid = hash_rid(rid);
+
+ /* Check that both hashes are non-zero*/
+
+ if (h_domain && h_rid) {
+ ids[i]->xid.id = combine_hashes(h_domain, h_rid);
+ ids[i]->status = ID_MAPPED;
+ }
+ }
+
+done:
+ return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS be_close(struct idmap_domain *dom)
+{
+ if (hashed_domains)
+ talloc_free(hashed_domains);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS nss_hash_init(struct nss_domain_entry *e )
+{
+ return be_init(NULL, NULL);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_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 = NT_STATUS_UNSUCCESSFUL;
+
+ nt_status = nss_hash_init(e);
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+ if (!homedir || !shell || !gecos) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+ }
+
+ *homedir = talloc_strdup(ctx, lp_template_homedir());
+ BAIL_ON_PTR_NT_ERROR(*homedir, nt_status);
+
+ *shell = talloc_strdup(ctx, lp_template_shell());
+ BAIL_ON_PTR_NT_ERROR(*shell, nt_status);
+
+ *gecos = NULL;
+
+ /* Initialize the gid so that the upper layer fills
+ in the proper Windows primary group */
+
+ if (*p_gid) {
+ *p_gid = (gid_t)-1;
+ }
+
+done:
+ return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_map_to_alias(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *name,
+ char **alias)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ const char *value;
+
+ value = talloc_asprintf(mem_ctx, "%s\\%s", domain, name);
+ BAIL_ON_PTR_NT_ERROR(value, nt_status);
+
+ nt_status = mapfile_lookup_key(mem_ctx, value, alias);
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+ return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_map_from_alias(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *alias,
+ char **name)
+{
+ return mapfile_lookup_value(mem_ctx, alias, name);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_close(void)
+{
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Dispatch Tables for IDMap and NssInfo Methods
+********************************************************************/
+
+static struct idmap_methods hash_idmap_methods = {
+ .init = be_init,
+ .unixids_to_sids = unixids_to_sids,
+ .sids_to_unixids = sids_to_unixids,
+ .close_fn = be_close
+};
+
+static struct nss_info_methods hash_nss_methods = {
+ .init = nss_hash_init,
+ .get_nss_info = nss_hash_get_info,
+ .map_to_alias = nss_hash_map_to_alias,
+ .map_from_alias = nss_hash_map_from_alias,
+ .close_fn = nss_hash_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_hash_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,
+ "hash", &hash_idmap_methods);
+
+ if ( !NT_STATUS_IS_OK(idmap_status) ) {
+ DEBUG(0,("Failed to register hash idmap plugin.\n"));
+ return idmap_status;
+ }
+ }
+
+ if ( !NT_STATUS_IS_OK(nss_status) ) {
+ nss_status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+ "hash", &hash_nss_methods);
+ if ( !NT_STATUS_IS_OK(nss_status) ) {
+ DEBUG(0,("Failed to register hash idmap nss plugin.\n"));
+ return nss_status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_hash/idmap_hash.h b/source3/winbindd/idmap_hash/idmap_hash.h
new file mode 100644
index 0000000000..621520e950
--- /dev/null
+++ b/source3/winbindd/idmap_hash/idmap_hash.h
@@ -0,0 +1,60 @@
+/*
+ * lwopen.h
+ *
+ * Copyright (C) Gerald Carter <jerry@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _LWOPEN_H
+#define _LWOPEN_H
+
+#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 BAIL_ON_PTR_NT_ERROR(p, x) \
+ do { \
+ if ((p) == NULL ) { \
+ DEBUG(10,("NULL pointer!\n")); \
+ x = NT_STATUS_NO_MEMORY; \
+ goto done; \
+ } else { \
+ x = NT_STATUS_OK; \
+ } \
+ } while (0);
+
+#define PRINT_NTSTATUS_ERROR(x, hdr, level) \
+ do { \
+ if (!NT_STATUS_IS_OK(x)) { \
+ DEBUG(level,("Likewise Open ("hdr"): %s\n", nt_errstr(x))); \
+ } \
+ } while(0);
+
+
+NTSTATUS mapfile_lookup_key(TALLOC_CTX *ctx,
+ const char *value,
+ char **key);
+
+NTSTATUS mapfile_lookup_value(TALLOC_CTX *ctx,
+ const char *key,
+ char **value);
+
+#endif /* _LWOPEN_H */
diff --git a/source3/winbindd/idmap_hash/mapfile.c b/source3/winbindd/idmap_hash/mapfile.c
new file mode 100644
index 0000000000..5ab1142ffe
--- /dev/null
+++ b/source3/winbindd/idmap_hash/mapfile.c
@@ -0,0 +1,175 @@
+/*
+ * mapfile.c
+ *
+ * Copyright (C) Gerald Carter <jerry@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "includes.h"
+#include "winbindd/winbindd.h"
+#include "idmap_hash.h"
+#include <stdio.h>
+
+XFILE *lw_map_file = NULL;
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool mapfile_open(void)
+{
+ const char *mapfile_name = NULL;
+
+ /* If we have an open handle, just reset it */
+
+ if (lw_map_file) {
+ return (x_tseek(lw_map_file, 0, SEEK_SET) == 0);
+ }
+
+ mapfile_name = lp_parm_const_string(-1, "idmap_hash", "name_map", NULL);
+ if (!mapfile_name) {
+ return false;
+ }
+
+ lw_map_file = x_fopen(mapfile_name, O_RDONLY, 0);
+ if (!lw_map_file) {
+ DEBUG(0,("can't open idmap_hash:name_map (%s). Error %s\n",
+ mapfile_name, strerror(errno) ));
+ return false;
+ }
+
+ return true;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool mapfile_read_line(fstring key, fstring value)
+{
+ char buffer[1024];
+ char *p;
+ int len;
+
+ if (!lw_map_file)
+ return false;
+
+ if ((p = x_fgets(buffer, sizeof(buffer)-1, lw_map_file)) == NULL) {
+ return false;
+ }
+
+ /* Strip newlines and carriage returns */
+
+ len = strlen_m(buffer) - 1;
+ while ((buffer[len] == '\n') || (buffer[len] == '\r')) {
+ buffer[len--] = '\0';
+ }
+
+
+ if ((p = strchr_m(buffer, '=')) == NULL ) {
+ DEBUG(0,("idmap_hash: Bad line in name_map (%s)\n", buffer));
+ return false;
+ }
+
+ *p = '\0';
+ p++;
+
+ fstrcpy(key, buffer);
+ fstrcpy(value, p);
+
+ /* Eat whitespace */
+
+ if (!trim_char(key, ' ', ' '))
+ return false;
+
+ if (!trim_char(value, ' ', ' '))
+ return false;
+
+ return true;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool mapfile_close(void)
+{
+ int ret = 0;
+ if (lw_map_file) {
+ ret = x_fclose(lw_map_file);
+ lw_map_file = NULL;
+ }
+
+ return (ret == 0);
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+NTSTATUS mapfile_lookup_key(TALLOC_CTX *ctx, const char *value, char **key)
+{
+ fstring r_key, r_value;
+ NTSTATUS ret = NT_STATUS_NOT_FOUND;
+
+ if (!mapfile_open())
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+
+ while (mapfile_read_line(r_key, r_value))
+ {
+ if (strequal(r_value, value)) {
+ ret = NT_STATUS_OK;
+
+ /* We're done once finishing this block */
+ *key = talloc_strdup(ctx, r_key);
+ if (!*key) {
+ ret = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ }
+
+ mapfile_close();
+
+ return ret;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+NTSTATUS mapfile_lookup_value(TALLOC_CTX *ctx, const char *key, char **value)
+{
+ fstring r_key, r_value;
+ NTSTATUS ret = NT_STATUS_NOT_FOUND;
+
+ if (!mapfile_open())
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+
+ while (mapfile_read_line(r_key, r_value))
+ {
+ if (strequal(r_key, key)) {
+ ret = NT_STATUS_OK;
+
+ /* We're done once finishing this block */
+ *value = talloc_strdup(ctx, r_value);
+ if (!*key) {
+ ret = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ }
+
+ mapfile_close();
+
+ return ret;
+}
diff --git a/source3/winbindd/idmap_tdb.c b/source3/winbindd/idmap_tdb.c
index 9e66eed0c8..f9d3a9fbff 100644
--- a/source3/winbindd/idmap_tdb.c
+++ b/source3/winbindd/idmap_tdb.c
@@ -228,7 +228,7 @@ static NTSTATUS idmap_tdb_open_db(TALLOC_CTX *memctx, TDB_CONTEXT **tdbctx)
goto done;
}
- if (!file_exist(tdbfile, &stbuf)) {
+ if (!file_exist_stat(tdbfile, &stbuf)) {
tdb_is_new = True;
}
diff --git a/source3/winbindd/idmap_tdb2.c b/source3/winbindd/idmap_tdb2.c
index 3066db6f3b..8bde963c60 100644
--- a/source3/winbindd/idmap_tdb2.c
+++ b/source3/winbindd/idmap_tdb2.c
@@ -94,12 +94,11 @@ static NTSTATUS idmap_tdb2_open_db(void)
*/
static NTSTATUS idmap_tdb2_alloc_load(void)
{
- const char *range;
uid_t low_uid = 0;
uid_t high_uid = 0;
gid_t low_gid = 0;
gid_t high_gid = 0;
- uint32 low_id, high_id;
+ uint32 low_id;
/* see if a idmap script is configured */
idmap_tdb2_state.idmap_script = lp_parm_const_string(-1, "idmap",
@@ -187,6 +186,10 @@ static NTSTATUS idmap_tdb2_allocate_id(struct unixid *xid)
uint32_t high_hwm;
uint32_t hwm;
int res;
+ NTSTATUS status;
+
+ status = idmap_tdb2_open_db();
+ NT_STATUS_NOT_OK_RETURN(status);
/* Get current high water mark */
switch (xid->type) {
@@ -264,6 +267,10 @@ static NTSTATUS idmap_tdb2_get_hwm(struct unixid *xid)
const char *hwmtype;
uint32_t hwm;
uint32_t high_hwm;
+ NTSTATUS status;
+
+ status = idmap_tdb2_open_db();
+ NT_STATUS_NOT_OK_RETURN(status);
/* Get current high water mark */
switch (xid->type) {
@@ -451,6 +458,10 @@ static NTSTATUS idmap_tdb2_id_to_sid(struct idmap_tdb2_context *ctx, struct id_m
NTSTATUS ret;
TDB_DATA data;
char *keystr;
+ NTSTATUS status;
+
+ status = idmap_tdb2_open_db();
+ NT_STATUS_NOT_OK_RETURN(status);
if (!ctx || !map) {
return NT_STATUS_INVALID_PARAMETER;
@@ -546,6 +557,10 @@ static NTSTATUS idmap_tdb2_sid_to_id(struct idmap_tdb2_context *ctx, struct id_m
TDB_DATA data;
char *keystr;
unsigned long rec_id = 0;
+ NTSTATUS status;
+
+ status = idmap_tdb2_open_db();
+ NT_STATUS_NOT_OK_RETURN(status);
if ((keystr = sid_string_talloc(ctx, map->sid)) == NULL) {
DEBUG(0, ("Out of memory!\n"));
diff --git a/source3/winbindd/idmap_util.c b/source3/winbindd/idmap_util.c
index b10a1a4ba9..9f876618be 100644
--- a/source3/winbindd/idmap_util.c
+++ b/source3/winbindd/idmap_util.c
@@ -121,7 +121,7 @@ backend:
return NT_STATUS_NONE_MAPPED;
}
- idmap_cache_set_sid2uid(sid, gid);
+ idmap_cache_set_sid2gid(sid, gid);
return NT_STATUS_OK;
}
diff --git a/source3/winbindd/nss_info.c b/source3/winbindd/nss_info.c
index daa3dd037d..0e8cb60257 100644
--- a/source3/winbindd/nss_info.c
+++ b/source3/winbindd/nss_info.c
@@ -281,6 +281,47 @@ static struct nss_domain_entry *find_nss_domain( const char *domain )
/********************************************************************
*******************************************************************/
+ NTSTATUS nss_map_to_alias( TALLOC_CTX *mem_ctx, const char *domain,
+ const char *name, char **alias )
+{
+ struct nss_domain_entry *p;
+ struct nss_info_methods *m;
+
+ if ( (p = find_nss_domain( domain )) == NULL ) {
+ DEBUG(4,("nss_map_to_alias: Failed to find nss domain pointer for %s\n",
+ domain ));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ m = p->backend->methods;
+
+ return m->map_to_alias( mem_ctx, domain, name, alias );
+}
+
+
+/********************************************************************
+ *******************************************************************/
+
+ NTSTATUS nss_map_from_alias( TALLOC_CTX *mem_ctx, const char *domain,
+ const char *alias, char **name )
+{
+ struct nss_domain_entry *p;
+ struct nss_info_methods *m;
+
+ if ( (p = find_nss_domain( domain )) == NULL ) {
+ DEBUG(4,("nss_map_from_alias: Failed to find nss domain pointer for %s\n",
+ domain ));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ m = p->backend->methods;
+
+ return m->map_from_alias( mem_ctx, domain, alias, name );
+}
+
+/********************************************************************
+ *******************************************************************/
+
NTSTATUS nss_close( const char *parameters )
{
struct nss_domain_entry *p = nss_domain_list;
diff --git a/source3/winbindd/nss_info_template.c b/source3/winbindd/nss_info_template.c
index aaf02e4abe..d8f903ddd0 100644
--- a/source3/winbindd/nss_info_template.c
+++ b/source3/winbindd/nss_info_template.c
@@ -45,6 +45,8 @@ static NTSTATUS nss_template_get_info( struct nss_domain_entry *e,
if ( !homedir || !shell || !gecos )
return NT_STATUS_INVALID_PARAMETER;
+ /* protect against home directories using whitespace in the
+ username */
*homedir = talloc_strdup( ctx, lp_template_homedir() );
*shell = talloc_strdup( ctx, lp_template_shell() );
*gecos = NULL;
@@ -56,6 +58,28 @@ static NTSTATUS nss_template_get_info( struct nss_domain_entry *e,
return NT_STATUS_OK;
}
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_template_map_to_alias( TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *name,
+ char **alias )
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_template_map_from_alias( TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *alias,
+ char **name )
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
/************************************************************************
***********************************************************************/
@@ -69,9 +93,11 @@ static NTSTATUS nss_template_close( void )
***********************************************************************/
static struct nss_info_methods nss_template_methods = {
- .init = nss_template_init,
- .get_nss_info = nss_template_get_info,
- .close_fn = nss_template_close
+ .init = nss_template_init,
+ .get_nss_info = nss_template_get_info,
+ .map_to_alias = nss_template_map_to_alias,
+ .map_from_alias = nss_template_map_from_alias,
+ .close_fn = nss_template_close
};
NTSTATUS nss_info_template_init( void )
diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c
index 44b5415726..5d4f21a820 100644
--- a/source3/winbindd/winbindd.c
+++ b/source3/winbindd/winbindd.c
@@ -66,7 +66,7 @@ static bool reload_services_file(const char *logfile)
if (lp_loaded()) {
const char *fname = lp_configfile();
- if (file_exist(fname,NULL) && !strcsequal(fname,get_dyn_CONFIGFILE())) {
+ if (file_exist(fname) && !strcsequal(fname,get_dyn_CONFIGFILE())) {
set_dyn_CONFIGFILE(fname);
}
}
@@ -1120,7 +1120,7 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- if (!directory_exist(lp_lockdir(), NULL)) {
+ if (!directory_exist(lp_lockdir())) {
mkdir(lp_lockdir(), 0755);
}
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
index 894e7866b3..1febddf110 100644
--- a/source3/winbindd/winbindd_ads.c
+++ b/source3/winbindd/winbindd_ads.c
@@ -1023,10 +1023,11 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
DEBUG(10,("ads: lookup_groupmem: got sid %s from "
"cache\n", sid_string_dbg(&sid)));
sid_copy(&(*sid_mem)[*num_names], &sid);
- (*names)[*num_names] = talloc_asprintf(*names, "%s%c%s",
- domain_name,
- *lp_winbind_separator(),
- name );
+ (*names)[*num_names] = fill_domain_username_talloc(
+ *names,
+ domain_name,
+ name,
+ true);
(*name_types)[*num_names] = name_type;
(*num_names)++;
@@ -1071,11 +1072,12 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
{
sid_copy(&(*sid_mem)[*num_names],
&sid_mem_nocache[i]);
- (*names)[*num_names] = talloc_asprintf( *names,
- "%s%c%s",
- domains_nocache[i],
- *lp_winbind_separator(),
- names_nocache[i] );
+ (*names)[*num_names] =
+ fill_domain_username_talloc(
+ *names,
+ domains_nocache[i],
+ names_nocache[i],
+ true);
(*name_types)[*num_names] = name_types_nocache[i];
(*num_names)++;
}
diff --git a/source3/winbindd/winbindd_async.c b/source3/winbindd/winbindd_async.c
index 1481aed8e1..7500bcbe5b 100644
--- a/source3/winbindd/winbindd_async.c
+++ b/source3/winbindd/winbindd_async.c
@@ -366,7 +366,7 @@ static void lookupname_recv(TALLOC_CTX *mem_ctx, bool success,
/********************************************************************
The lookup name call first contacts a DC in its own domain
- and fallbacks to contact a DC in the forest in our domain doesn't
+ and fallbacks to contact a DC if the forest in our domain doesn't
know the name.
********************************************************************/
diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index 2fbb01b623..360e915bc4 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -934,6 +934,8 @@ static void wcache_save_lockout_policy(struct winbindd_domain *domain,
centry_free(centry);
}
+
+
static void wcache_save_password_policy(struct winbindd_domain *domain,
NTSTATUS status,
struct samr_DomInfo1 *policy)
@@ -957,6 +959,209 @@ static void wcache_save_password_policy(struct winbindd_domain *domain,
centry_free(centry);
}
+/***************************************************************************
+ ***************************************************************************/
+
+static void wcache_save_username_alias(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *name, const char *alias)
+{
+ struct cache_entry *centry;
+ fstring uname;
+
+ if ( (centry = centry_start(domain, status)) == NULL )
+ return;
+
+ centry_put_string( centry, alias );
+
+ fstrcpy(uname, name);
+ strupper_m(uname);
+ centry_end(centry, "NSS/NA/%s", uname);
+
+ DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
+
+ centry_free(centry);
+}
+
+static void wcache_save_alias_username(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *alias, const char *name)
+{
+ struct cache_entry *centry;
+ fstring uname;
+
+ if ( (centry = centry_start(domain, status)) == NULL )
+ return;
+
+ centry_put_string( centry, name );
+
+ fstrcpy(uname, alias);
+ strupper_m(uname);
+ centry_end(centry, "NSS/AN/%s", uname);
+
+ DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
+
+ centry_free(centry);
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *name, char **alias )
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ char *upper_name;
+
+ if ( domain->internal )
+ return NT_STATUS_NOT_SUPPORTED;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ if ( (upper_name = SMB_STRDUP(name)) == NULL )
+ return NT_STATUS_NO_MEMORY;
+ strupper_m(upper_name);
+
+ centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
+
+ SAFE_FREE( upper_name );
+
+ if (!centry)
+ goto do_query;
+
+ status = centry->status;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ centry_free(centry);
+ return status;
+ }
+
+ *alias = centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
+ name, *alias ? *alias : "(none)"));
+
+ return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+do_query:
+
+ /* If its not in cache and we are offline, then fail */
+
+ if ( get_global_winbindd_state_offline() || !domain->online ) {
+ DEBUG(8,("resolve_username_to_alias: rejecting query "
+ "in offline mode\n"));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
+
+ if ( NT_STATUS_IS_OK( status ) ) {
+ wcache_save_username_alias(domain, status, name, *alias);
+ }
+
+ if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
+ wcache_save_username_alias(domain, status, name, "(NULL)");
+ }
+
+ DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
+ nt_errstr(status)));
+
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
+ set_domain_offline( domain );
+ }
+
+ return status;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *alias, char **name )
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ char *upper_name;
+
+ if ( domain->internal )
+ return NT_STATUS_NOT_SUPPORTED;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ if ( (upper_name = SMB_STRDUP(alias)) == NULL )
+ return NT_STATUS_NO_MEMORY;
+ strupper_m(upper_name);
+
+ centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
+
+ SAFE_FREE( upper_name );
+
+ if (!centry)
+ goto do_query;
+
+ status = centry->status;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ centry_free(centry);
+ return status;
+ }
+
+ *name = centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
+ alias, *name ? *name : "(none)"));
+
+ return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+do_query:
+
+ /* If its not in cache and we are offline, then fail */
+
+ if ( get_global_winbindd_state_offline() || !domain->online ) {
+ DEBUG(8,("resolve_alias_to_username: rejecting query "
+ "in offline mode\n"));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ /* an alias cannot contain a domain prefix or '@' */
+
+ if (strchr(alias, '\\') || strchr(alias, '@')) {
+ DEBUG(10,("resolve_alias_to_username: skipping fully "
+ "qualified name %s\n", alias));
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
+
+ if ( NT_STATUS_IS_OK( status ) ) {
+ wcache_save_alias_username( domain, status, alias, *name );
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ wcache_save_alias_username(domain, status, alias, "(NULL)");
+ }
+
+ DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
+ nt_errstr(status)));
+
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
+ set_domain_offline( domain );
+ }
+
+ return status;
+}
+
NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
{
struct winbind_cache *cache = get_cache(domain);
@@ -3257,6 +3462,48 @@ static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
return 0;
}
+static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
+ TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
+ return 0;
+}
+
+static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
+ TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
+ return 0;
+}
+
static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
struct tdb_validation_status *state)
{
@@ -3358,6 +3605,8 @@ struct key_val_struct {
{"NSS/PWINFO/", validate_pwinfo},
{"TRUSTDOMS/", validate_trustdoms},
{"TRUSTDOMCACHE/", validate_trustdomcache},
+ {"NSS/NA/", validate_nss_na},
+ {"NSS/AN/", validate_nss_an},
{"WINBINDD_OFFLINE", validate_offline},
{WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
{NULL, NULL}
diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
index ce851649ba..db43101a34 100644
--- a/source3/winbindd/winbindd_cm.c
+++ b/source3/winbindd/winbindd_cm.c
@@ -1080,7 +1080,7 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
fstring name )
{
struct ip_service ip_list;
- uint32_t nt_version = NETLOGON_VERSION_1;
+ uint32_t nt_version = NETLOGON_NT_VERSION_1;
ip_list.ss = *pss;
ip_list.port = 0;
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
index 63ce0e8d7f..994c94b5be 100644
--- a/source3/winbindd/winbindd_dual.c
+++ b/source3/winbindd/winbindd_dual.c
@@ -120,6 +120,10 @@ void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
SMB_ASSERT(continuation != NULL);
+ DEBUG(10, ("Sending request to child pid %d (domain=%s)\n",
+ (int)child->pid,
+ (child->domain != NULL) ? child->domain->name : "''"));
+
state = TALLOC_P(mem_ctx, struct winbindd_async_request);
if (state == NULL) {
@@ -196,10 +200,12 @@ static void async_request_fail(struct winbindd_async_request *state)
TALLOC_FREE(state->reply_timeout_event);
- SMB_ASSERT(state->child_pid != (pid_t)0);
+ /* If child exists and is not already reaped,
+ send kill signal to child. */
- /* If not already reaped, send kill signal to child. */
- if (state->child->pid == state->child_pid) {
+ if ((state->child->pid != (pid_t)0) &&
+ (state->child->pid != (pid_t)-1) &&
+ (state->child->pid == state->child_pid)) {
kill(state->child_pid, SIGTERM);
/*
@@ -294,13 +300,27 @@ static void schedule_async_request(struct winbindd_child *child)
return; /* Busy */
}
+ /*
+ * This may be a reschedule, so we might
+ * have an existing timeout event pending on
+ * the first entry in the child->requests list
+ * (we only send one request at a time).
+ * Ensure we free it before we reschedule.
+ * Bug #5814, from hargagan <shargagan@novell.com>.
+ * JRA.
+ */
+
+ TALLOC_FREE(request->reply_timeout_event);
+
if ((child->pid == 0) && (!fork_domain_child(child))) {
- /* Cancel all outstanding requests */
+ /* fork_domain_child failed.
+ Cancel all outstanding requests */
while (request != NULL) {
/* request might be free'd in the continuation */
struct winbindd_async_request *next = request->next;
- request->continuation(request->private_data, False);
+
+ async_request_fail(request);
request = next;
}
return;
@@ -487,6 +507,17 @@ void winbind_child_died(pid_t pid)
child->event.flags = 0;
child->pid = 0;
+ if (child->requests) {
+ /*
+ * schedule_async_request() will also
+ * clear this event but the call is
+ * idempotent so it doesn't hurt to
+ * cover all possible future code
+ * paths. JRA.
+ */
+ TALLOC_FREE(child->requests->reply_timeout_event);
+ }
+
schedule_async_request(child);
}
@@ -874,7 +905,7 @@ static bool calculate_next_machine_pwd_change(const char *domain,
if (time(NULL) < (pass_last_set_time + timeout)) {
next_change = pass_last_set_time + timeout;
DEBUG(10,("machine password still valid until: %s\n",
- http_timestring(next_change)));
+ http_timestring(talloc_tos(), next_change)));
*t = timeval_set(next_change, 0);
return true;
}
diff --git a/source3/winbindd/winbindd_group.c b/source3/winbindd/winbindd_group.c
index 4d5026d158..f2b6fbefb5 100644
--- a/source3/winbindd/winbindd_group.c
+++ b/source3/winbindd/winbindd_group.c
@@ -35,7 +35,11 @@ static void add_member(const char *domain, const char *user,
{
fstring name;
- fill_domain_username(name, domain, user, True);
+ if (domain != NULL) {
+ fill_domain_username(name, domain, user, True);
+ } else {
+ fstrcpy(name, user);
+ }
safe_strcat(name, ",", sizeof(name)-1);
string_append(pp_members, name);
*p_num_members += 1;
@@ -136,7 +140,7 @@ static void add_expanded_sid(const DOM_SID *sid,
continue;
}
- add_member(domain->name, names[i], pp_members, p_num_members);
+ add_member(NULL, names[i], pp_members, p_num_members);
}
done:
@@ -179,12 +183,32 @@ static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
/* Fill a grent structure from various other information */
-static bool fill_grent(struct winbindd_gr *gr, const char *dom_name,
- const char *gr_name, gid_t unix_gid)
+static bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
+ const char *dom_name,
+ char *gr_name, gid_t unix_gid)
{
fstring full_group_name;
+ char *mapped_name = NULL;
+ struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ nt_status = normalize_name_map(mem_ctx, domain, gr_name,
+ &mapped_name);
- fill_domain_username( full_group_name, dom_name, gr_name, True );
+ /* Basic whitespace replacement */
+ if (NT_STATUS_IS_OK(nt_status)) {
+ fill_domain_username(full_group_name, dom_name,
+ mapped_name, true);
+ }
+ /* Mapped to an aliase */
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
+ fstrcpy(full_group_name, mapped_name);
+ }
+ /* no change */
+ else {
+ fill_domain_username( full_group_name, dom_name,
+ gr_name, True );
+ }
gr->gr_gid = unix_gid;
@@ -280,7 +304,10 @@ static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
char *domainname = NULL;
char *username = NULL;
fstring name;
+ char *mapped_name = NULL;
enum lsa_SidType type;
+ struct winbindd_domain *target_domain = NULL;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
DEBUG(10,("fill_grent_mem_domain_users: "
"sid %s in 'Domain Users' in domain %s\n",
@@ -300,7 +327,24 @@ static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
nt_errstr(status)));
return False;
}
- fill_domain_username(name, domain->name, username, True);
+
+ target_domain = find_domain_from_name_noinit(domainname);
+ name_map_status = normalize_name_map(mem_ctx, target_domain,
+ username, &mapped_name);
+
+ /* Basic whitespace replacement */
+ if (NT_STATUS_IS_OK(name_map_status)) {
+ fill_domain_username(name, domainname, mapped_name, true);
+ }
+ /* Mapped to an alias */
+ else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
+ fstrcpy(name, mapped_name);
+ }
+ /* no mapping done...use original name */
+ else {
+ fill_domain_username(name, domainname, username, true);
+ }
+
len = strlen(name);
buf_len = len + 1;
if (!(buf = (char *)SMB_MALLOC(buf_len))) {
@@ -552,6 +596,7 @@ static bool fill_grent_mem(struct winbindd_domain *domain,
uint32 n_members = 0;
char **members = NULL;
NTSTATUS nt_status;
+ int j;
nt_status = expand_groups( mem_ctx, domain,
glist, n_glist,
@@ -562,13 +607,45 @@ static bool fill_grent_mem(struct winbindd_domain *domain,
goto done;
}
- /* Add new group members to list */
+ /* Add new group members to list. Pass through the
+ alias mapping function */
- nt_status = add_names_to_list( mem_ctx, &names, &num_names,
- members, n_members );
- if ( !NT_STATUS_IS_OK(nt_status) ) {
- result = False;
- goto done;
+ for (j=0; j<n_members; j++) {
+ fstring name_domain, name_acct;
+ fstring qualified_name;
+ char *mapped_name = NULL;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ struct winbindd_domain *target_domain = NULL;
+
+ if (parse_domain_user(members[j], name_domain, name_acct)) {
+ target_domain = find_domain_from_name_noinit(name_domain);
+ /* NOW WHAT ? */
+ }
+ if (!target_domain) {
+ target_domain = domain;
+ }
+
+ name_map_status = normalize_name_map(members, target_domain,
+ name_acct, &mapped_name);
+
+ /* Basic whitespace replacement */
+ if (NT_STATUS_IS_OK(name_map_status)) {
+ fill_domain_username(qualified_name, name_domain,
+ mapped_name, true);
+ mapped_name = qualified_name;
+ }
+ /* no mapping at all */
+ else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
+ mapped_name = members[j];
+ }
+
+ nt_status = add_names_to_list( mem_ctx, &names,
+ &num_names,
+ &mapped_name, 1);
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ result = False;
+ goto done;
+ }
}
TALLOC_FREE( members );
@@ -679,6 +756,7 @@ void winbindd_getgrnam(struct winbindd_cli_state *state)
struct winbindd_domain *domain;
fstring name_domain, name_group;
char *tmp;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
/* Ensure null termination */
state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
@@ -686,11 +764,20 @@ void winbindd_getgrnam(struct winbindd_cli_state *state)
DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
state->request.data.groupname));
- /* Parse domain and groupname */
+ nt_status = normalize_name_unmap(state->mem_ctx,
+ state->request.data.groupname,
+ &tmp);
+ /* If we didn't map anything in the above call, just reset the
+ tmp pointer to the original string */
+ if (!NT_STATUS_IS_OK(nt_status) &&
+ !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
+ {
+ tmp = state->request.data.groupname;
+ }
- memset(name_group, 0, sizeof(fstring));
+ /* Parse domain and groupname */
- tmp = state->request.data.groupname;
+ memset(name_group, 0, sizeof(name_group));
name_domain[0] = '\0';
name_group[0] = '\0';
@@ -723,7 +810,7 @@ void winbindd_getgrnam(struct winbindd_cli_state *state)
/* Get rid and name type from name */
- ws_name_replace( name_group, WB_REPLACE_CHAR );
+ fstrcpy( name_group, tmp );
winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
getgrnam_recv, WINBINDD_GETGRNAM, state );
@@ -771,7 +858,8 @@ static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
return;
}
- if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
+ if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
+ dom_name, group_name, gid) ||
!fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
&num_gr_mem, &gr_mem, &gr_mem_len))
{
@@ -796,6 +884,9 @@ static void getgrsid_lookupsid_recv( void *private_data, bool success,
enum lsa_SidType name_type )
{
struct getgrsid_state *s = (struct getgrsid_state *)private_data;
+ char *mapped_name = NULL;
+ fstring raw_name;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
if (!success) {
DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
@@ -814,15 +905,39 @@ static void getgrsid_lookupsid_recv( void *private_data, bool success,
dom_name, name, name_type));
request_error(s->state);
return;
-}
+ }
- if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
- "%s%c%s",
- dom_name,
- *lp_winbind_separator(),
- name)) == NULL )
-{
- DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
+ /* normalize the name and ensure that we have the DOM\name
+ coming out of here */
+
+ fstrcpy(raw_name, name);
+
+ nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
+ &mapped_name);
+
+ /* basiuc whitespace reversal */
+ if (NT_STATUS_IS_OK(nt_status)) {
+ s->group_name = talloc_asprintf(s->state->mem_ctx,
+ "%s%c%s",
+ dom_name,
+ *lp_winbind_separator(),
+ mapped_name);
+ }
+ /* mapped from alias */
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
+ s->group_name = mapped_name;
+ }
+ /* no mapping at all. use original string */
+ else {
+ s->group_name = talloc_asprintf(s->state->mem_ctx,
+ "%s%c%s",
+ dom_name,
+ *lp_winbind_separator(),
+ raw_name);
+ }
+
+ if (s->group_name == NULL) {
+ DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
request_error(s->state);
return;
}
@@ -831,10 +946,10 @@ static void getgrsid_lookupsid_recv( void *private_data, bool success,
winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
getgrsid_sid2gid_recv, s);
- }
+}
static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
- {
+{
struct getgrsid_state *s;
if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
@@ -1261,7 +1376,7 @@ void winbindd_getgrent(struct winbindd_cli_state *state)
fill_domain_username(domain_group_name, ent->domain_name,
name_list[ent->sam_entry_index].acct_name, True);
- result = fill_grent(&group_list[group_list_ndx],
+ result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
ent->domain_name,
name_list[ent->sam_entry_index].acct_name,
group_gid);
@@ -1413,6 +1528,8 @@ static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
void winbindd_getgroups(struct winbindd_cli_state *state)
{
struct getgroups_state *s;
+ char *real_name = NULL;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
/* Ensure null termination */
state->request.data.username
@@ -1432,13 +1549,22 @@ void winbindd_getgroups(struct winbindd_cli_state *state)
s->state = state;
- ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
+ nt_status = normalize_name_unmap(state->mem_ctx,
+ state->request.data.username,
+ &real_name);
+
+ /* Reset the real_name pointer if we didn't do anything
+ productive in the above call */
+ if (!NT_STATUS_IS_OK(nt_status) &&
+ !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
+ {
+ real_name = state->request.data.username;
+ }
- if (!parse_domain_user_talloc(state->mem_ctx,
- state->request.data.username,
+ if (!parse_domain_user_talloc(state->mem_ctx, real_name,
&s->domname, &s->username)) {
DEBUG(5, ("Could not parse domain user: %s\n",
- state->request.data.username));
+ real_name));
/* error out if we do not have nested group support */
diff --git a/source3/winbindd/winbindd_locator.c b/source3/winbindd/winbindd_locator.c
index b2a8bd7e30..b60d235f70 100644
--- a/source3/winbindd/winbindd_locator.c
+++ b/source3/winbindd/winbindd_locator.c
@@ -45,11 +45,15 @@ struct winbindd_child *locator_child(void)
void winbindd_dsgetdcname(struct winbindd_cli_state *state)
{
- state->request.domain_name
- [sizeof(state->request.domain_name)-1] = '\0';
+ state->request.data.dsgetdcname.domain_name
+ [sizeof(state->request.data.dsgetdcname.domain_name)-1] = '\0';
+ state->request.data.dsgetdcname.site_name
+ [sizeof(state->request.data.dsgetdcname.site_name)-1] = '\0';
+ state->request.data.dsgetdcname.domain_guid
+ [sizeof(state->request.data.dsgetdcname.domain_guid)-1] = '\0';
DEBUG(3, ("[%5lu]: dsgetdcname for %s\n", (unsigned long)state->pid,
- state->request.domain_name));
+ state->request.data.dsgetdcname.domain_name));
sendto_child(state, locator_child());
}
@@ -94,44 +98,59 @@ static uint32_t get_dsgetdc_flags(uint32_t wbc_flags)
return ds_flags;
}
-
static enum winbindd_result dual_dsgetdcname(struct winbindd_domain *domain,
struct winbindd_cli_state *state)
{
NTSTATUS result;
struct netr_DsRGetDCNameInfo *info = NULL;
- const char *dc = NULL;
uint32_t ds_flags = 0;
+ struct GUID guid, *guid_ptr = NULL;
+ const char *guid_str = NULL;
- state->request.domain_name
- [sizeof(state->request.domain_name)-1] = '\0';
+ state->request.data.dsgetdcname.domain_name
+ [sizeof(state->request.data.dsgetdcname.domain_name)-1] = '\0';
+ state->request.data.dsgetdcname.site_name
+ [sizeof(state->request.data.dsgetdcname.site_name)-1] = '\0';
+ state->request.data.dsgetdcname.domain_guid
+ [sizeof(state->request.data.dsgetdcname.domain_guid)-1] = '\0';
DEBUG(3, ("[%5lu]: dsgetdcname for %s\n", (unsigned long)state->pid,
- state->request.domain_name));
+ state->request.data.dsgetdcname.domain_name));
ds_flags = get_dsgetdc_flags(state->request.flags);
- result = dsgetdcname(state->mem_ctx, winbind_messaging_context(),
- state->request.domain_name,
- NULL, NULL, ds_flags, &info);
-
- if (!NT_STATUS_IS_OK(result)) {
- return WINBINDD_ERROR;
+ result = GUID_from_string(state->request.data.dsgetdcname.domain_guid,
+ &guid);
+ if (NT_STATUS_IS_OK(result) && !GUID_all_zero(&guid)) {
+ guid_ptr = &guid;
}
- if (info->dc_address) {
- dc = strip_hostname(info->dc_address);
- }
+ result = dsgetdcname(state->mem_ctx,
+ winbind_messaging_context(),
+ state->request.data.dsgetdcname.domain_name,
+ guid_ptr,
+ state->request.data.dsgetdcname.site_name,
+ ds_flags,
+ &info);
- if ((!dc || !is_ipaddress_v4(dc)) && info->dc_unc) {
- dc = strip_hostname(info->dc_unc);
+ if (!NT_STATUS_IS_OK(result)) {
+ return WINBINDD_ERROR;
}
- if (!dc || !*dc) {
+ guid_str = GUID_string(state->mem_ctx, &info->domain_guid);
+ if (!guid_str) {
return WINBINDD_ERROR;
}
- fstrcpy(state->response.data.dc_name, dc);
+ fstrcpy(state->response.data.dsgetdcname.dc_unc, info->dc_unc);
+ fstrcpy(state->response.data.dsgetdcname.dc_address, info->dc_address);
+ state->response.data.dsgetdcname.dc_address_type = info->dc_address_type;
+ fstrcpy(state->response.data.dsgetdcname.domain_guid, guid_str);
+ fstrcpy(state->response.data.dsgetdcname.domain_name, info->domain_name);
+ fstrcpy(state->response.data.dsgetdcname.forest_name, info->forest_name);
+ state->response.data.dsgetdcname.dc_flags = info->dc_flags;
+ fstrcpy(state->response.data.dsgetdcname.dc_site_name, info->dc_site_name);
+ fstrcpy(state->response.data.dsgetdcname.client_site_name, info->client_site_name);
return WINBINDD_OK;
}
diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c
index d4a2e3ed79..9ff3899661 100644
--- a/source3/winbindd/winbindd_pam.c
+++ b/source3/winbindd/winbindd_pam.c
@@ -127,7 +127,7 @@ static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
DATA_BLOB blob;
enum ndr_err_code ndr_err;
- ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
(ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
DEBUG(0,("append_info3_as_ndr: failed to append\n"));
@@ -811,7 +811,9 @@ void winbindd_pam_auth(struct winbindd_cli_state *state)
{
struct winbindd_domain *domain;
fstring name_domain, name_user;
+ char *mapped_user = NULL;
NTSTATUS result;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
/* Ensure null termination */
state->request.data.auth.user
@@ -831,10 +833,20 @@ void winbindd_pam_auth(struct winbindd_cli_state *state)
/* Parse domain and username */
- ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
+ name_map_status = normalize_name_unmap(state->mem_ctx,
+ state->request.data.auth.user,
+ &mapped_user);
- if (!canonicalize_username(state->request.data.auth.user,
- name_domain, name_user)) {
+ /* If the name normalization didnt' actually do anything,
+ just use the original name */
+
+ if (!NT_STATUS_IS_OK(name_map_status) &&
+ !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ mapped_user = state->request.data.auth.user;
+ }
+
+ if (!canonicalize_username(mapped_user, name_domain, name_user)) {
result = NT_STATUS_NO_SUCH_USER;
goto done;
}
@@ -1447,7 +1459,10 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
NTSTATUS result = NT_STATUS_LOGON_FAILURE;
NTSTATUS krb5_result = NT_STATUS_OK;
fstring name_domain, name_user;
+ char *mapped_user;
+ fstring domain_user;
struct netr_SamInfo3 *info3 = NULL;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
/* Ensure null termination */
state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
@@ -1465,9 +1480,26 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
/* Parse domain and username */
- ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
+ name_map_status = normalize_name_unmap(state->mem_ctx,
+ state->request.data.auth.user,
+ &mapped_user);
- parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+ /* If the name normalization didnt' actually do anything,
+ just use the original name */
+
+ if (!NT_STATUS_IS_OK(name_map_status) &&
+ !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ mapped_user = state->request.data.auth.user;
+ }
+
+ parse_domain_user(mapped_user, name_domain, name_user);
+
+ if ( mapped_user != state->request.data.auth.user ) {
+ fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
+ safe_strcpy( state->request.data.auth.user, domain_user,
+ sizeof(state->request.data.auth.user)-1 );
+ }
if (domain->online == false) {
result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
@@ -1970,14 +2002,30 @@ done:
void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
{
fstring domain, user;
+ char *mapped_user;
struct winbindd_domain *contact_domain;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
state->request.data.chauthtok.user));
/* Setup crap */
- ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
+ nt_status = normalize_name_unmap(state->mem_ctx,
+ state->request.data.chauthtok.user,
+ &mapped_user);
+
+ /* Update the chauthtok name if we did any mapping */
+
+ if (NT_STATUS_IS_OK(nt_status) ||
+ NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
+ {
+ fstrcpy(state->request.data.chauthtok.user, mapped_user);
+ }
+
+ /* Must pass in state->...chauthtok.user because
+ canonicalize_username() assumes an fstring(). Since
+ we have already copied it (if necessary), this is ok. */
if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index e0fc073a0a..95ccf30cfe 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -48,8 +48,6 @@ int count_all_current_connections(void);
bool claim_connection(connection_struct *conn, const char *name,
uint32 msg_flags);
bool register_message_flags(bool doreg, uint32 msg_flags);
-bool store_pipe_opendb( smb_np_struct *p );
-bool delete_pipe_opendb( smb_np_struct *p );
/* The following definitions come from winbindd/winbindd.c */
@@ -569,6 +567,10 @@ bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
void parse_add_domuser(void *buf, char *domuser, int *len);
bool canonicalize_username(fstring username_inout, fstring domain, fstring user);
void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume);
+char *fill_domain_username_talloc(TALLOC_CTX *ctx,
+ const char *domain,
+ const char *user,
+ bool can_assume);
const char *get_winbind_pipe_dir(void) ;
char *get_winbind_priv_pipe_dir(void) ;
int open_winbindd_socket(void);
@@ -583,8 +585,22 @@ NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
const DOM_SID *user_sid,
uint32 *p_num_groups, DOM_SID **user_sids);
-void ws_name_replace( char *name, char replace );
-void ws_name_return( char *name, char replace );
+
+NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ char *name,
+ char **normalized);
+NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
+ char *name,
+ char **normalized);
+
+NTSTATUS resolve_username_to_alias(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *name, char **alias);
+NTSTATUS resolve_alias_to_username(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *alias, char **name);
+
bool winbindd_can_contact_domain(struct winbindd_domain *domain);
bool winbindd_internal_child(struct winbindd_child *child);
void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain);
diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c
index bb79d7ec12..d966e50159 100644
--- a/source3/winbindd/winbindd_rpc.c
+++ b/source3/winbindd/winbindd_rpc.c
@@ -279,6 +279,8 @@ NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
char *full_name = NULL;
struct rpc_pipe_client *cli;
POLICY_HND lsa_policy;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ char *mapped_name = NULL;
if (name == NULL || *name=='\0') {
full_name = talloc_asprintf(mem_ctx, "%s", domain_name);
@@ -294,9 +296,19 @@ NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
DEBUG(3,("rpc: name_to_sid name=%s\n", full_name));
- ws_name_return( full_name, WB_REPLACE_CHAR );
+ name_map_status = normalize_name_unmap(mem_ctx, full_name,
+ &mapped_name);
- DEBUG(3,("name_to_sid [rpc] %s for domain %s\n", full_name?full_name:"", domain_name ));
+ /* Reset the full_name pointer if we mapped anytthing */
+
+ if (NT_STATUS_IS_OK(name_map_status) ||
+ NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ full_name = mapped_name;
+ }
+
+ DEBUG(3,("name_to_sid [rpc] %s for domain %s\n",
+ full_name?full_name:"", domain_name ));
result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
if (!NT_STATUS_IS_OK(result))
@@ -332,6 +344,8 @@ NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain,
NTSTATUS result;
struct rpc_pipe_client *cli;
POLICY_HND lsa_policy;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ char *mapped_name = NULL;
DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_dbg(sid),
domain->name ));
@@ -356,9 +370,17 @@ NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain,
*domain_name = domains[0];
*name = names[0];
- ws_name_replace( *name, WB_REPLACE_CHAR );
-
DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
+
+ name_map_status = normalize_name_map(mem_ctx, domain, *name,
+ &mapped_name);
+ if (NT_STATUS_IS_OK(name_map_status) ||
+ NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ *name = mapped_name;
+ DEBUG(5,("returning mapped name -- %s\n", *name));
+ }
+
return NT_STATUS_OK;
}
@@ -411,8 +433,20 @@ NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
ret_names = *names;
for (i=0; i<num_rids; i++) {
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ char *mapped_name = NULL;
+
if ((*types)[i] != SID_NAME_UNKNOWN) {
- ws_name_replace( ret_names[i], WB_REPLACE_CHAR );
+ name_map_status = normalize_name_map(mem_ctx,
+ domain,
+ ret_names[i],
+ &mapped_name);
+ if (NT_STATUS_IS_OK(name_map_status) ||
+ NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ ret_names[i] = mapped_name;
+ }
+
*domain_name = domains[i];
}
}
@@ -820,7 +854,10 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
}
for (r=0; r<tmp_names.count; r++) {
- (*names)[i+r] = CONST_DISCARD(char *, tmp_names.names[r].string);
+ (*names)[i+r] = fill_domain_username_talloc(mem_ctx,
+ domain->name,
+ tmp_names.names[r].string,
+ true);
(*name_types)[i+r] = tmp_types.ids[r];
}
@@ -981,7 +1018,7 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
&info);
if (NT_STATUS_IS_OK(result)) {
- *seq = info->info2.sequence_num;
+ *seq = info->general.sequence_num;
got_seq_num = True;
}
diff --git a/source3/winbindd/winbindd_user.c b/source3/winbindd/winbindd_user.c
index 3b6dfdda1c..e5d0a22a73 100644
--- a/source3/winbindd/winbindd_user.c
+++ b/source3/winbindd/winbindd_user.c
@@ -67,12 +67,15 @@ static bool fillup_pw_field(const char *lp_template,
}
/* Fill a pwent structure with information we have obtained */
-static bool winbindd_fill_pwent(char *dom_name, char *user_name,
+static bool winbindd_fill_pwent(TALLOC_CTX *ctx, char *dom_name, char *user_name,
DOM_SID *user_sid, DOM_SID *group_sid,
char *full_name, char *homedir, char *shell,
struct winbindd_pw *pw)
{
fstring output_username;
+ char *mapped_name = NULL;
+ struct winbindd_domain *domain = NULL;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
if (!pw || !dom_name || !user_name)
return False;
@@ -99,7 +102,28 @@ static bool winbindd_fill_pwent(char *dom_name, char *user_name,
/* Username */
- fill_domain_username(output_username, dom_name, user_name, True);
+ domain = find_domain_from_name_noinit(dom_name);
+ if (domain) {
+ nt_status = normalize_name_map(ctx, domain, user_name,
+ &mapped_name);
+ } else {
+ DEBUG(5,("winbindd_fill_pwent: Failed to find domain for %s. "
+ "Disabling name alias support\n", dom_name));
+ nt_status = NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* Basic removal of whitespace */
+ if (NT_STATUS_IS_OK(nt_status)) {
+ fill_domain_username(output_username, dom_name, mapped_name, True);
+ }
+ /* Complete name replacement */
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
+ fstrcpy(output_username, mapped_name);
+ }
+ /* No change at all */
+ else {
+ fill_domain_username(output_username, dom_name, user_name, True);
+ }
safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
@@ -179,6 +203,7 @@ struct getpwsid_state {
uid_t uid;
DOM_SID group_sid;
gid_t gid;
+ bool username_mapped;
};
static void getpwsid_queryuser_recv(void *private_data, bool success,
@@ -231,6 +256,8 @@ static void getpwsid_queryuser_recv(void *private_data, bool success,
fstring username;
struct getpwsid_state *s =
talloc_get_type_abort(private_data, struct getpwsid_state);
+ char *mapped_name;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
if (!success) {
DEBUG(5, ("Could not query domain %s SID %s\n",
@@ -272,7 +299,23 @@ static void getpwsid_queryuser_recv(void *private_data, bool success,
strlower_m( username );
s->username = talloc_strdup(s->state->mem_ctx, username);
- ws_name_replace( s->username, WB_REPLACE_CHAR );
+ nt_status = normalize_name_map(s->state->mem_ctx, s->domain,
+ s->username, &mapped_name);
+
+ /* Basic removal of whitespace */
+ if (NT_STATUS_IS_OK(nt_status)) {
+ s->username = mapped_name;
+ s->username_mapped = false;
+ }
+ /* Complete name replacement */
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
+ s->username = mapped_name;
+ s->username_mapped = true;
+ }
+ /* No change at all */
+ else {
+ s->username_mapped = false;
+ }
s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
@@ -330,8 +373,16 @@ static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
pw = &s->state->response.data.pw;
pw->pw_uid = s->uid;
pw->pw_gid = s->gid;
+
+ /* allow username to be overridden by the alias mapping */
+
+ if ( s->username_mapped ) {
+ fstrcpy( output_username, s->username );
+ } else {
fill_domain_username(output_username, s->domain->name,
s->username, True);
+ }
+
safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
@@ -370,8 +421,10 @@ void winbindd_getpwnam(struct winbindd_cli_state *state)
{
struct winbindd_domain *domain;
fstring domname, username;
+ char *mapped_user = NULL;
char *domuser;
size_t dusize;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
domuser = state->request.data.username;
dusize = sizeof(state->request.data.username);
@@ -383,9 +436,19 @@ void winbindd_getpwnam(struct winbindd_cli_state *state)
(unsigned long)state->pid,
domuser));
- ws_name_return(domuser, WB_REPLACE_CHAR);
+ nt_status = normalize_name_unmap(state->mem_ctx, domuser,
+ &mapped_user);
+
+ /* If we could not convert from an aliased name or a
+ normalized name, then just use the original name */
+
+ if (!NT_STATUS_IS_OK(nt_status) &&
+ !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
+ {
+ mapped_user = domuser;
+ }
- if (!parse_domain_user(domuser, domname, username)) {
+ if (!parse_domain_user(mapped_user, domname, username)) {
DEBUG(5, ("Could not parse domain user: %s\n", domuser));
request_error(state);
return;
@@ -743,6 +806,7 @@ void winbindd_getpwent(struct winbindd_cli_state *state)
/* Lookup user info */
result = winbindd_fill_pwent(
+ state->mem_ctx,
ent->domain_name,
name_list[ent->sam_entry_index].name,
&name_list[ent->sam_entry_index].user_sid,
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
index 132c96f1ee..fdfc8ed9d1 100644
--- a/source3/winbindd/winbindd_util.c
+++ b/source3/winbindd/winbindd_util.c
@@ -111,7 +111,16 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const
const char *alternative_name = NULL;
char *idmap_config_option;
const char *param;
+ const char **ignored_domains, **dom;
+ ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
+ for (dom=ignored_domains; dom && *dom; dom++) {
+ if (gen_fnmatch(*dom, domain_name) == 0) {
+ DEBUG(2,("Ignoring domain '%s'\n", domain_name));
+ return NULL;
+ }
+ }
+
/* ignore alt_name if we are not in an AD domain */
if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
@@ -436,6 +445,10 @@ static void rescan_forest_root_trusts( void )
&dom_list[i].sid );
}
+ if (d == NULL) {
+ continue;
+ }
+
DEBUG(10,("rescan_forest_root_trusts: Following trust path "
"for domain tree root %s (%s)\n",
d->name, d->alt_name ));
@@ -500,6 +513,10 @@ static void rescan_forest_trusts( void )
&cache_methods,
&dom_list[i].sid );
}
+
+ if (d == NULL) {
+ continue;
+ }
DEBUG(10,("Following trust path for domain %s (%s)\n",
d->name, d->alt_name ));
@@ -1058,13 +1075,12 @@ void free_getent_state(struct getent_state *state)
temp = state;
while(temp != NULL) {
- struct getent_state *next;
+ struct getent_state *next = temp->next;
/* Free sam entries then list entry */
SAFE_FREE(state->sam_entries);
DLIST_REMOVE(state, state);
- next = temp->next;
SAFE_FREE(temp);
temp = next;
@@ -1160,7 +1176,7 @@ void parse_add_domuser(void *buf, char *domuser, int *len)
}
}
- safe_strcpy(buf, user, *len);
+ safe_strcpy((char *)buf, user, *len);
}
/* Ensure an incoming username from NSS is fully qualified. Replace the
@@ -1213,6 +1229,33 @@ void fill_domain_username(fstring name, const char *domain, const char *user, bo
}
}
+/**
+ * talloc version of fill_domain_username()
+ * return NULL on talloc failure.
+ */
+char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *user,
+ bool can_assume)
+{
+ char *tmp_user, *name;
+
+ tmp_user = talloc_strdup(mem_ctx, user);
+ strlower_m(tmp_user);
+
+ if (can_assume && assume_domain(domain)) {
+ name = tmp_user;
+ } else {
+ name = talloc_asprintf(mem_ctx, "%s%c%s",
+ domain,
+ *lp_winbind_separator(),
+ tmp_user);
+ TALLOC_FREE(tmp_user);
+ }
+
+ return name;
+}
+
/*
* Winbindd socket accessor functions
*/
@@ -1378,34 +1421,107 @@ NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
We use this to remove spaces from user and group names
********************************************************************/
-void ws_name_replace( char *name, char replace )
+NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ char *name,
+ char **normalized)
{
- char replace_char[2] = { 0x0, 0x0 };
-
- if ( !lp_winbind_normalize_names() || (replace == '\0') )
- return;
+ NTSTATUS nt_status;
- replace_char[0] = replace;
- all_string_sub( name, " ", replace_char, 0 );
+ if (!name || !normalized) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
- return;
+ if (!lp_winbind_normalize_names()) {
+ return NT_STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ /* Alias support and whitespace replacement are mutually
+ exclusive */
+
+ nt_status = resolve_username_to_alias(mem_ctx, domain,
+ name, normalized );
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* special return code to let the caller know we
+ mapped to an alias */
+ return NT_STATUS_FILE_RENAMED;
+ }
+
+ /* check for an unreachable domain */
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
+ domain->name));
+ set_domain_offline(domain);
+ return nt_status;
+ }
+
+ /* deal with whitespace */
+
+ *normalized = talloc_strdup(mem_ctx, name);
+ if (!(*normalized)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ all_string_sub( *normalized, " ", "_", 0 );
+
+ return NT_STATUS_OK;
}
/*********************************************************************
- We use this to do the inverse of ws_name_replace()
+ We use this to do the inverse of normalize_name_map()
********************************************************************/
-void ws_name_return( char *name, char replace )
+NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
+ char *name,
+ char **normalized)
{
- char replace_char[2] = { 0x0, 0x0 };
-
- if ( !lp_winbind_normalize_names() || (replace == '\0') )
- return;
+ NTSTATUS nt_status;
+ struct winbindd_domain *domain = find_our_domain();
+
+ if (!name || !normalized) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
- replace_char[0] = replace;
- all_string_sub( name, replace_char, " ", 0 );
+ if (!lp_winbind_normalize_names()) {
+ return NT_STATUS_PROCEDURE_NOT_FOUND;
+ }
- return;
+ /* Alias support and whitespace replacement are mutally
+ exclusive */
+
+ /* When mapping from an alias to a username, we don't know the
+ domain. But we only need a domain structure to cache
+ a successful lookup , so just our own domain structure for
+ the seqnum. */
+
+ nt_status = resolve_alias_to_username(mem_ctx, domain,
+ name, normalized);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* Special return code to let the caller know we mapped
+ from an alias */
+ return NT_STATUS_FILE_RENAMED;
+ }
+
+ /* check for an unreachable domain */
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
+ domain->name));
+ set_domain_offline(domain);
+ return nt_status;
+ }
+
+ /* deal with whitespace */
+
+ *normalized = talloc_strdup(mem_ctx, name);
+ if (!(*normalized)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ all_string_sub(*normalized, "_", " ", 0);
+
+ return NT_STATUS_OK;
}
/*********************************************************************