From 63554b4078e3039bfeda46d8bc77d6ad7ab189ad Mon Sep 17 00:00:00 2001 From: "Gerald (Jerry) Carter" Date: Mon, 15 Sep 2008 15:51:44 -0500 Subject: idmap_hash: Add the idmap/nss-info provider from Likewise Open. * Port the Likewise Open idmap/nss_info provider (renamed to idmap_hash). * uids & gids are generated based on a hashing algorithm that collapse the Domain SID to a 31 bit number. The reverse mapping from the high order 11 bits to the originat8ing sdomain SID is stored in a has table initialized at start up. * Includes support for "idmap_hash:name_map = " for the name aliasing layer. The name map file consist of entries in the form "alias = DOMAIN\name" --- source3/Makefile.in | 8 + source3/configure.in | 1 + source3/winbindd/idmap_hash/idmap_hash.c | 393 +++++++++++++++++++++++++++++++ source3/winbindd/idmap_hash/idmap_hash.h | 60 +++++ source3/winbindd/idmap_hash/mapfile.c | 175 ++++++++++++++ 5 files changed, 637 insertions(+) create mode 100644 source3/winbindd/idmap_hash/idmap_hash.c create mode 100644 source3/winbindd/idmap_hash/idmap_hash.h create mode 100644 source3/winbindd/idmap_hash/mapfile.c diff --git a/source3/Makefile.in b/source3/Makefile.in index 11399bad3c..ae446fc2a5 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -979,6 +979,10 @@ IDMAP_OBJ = winbindd/idmap.o winbindd/idmap_util.o @IDMAP_STATIC@ NSS_INFO_OBJ = winbindd/nss_info.o @NSS_INFO_STATIC@ +IDMAP_HASH_OBJ = \ + winbindd/idmap_hash/idmap_hash.o \ + winbindd/idmap_hash/mapfile.o + WINBINDD_OBJ1 = \ winbindd/winbindd.o \ winbindd/winbindd_user.o \ @@ -2208,6 +2212,10 @@ bin/ad.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ad.o @echo "Building plugin $@" @$(SHLD_MODULE) winbindd/idmap_ad.o +bin/hash.@SHLIBEXT@: $(BINARY_PREREQS) $(IDMAP_HASH_OBJ) + @echo "Building plugin $@" + @$(SHLD_MODULE) $(IDMAP_HASH_OBJ) + bin/tdb2.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_tdb2.o @echo "Building plugin $@" @$(SHLD_MODULE) winbindd/idmap_tdb2.o diff --git a/source3/configure.in b/source3/configure.in index d9766e49d0..8025cc3523 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -6057,6 +6057,7 @@ SMB_MODULE(idmap_passdb, winbindd/idmap_passdb.o, "bin/passdb.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_nss, winbindd/idmap_nss.o, "bin/nss.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_rid, winbindd/idmap_rid.o, "bin/rid.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_ad, winbindd/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP) +SMB_MODULE(idmap_hash, \$(IDMAP_HASH_OBJ), "bin/hash.$SHLIBEXT", IDMAP) SMB_SUBSYSTEM(IDMAP, winbindd/idmap.o) SMB_MODULE(nss_info_template, winbindd/nss_info_template.o, "bin/template.$SHLIBEXT", NSS_INFO) diff --git a/source3/winbindd/idmap_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 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 . + * + */ + +#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 %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 + * + * 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 . + * + */ + +#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 + * + * 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 . + * + */ + +#include "includes.h" +#include "winbindd/winbindd.h" +#include "idmap_hash.h" +#include + +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; +} -- cgit