idmap_hash: Add the idmap/nss-info provider from Likewise Open.
authorGerald (Jerry) Carter <jerry@samba.org>
Mon, 15 Sep 2008 20:51:44 +0000 (15:51 -0500)
committerJeremy Allison <jra@samba.org>
Tue, 16 Sep 2008 17:28:11 +0000 (10:28 -0700)
* 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 = <filename>" for the
  name aliasing layer.  The name map file consist of entries in
  the form "alias = DOMAIN\name"

source3/Makefile.in
source3/configure.in
source3/winbindd/idmap_hash/idmap_hash.c [new file with mode: 0644]
source3/winbindd/idmap_hash/idmap_hash.h [new file with mode: 0644]
source3/winbindd/idmap_hash/mapfile.c [new file with mode: 0644]

index 11399bad3c522668ba1001757e4f3048273690e6..ae446fc2a5f5080432aa834e3aebf6588127c17c 100644 (file)
@@ -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
index d9766e49d034525be11ec286c1c1cdfb2d7f55a3..8025cc35236d7e1ab27a31cf5ca4ec3ee3adc595 100644 (file)
@@ -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 (file)
index 0000000..a050f99
--- /dev/null
@@ -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 (file)
index 0000000..621520e
--- /dev/null
@@ -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 (file)
index 0000000..5ab1142
--- /dev/null
@@ -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;
+}