diff options
-rw-r--r-- | docs-xml/manpages-3/idmap_autorid.8.xml | 134 | ||||
-rw-r--r-- | source3/Makefile.in | 4 | ||||
-rw-r--r-- | source3/configure.in | 1 | ||||
-rw-r--r-- | source3/winbindd/idmap_autorid.c | 471 |
4 files changed, 610 insertions, 0 deletions
diff --git a/docs-xml/manpages-3/idmap_autorid.8.xml b/docs-xml/manpages-3/idmap_autorid.8.xml new file mode 100644 index 0000000000..38790ea2b0 --- /dev/null +++ b/docs-xml/manpages-3/idmap_autorid.8.xml @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="idmap_autorid.8"> + +<refmeta> + <refentrytitle>idmap_autorid</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.6</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>idmap_autorid</refname> + <refpurpose>Samba's idmap_autorid Backend for Winbind</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <title>DESCRIPTION</title> + <para>The idmap_autorid backend provides a way to use an algorithmic + mapping scheme to map UIDs/GIDs and SIDs that is more deterministic + than idmap_tdb and easier to configure than idmap_rid.</para> + <para>The module works similar to idmap_rid, but it automatically + configures the range to be used for each domain, so there is no need + to specify a specific range for each domain in the forest, the only + configuration that is needed is the range of uid/gids that shall + be used for user/group mappings and an optional size of the ranges + to be used.</para> + <para>The mappings of which domain is mapped to which range is stored + in autorid.tdb, thus you should backup this database regularly.</para> + <para>Due to the algorithm being used, it is the module that is + most easy to use as it only requires a minimal configuration.</para> +</refsynopsisdiv> + +<refsect1> + <title>IDMAP OPTIONS</title> + + <variablelist> + <varlistentry> + <term>rangesize = numberofidsperdomain</term> + <listitem><para> + Defines the available number of uids/gids per domain. The + minimum needed value is 2000. SIDs with RIDs larger than this + value cannot be mapped, are ignored and the corresponding map + is discarded. Choose this value carefully, as this should + not be changed after the first ranges for domains have been + defined, otherwise mappings between domains will get intermixed + leading to unpredictable results. Please note that RIDs in Windows + Domains usually start with 500 for builtin users and 1000 + for regular users. As the parameter cannot be changed later, please + plan accordingly for your expected number of users in a domain + with safety margins. + </para> + <para>The default value is 100000.</para> + </listitem> + </varlistentry> + </variablelist> +</refsect1> + +<refsect1> + <title>THE MAPPING FORMULAS</title> + <para> + The Unix ID for a RID is calculated this way: + <programlisting> + ID = IDMAP UID LOW VALUE + DOMAINRANGENUMBER * RANGESIZE + RID + </programlisting> + </para> + <para> + Correspondingly, the formula for calculating the RID for a + given Unix ID is this: + <programlisting> + RID = ID - IDMAP UID LOW VALUE - DOMAINRANGENUMBER * RANGESIZE + </programlisting> + </para> +</refsect1> + +<refsect1> + <title>EXAMPLES</title> + <para> + This example shows you the minimal configuration that will + work for the principial domain and 19 trusted domains. + </para> + + <programlisting> + [global] + security = ads + workgroup = CUSTOMER + realm = CUSTOMER.COM + + idmap backend = autorid + idmap uid = 1000000-1999999 + idmap gid = 1000000-1999999 + + </programlisting> + + <para> + This example shows how to configure idmap_autorid as default + for all domains with a potentially large amount of users + plus a specific configuration for a trusted domain + that uses the SFU mapping scheme. Please note that idmap uid/gid + ranges and sfu ranges are not allowed to overlap. + </para> + + <programlisting> + [global] + security = ads + workgroup = CUSTOMER + realm = CUSTOMER.COM + + idmap backend = autorid + autorid:rangesize = 1000000 + idmap uid = 1000000-19999999 + idmap gid = 1000000-19999999 + + idmap config TRUSTED : backend = ad + idmap config TRUSTED : range = 50000 - 99999 + idmap config TRUSTED : schema_mode = sfu + </programlisting> + +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> + The original Samba software and related utilities + were created by Andrew Tridgell. Samba is now developed + by the Samba Team as an Open Source project similar + to the way the Linux kernel is developed. + </para> +</refsect1> + +</refentry> diff --git a/source3/Makefile.in b/source3/Makefile.in index 411a4c57a0..60361e7fdc 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -2772,6 +2772,10 @@ bin/passdb.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_passdb.o @echo "Building plugin $@" @$(SHLD_MODULE) winbindd/idmap_passdb.o +bin/autorid.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_autorid.o + @echo "Building plugin $@" + @$(SHLD_MODULE) winbindd/idmap_autorid.o + bin/ad.@SHLIBEXT@: $(BINARY_PREREQS) winbindd/idmap_ad.o @echo "Building plugin $@" @$(SHLD_MODULE) winbindd/idmap_ad.o diff --git a/source3/configure.in b/source3/configure.in index e67a262650..560dbde9a0 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -6693,6 +6693,7 @@ SMB_MODULE(idmap_tdb2, winbindd/idmap_tdb2.o, "bin/tdb2.$SHLIBEXT", IDMAP) 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_autorid, winbindd/idmap_autorid.o, "bin/autorid.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_ad, winbindd/idmap_ad.o, "bin/ad.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_hash, \$(IDMAP_HASH_OBJ), "bin/hash.$SHLIBEXT", IDMAP) SMB_MODULE(idmap_adex, \$(IDMAP_ADEX_OBJ), "bin/adex.$SHLIBEXT", IDMAP) diff --git a/source3/winbindd/idmap_autorid.c b/source3/winbindd/idmap_autorid.c new file mode 100644 index 0000000000..c856ae00c3 --- /dev/null +++ b/source3/winbindd/idmap_autorid.c @@ -0,0 +1,471 @@ +/* + * idmap_autorid: static map between Active Directory/NT RIDs + * and RFC 2307 accounts + * + * based on the idmap_rid module, but this module defines the ranges + * for the domains by automatically allocating a range for each domain + * + * Copyright (C) Christian Ambach, 2010 + * + * 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.h" +#include "dbwrap.h" +#include "idmap.h" +#include "../libcli/security/dom_sid.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +#define HWM "NEXT RANGE" +struct autorid_global_config { + uint32_t minvalue; + uint32_t rangesize; + uint32_t maxranges; +}; + +struct autorid_domain_config { + struct dom_sid sid; + uint32_t domainnum; + struct autorid_global_config *globalcfg; +}; + +/* handle to the tdb storing domain <-> range assignments */ +static struct db_context *autorid_db; + +static NTSTATUS idmap_autorid_get_domainrange(struct db_context *db, + void *private_data) +{ + NTSTATUS ret; + uint32_t domainnum, hwm; + char *sidstr, *numstr; + struct autorid_domain_config *cfg; + + cfg = (struct autorid_domain_config *)private_data; + sidstr = dom_sid_string(talloc_tos(), &(cfg->sid)); + + if (!sidstr) { + return NT_STATUS_NO_MEMORY; + } + + if (!dbwrap_fetch_uint32(db, sidstr, &domainnum)) { + DEBUG(10, ("Acquiring new range for domain %s\n", sidstr)); + + /* fetch the current HWM */ + if (!dbwrap_fetch_uint32(db, HWM, &hwm)) { + DEBUG(1, ("Fatal error while fetching current " + "HWM value!\n")); + goto error; + } + + /* do we have a range left? */ + if (hwm >= cfg->globalcfg->maxranges) { + DEBUG(1, ("No more domain ranges available!\n")); + ret = NT_STATUS_NO_MEMORY; + goto error; + } + + /* increase the HWM */ + ret = dbwrap_change_uint32_atomic(db, HWM, &domainnum, 1); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("Fatal error while fetching a new " + "domain range value!\n")); + goto error; + } + + /* store away the new mapping in both directions */ + ret = dbwrap_trans_store_uint32(db, sidstr, domainnum); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("Fatal error while storing new " + "domain->range assignment!\n")); + goto error; + } + + numstr = talloc_asprintf(db, "%u", domainnum); + if (!numstr) { + ret = NT_STATUS_NO_MEMORY; + goto error; + } + + ret = dbwrap_trans_store_bystring(db, numstr, + string_term_tdb_data(sidstr), + TDB_INSERT); + if (!NT_STATUS_IS_OK(ret)) { + talloc_free(numstr); + DEBUG(1, ("Fatal error while storing " + "new domain->range assignment!\n")); + goto error; + } + talloc_free(numstr); + DEBUG(5, ("Acquired new range #%d for domain %s\n", + domainnum, sidstr)); + } + + DEBUG(10, ("Using range #%d for domain %s\n", domainnum, sidstr)); + cfg->domainnum = domainnum; + + talloc_free(sidstr); + return NT_STATUS_OK; + + error: + talloc_free(sidstr); + return ret; + +} + +static NTSTATUS idmap_autorid_id_to_sid(TALLOC_CTX * memctx, + struct autorid_global_config *cfg, + struct id_map *map) +{ + uint32_t range; + TDB_DATA data; + char *keystr; + struct dom_sid sid; + + /* can this be one of our ids? */ + if (map->xid.id < cfg->minvalue) { + DEBUG(10, ("id %d is lower than minimum value, " + "ignoring mapping request\n", map->xid.id)); + map->status = ID_UNKNOWN; + return NT_STATUS_OK; + } + + if (map->xid.id > (cfg->minvalue + cfg->rangesize * cfg->maxranges)) { + DEBUG(10, ("id %d is outside of maximum id value, " + "ignoring mapping request\n", map->xid.id)); + map->status = ID_UNKNOWN; + return NT_STATUS_OK; + } + + /* determine the range of this uid */ + range = ((map->xid.id - cfg->minvalue) / cfg->rangesize); + + keystr = talloc_asprintf(memctx, "%u", range); + if (!keystr) { + return NT_STATUS_NO_MEMORY; + } + + data = dbwrap_fetch_bystring(autorid_db, memctx, keystr); + + if (!data.dptr) { + DEBUG(4, ("id %d belongs to range %d which does not have " + "domain mapping, ignoring mapping request\n", + map->xid.id, range)); + } else { + string_to_sid(&sid, (const char *)data.dptr); + + sid_compose(map->sid, &sid, + (map->xid.id - cfg->minvalue - + range * cfg->rangesize)); + + /* We **really** should have some way of validating + the SID exists and is the correct type here. But + that is a deficiency in the idmap_rid design. */ + + map->status = ID_MAPPED; + } + return NT_STATUS_OK; +} + +/********************************** + Single sid to id lookup function. +**********************************/ + +static NTSTATUS idmap_autorid_sid_to_id(TALLOC_CTX * memctx, + struct autorid_global_config *global, + struct autorid_domain_config *domain, + struct id_map *map) +{ + uint32_t rid; + + sid_peek_rid(map->sid, &rid); + + /* if the rid is higher than the size of the range, we cannot map it */ + if (rid >= global->rangesize) { + map->status = ID_UNKNOWN; + DEBUG(2, ("RID %d is larger then size of range (%d), " + "user cannot be mapped\n", rid, global->rangesize)); + return NT_STATUS_UNSUCCESSFUL; + } + map->xid.id = global->minvalue + + (global->rangesize * domain->domainnum)+rid; + + /* We **really** should have some way of validating + the SID exists and is the correct type here. But + that is a deficiency in the idmap_rid design. */ + + map->status = ID_MAPPED; + + return NT_STATUS_OK; +} + +/********************************** + lookup a set of unix ids. +**********************************/ + +static NTSTATUS idmap_autorid_unixids_to_sids(struct idmap_domain *dom, + struct id_map **ids) +{ + struct autorid_global_config *globalcfg; + TALLOC_CTX *ctx; + NTSTATUS ret; + int i; + + /* initialize the status to avoid surprise */ + for (i = 0; ids[i]; i++) { + ids[i]->status = ID_UNKNOWN; + } + + globalcfg = talloc_get_type(dom->private_data, + struct autorid_global_config); + + ctx = talloc_new(dom); + if (!ctx) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; ids[i]; i++) { + + ret = idmap_autorid_id_to_sid(ctx, globalcfg, ids[i]); + + if ((!NT_STATUS_IS_OK(ret)) && + (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) { + /* some fatal error occurred, log it */ + DEBUG(3, ("Unexpected error resolving an ID " + " (%d)\n", ids[i]->xid.id)); + goto failure; + } + } + + talloc_free(ctx); + return NT_STATUS_OK; + + failure: + talloc_free(ctx); + return ret; + +} + +/********************************** + lookup a set of sids. +**********************************/ + +static NTSTATUS idmap_autorid_sids_to_unixids(struct idmap_domain *dom, + struct id_map **ids) +{ + struct autorid_global_config *global; + struct autorid_domain_config *domaincfg; + struct dom_sid *domainsid; + struct winbindd_tdc_domain *domain; + uint32_t rid; + TALLOC_CTX *ctx; + NTSTATUS ret; + int i; + + ctx = talloc_new(dom); + domainsid = talloc(ctx, struct dom_sid); + if (!ctx || !domainsid) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto failure; + } + + if ((domaincfg = TALLOC_ZERO_P(ctx, + struct autorid_domain_config)) == NULL) { + DEBUG(0, ("Out of memory!\n")); + ret = NT_STATUS_NO_MEMORY; + goto failure; + } + + /* initialize the status to avoid surprise */ + for (i = 0; ids[i]; i++) { + ids[i]->status = ID_UNKNOWN; + } + + global = talloc_get_type(dom->private_data, + struct autorid_global_config); + + for (i = 0; ids[i]; i++) { + ZERO_STRUCTP(domainsid); + sid_copy(domainsid, ids[i]->sid); + if (!sid_split_rid(domainsid, &rid)) { + DEBUG(4, ("Could not determine domain SID from %s, " + "ignoring mapping request\n", + sid_string_dbg(ids[i]->sid))); + continue; + } + + domain = wcache_tdc_fetch_domainbysid(ctx, domainsid); + if (domain == NULL) { + DEBUG(10, ("Ignoring unknown domain sid %s\n", + sid_string_dbg(domainsid))); + continue; + } + + ZERO_STRUCTP(domaincfg); + domaincfg->sid = domain->sid; + domaincfg->globalcfg = global; + + ret = dbwrap_trans_do(autorid_db, idmap_autorid_get_domainrange, + domaincfg); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(3, ("Could not determine range for domain, " + "check previous messages for reason\n")); + goto failure; + } + + ret = idmap_autorid_sid_to_id(ctx, global, domaincfg, ids[i]); + + if ((!NT_STATUS_IS_OK(ret)) && + (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) { + /* some fatal error occurred, log it */ + DEBUG(3, ("Unexpected error resolving a SID (%s)\n", + sid_string_dbg(ids[i]->sid))); + goto failure; + } + } + + talloc_free(ctx); + return NT_STATUS_OK; + + failure: + talloc_free(ctx); + return ret; + +} + +/* + * open and initialize the database which stores the ranges for the domains + */ +static NTSTATUS idmap_autorid_db_init(void) +{ + int32_t hwm; + + if (autorid_db) { + /* its already open */ + return NT_STATUS_OK; + } + + /* Open idmap repository */ + autorid_db = db_open(NULL, state_path("autorid.tdb"), 0, + TDB_DEFAULT, O_RDWR | O_CREAT, 0644); + + if (!autorid_db) { + DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", + state_path("autorid.tdb"))); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Initialize high water mark for the currently used range to 0 */ + hwm = dbwrap_fetch_int32(autorid_db, HWM); + if ((hwm < 0)) { + if (!NT_STATUS_IS_OK + (dbwrap_trans_store_int32(autorid_db, HWM, 0))) { + DEBUG(0, + ("Unable to initialise HWM in autorid " + "database\n")); + return NT_STATUS_INTERNAL_DB_ERROR; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS idmap_autorid_initialize(struct idmap_domain *dom, + const char *params) +{ + struct autorid_global_config *config; + NTSTATUS status; + + config = TALLOC_ZERO_P(dom, struct autorid_global_config); + if (!config) { + DEBUG(0, ("Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + status = idmap_autorid_db_init(); + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + + config->minvalue = dom->low_id; + config->rangesize = lp_parm_int(-1, "autorid", "rangesize", 100000); + + if (config->rangesize < 2000) { + DEBUG(1, ("autorid rangesize must be at least 2000\n")); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + config->maxranges = (dom->high_id - dom->low_id + 1) / + config->rangesize; + + if (config->maxranges == 0) { + DEBUG(1, ("allowed uid range is smaller then rangesize, " + "increase uid range or decrease rangesize\n")); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + /* check if the high-low limit is a multiple of the rangesize */ + if ((dom->high_id - dom->low_id + 1) % config->rangesize != 0) { + DEBUG(5, ("High uid-low uid difference of %d " + "is not a multiple of the rangesize %d, " + "limiting ranges to lower boundary number of %d\n", + (dom->high_id - dom->low_id + 1), config->rangesize, + config->maxranges)); + } + + DEBUG(5, ("%d domain ranges with a size of %d are available\n", + config->maxranges, config->rangesize)); + + dom->private_data = config; + + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + + return NT_STATUS_OK; + + error: + talloc_free(config); + return status; +} + +/* + Close the idmap tdb instance +*/ +static NTSTATUS idmap_autorid_close(struct idmap_domain *dom) +{ + /* don't do anything */ + return NT_STATUS_OK; +} + +static struct idmap_methods autorid_methods = { + .init = idmap_autorid_initialize, + .unixids_to_sids = idmap_autorid_unixids_to_sids, + .sids_to_unixids = idmap_autorid_sids_to_unixids, + .close_fn = idmap_autorid_close +}; + +NTSTATUS idmap_autorid_init(void) +{ + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, + "autorid", &autorid_methods); +} |