Patch: Add a script id mapping module ...

Jeremy Allison jra at samba.org
Wed Jan 7 15:29:11 MST 2015


On Sat, Dec 27, 2014 at 08:06:37AM -0800, Richard Sharpe wrote:
> On Fri, Dec 26, 2014 at 8:03 AM, Richard Sharpe
> <realrichardsharpe at gmail.com> wrote:
> > Hi folks,
> >
> > I have cleaned up and tested the script module I proposed. I moved
> > from using popen to an internal Samba function and I added support for
> > ID_TYPE_BOTH after some discussion with Michael Adam. I also cleaned
> > up the documentation a bit and tested that the code works.
> >
> > Please provide further feedback or push if acceptable.
> 
> Reissued because I woke up in a cold sweat and realized that there was
> still as small problem.

OK, reviewing this now and I notice it uses:

NT_STATUS_HAVE_NO_MEMORY

macros. Can you remove them please ? I know they
might be in the original code, but we're trying
to move away from them in modern Samba.

Sorry,

Jeremy.




> From 58821cba7e178697bafcb1219a56dfc36d45baeb Mon Sep 17 00:00:00 2001
> From: Richard Sharpe <realrichardsharpe at gmail.com>
> Date: Tue, 23 Dec 2014 17:33:34 -0800
> Subject: [PATCH] Add a script-only idmap module.
> 
> In this third version I have cleaned up some unused variable warnings that
> only the Samba 3 build found and added a man page based on the idmap_tdb2
> man page. I have also added support for ID_TYPE_BOTH mappings and replaced
> calls to popen with something safer.
> 
> Signed-off-by: Richard Sharpe <rsharpe at samba.org>
> ---
>  docs-xml/manpages/idmap_script.8.xml | 164 +++++++++++++++
>  source3/winbindd/idmap_script.c      | 388 +++++++++++++++++++++++++++++++++++
>  source3/winbindd/wscript_build       |   8 +
>  source3/wscript                      |   1 +
>  4 files changed, 561 insertions(+)
>  create mode 100644 docs-xml/manpages/idmap_script.8.xml
>  create mode 100644 source3/winbindd/idmap_script.c
> 
> diff --git a/docs-xml/manpages/idmap_script.8.xml b/docs-xml/manpages/idmap_script.8.xml
> new file mode 100644
> index 0000000..d6467f0
> --- /dev/null
> +++ b/docs-xml/manpages/idmap_script.8.xml
> @@ -0,0 +1,164 @@
> +<?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_script.8">
> +
> +<refmeta>
> +	<refentrytitle>idmap_script</refentrytitle>
> +	<manvolnum>8</manvolnum>
> +	<refmiscinfo class="source">Samba</refmiscinfo>
> +	<refmiscinfo class="manual">System Administration tools</refmiscinfo>
> +	<refmiscinfo class="version">4.2</refmiscinfo>
> +</refmeta>
> +
> +
> +<refnamediv>
> +	<refname>idmap_tdb2</refname>
> +	<refpurpose>Samba's idmap_script Backend for Winbind</refpurpose>
> +</refnamediv>
> +
> +<refsynopsisdiv>
> +	<title>DESCRIPTION</title>
> +
> +	<para>
> +	The idmap_script plugin is a substitute for the idmap_tdb2
> +	backend used by winbindd for storing SID/uid/gid mapping tables
> +	in clustered environments with Samba and CTDB. It is a read only
> +	backend that uses a script to perform mapping.
> +	</para>
> +
> +	<para>
> +	It was developed out of the idmap_tdb2 back end and does not store
> +	SID/uid/gid mappings in a TDB, since the winbind_cache tdb will 
> +	store the mappings once they are provided.
> +	</para>
> +</refsynopsisdiv>
> +
> +<refsect1>
> +	<title>IDMAP OPTIONS</title>
> +
> +	<variablelist>
> +		<varlistentry>
> +		<term>range = low - high</term>
> +		<listitem><para>
> +			Defines the available matching uid and gid range for which the
> +			backend is authoritative.
> +		</para></listitem>
> +		</varlistentry>
> +
> +		<varlistentry>
> +		<term>script</term>
> +		<listitem><para>
> +			This option can be used to configure an external program
> +			for performing id mappings.
> +		</para></listitem>
> +		</varlistentry>
> +	</variablelist>
> +</refsect1>
> +
> +<refsect1>
> +	<title>IDMAP SCRIPT</title>
> +
> +	<para>
> +	The tdb2 idmap backend supports an external program for performing id mappings
> +	through the smb.conf option <parameter>idmap config * : script</parameter> or
> +	its deprecated legacy form <parameter>idmap : script</parameter>.
> +	</para>
> +
> +	<para>
> +	The mappings obtained by the script are then stored in the idmap tdb2
> +	database instead of mappings created by the incrementing id counters.
> +	It is therefore important that the script covers the complete range of
> +	SIDs that can be passed in for SID to Unix ID mapping, since otherwise
> +	SIDs unmapped by the script might get mapped to IDs that had
> +	previously been mapped by the script.
> +	</para>
> +
> +	<para>
> +	The script should accept the following command line options.
> +	</para>
> +
> +	<programlisting>
> +	SIDTOID S-1-xxxx
> +	IDTOSID UID xxxx
> +	IDTOSID GID xxxx
> +	IDTOSID XID xxxx
> +	</programlisting>
> +
> +	<para>
> +	And it should return one of the following responses as a single line of
> +	text.
> +	</para>
> +
> +	<programlisting>
> +	UID:yyyy
> +	GID:yyyy
> +	XID:yyyy
> +	SID:ssss
> +	ERR:yyyy
> +	</programlisting>
> +
> +	<para>
> +	XID indicates that the ID returned should be both a UID and a GID.
> +	That is, it requests an ID_TYPE_BOTH, but it is ultimately up to
> +	the script whether or not it can honor that request. It can choose
> +	to return a UID or a GID mapping only.
> +	</para>
> +</refsect1>
> +
> +<refsect1>
> +	<title>EXAMPLES</title>
> +
> +	<para>
> +	This example shows how script is used as a the default idmap backend
> +	using an external program via the script parameter:
> +	</para>
> +
> +	<programlisting>
> +	[global]
> +	idmap config * : backend = script
> +	idmap config * : range = 1000000-2000000
> +	idmap config * : script = /usr/local/samba/bin/idmap_script.sh
> +	</programlisting>
> +
> +	<para>
> +	This shows a simple script to partially perform the task:
> +	</para>
> +
> +	<programlisting>
> +	#!/bin/sh
> +	#
> +	# Uncomment this if you want some logging
> +	#echo $@ >> /tmp/idmap.sh.log
> +	if [ "$1" == "SIDTOID" ]
> +	then
> +		# Note. The number returned has to be within the range defined
> +		#echo "Sending UID:1000005" >> /tmp/idmap.sh.log
> +		echo "UID:1000005"
> +		exit 0
> +	else
> +		#echo "Sending ERR: No idea what to do" >> /tmp/idmap.sh.log
> +		echo "ERR: No idea what to do"
> +		exit 1
> +	fi
> +	</programlisting>
> +
> +	<para>
> +	Clearly, this script is not enough, as it should probably use wbinfo
> +	to determine if an incoming SID is a user or group SID and then
> +	look up the mapping in a table or use some other mechanism for
> +	mapping SIDs to UIDs and etc.
> +	</para>
> +</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/winbindd/idmap_script.c b/source3/winbindd/idmap_script.c
> new file mode 100644
> index 0000000..ff28691
> --- /dev/null
> +++ b/source3/winbindd/idmap_script.c
> @@ -0,0 +1,388 @@
> +/* 
> +   Unix SMB/CIFS implementation.
> +
> +   idmap script backend, used for Samba setups where you need to map SIDs to
> +   specific UIDs/GIDs.
> +
> +   Copyright (C) Richard Sharpe 2014.
> +
> +   This is heavily based upon idmap_tdb2.c, which is:
> +
> +   Copyright (C) Tim Potter 2000
> +   Copyright (C) Jim McDonough <jmcd at us.ibm.com> 2003
> +   Copyright (C) Jeremy Allison 2006
> +   Copyright (C) Simo Sorce 2003-2006
> +   Copyright (C) Michael Adam 2009-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 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 "system/filesys.h"
> +#include "winbindd.h"
> +#include "idmap.h"
> +#include "idmap_rw.h"
> +#include "../libcli/security/dom_sid.h"
> +
> +#undef DBGC_CLASS
> +#define DBGC_CLASS DBGC_IDMAP
> +
> +struct idmap_script_context {
> +	const char *script; /* script to provide idmaps */
> +};
> +
> +/*
> +  run a script to perform a mapping
> +
> +  The script should accept the following command lines:
> +
> +      SIDTOID S-1-xxxx -> XID:<id> | ERR:<str>
> +      SIDTOID S-1-xxxx -> UID:<id> | ERR:<str>
> +      SIDTOID S-1-xxxx -> GID:<id> | ERR:<str>
> +      IDTOSID XID xxxx -> SID:<sid> | ERR:<str>
> +      IDTOSID UID xxxx -> SID:<sid> | ERR:<str>
> +      IDTOSID GID xxxx -> SID:<sid> | ERR:<str>
> +
> +  where XID means both a UID and a GID. This is the case for ID_TYPE_BOTH.
> +
> +  TODO: Needs more validation ... like that we got a UID when we asked for one.
> + */
> +static NTSTATUS idmap_script_script(struct idmap_script_context *ctx,
> +				    struct id_map *map, const char *fmt, ...)
> +{
> +	va_list ap;
> +	char *cmd, **lines;
> +	int numlines = 0;
> +	unsigned long v;
> +
> +	cmd = talloc_asprintf(ctx, "%s ", ctx->script);
> +	NT_STATUS_HAVE_NO_MEMORY(cmd);	
> +
> +	va_start(ap, fmt);
> +	cmd = talloc_vasprintf_append(cmd, fmt, ap);
> +	va_end(ap);
> +	NT_STATUS_HAVE_NO_MEMORY(cmd);
> +
> +	lines = file_lines_pload(cmd, &numlines);
> +	talloc_free(cmd);
> +	if (!lines) {
> +		return NT_STATUS_NONE_MAPPED;
> +	}
> +
> +	DEBUG(10,("idmap script gave %d lines, first: %s\n", numlines,
> +		lines[0]));
> +
> +	if (sscanf(lines[0], "XID:%lu", &v) == 1) {
> +		map->xid.id   = v;
> +		map->xid.type = ID_TYPE_BOTH;
> +	} else if (sscanf(lines[0], "UID:%lu", &v) == 1) {
> +		map->xid.id   = v;
> +		map->xid.type = ID_TYPE_UID;
> +	} else if (sscanf(lines[0], "GID:%lu", &v) == 1) {
> +		map->xid.id   = v;
> +		map->xid.type = ID_TYPE_GID;		
> +	} else if (strncmp(lines[0], "SID:S-", 6) == 0) {
> +		if (!string_to_sid(map->sid, &lines[0][4])) {
> +			DEBUG(0,("Bad SID in '%s' from idmap script %s\n",
> +				 lines[0], ctx->script));
> +			talloc_free(lines);
> +			return NT_STATUS_NONE_MAPPED;			
> +		}
> +	} else {
> +		DEBUG(0,("Bad reply '%s' from idmap script %s\n",
> +			 lines[0], ctx->script));
> +		talloc_free(lines);
> +		return NT_STATUS_NONE_MAPPED;
> +	}
> +
> +	talloc_free(lines);
> +	return NT_STATUS_OK;
> +}
> +
> +/*
> +  Single id to sid lookup function. 
> +*/
> +static NTSTATUS idmap_script_id_to_sid(struct idmap_domain *dom,
> +				       struct id_map *map)
> +{
> +	NTSTATUS ret;
> +	char *keystr;
> +	char *sidstr;
> +	struct idmap_script_context *ctx = dom->private_data;
> +
> +	if (!dom || !map) {
> +		return NT_STATUS_INVALID_PARAMETER;
> +	}
> +
> +	/* apply filters before checking */
> +	if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
> +		DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
> +				map->xid.id, dom->low_id, dom->high_id));
> +		return NT_STATUS_NONE_MAPPED;
> +	}
> +
> +	switch (map->xid.type) {
> +
> +	case ID_TYPE_UID:
> +		keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
> +		break;
> +
> +	case ID_TYPE_GID:
> +		keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
> +		break;
> +
> +	case ID_TYPE_BOTH:
> +		keystr = talloc_asprintf(ctx, "XID %lu", (unsigned long)map->xid.id);
> +		break;
> +
> +	default:
> +		DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
> +		return NT_STATUS_INVALID_PARAMETER;
> +	}
> +
> +	if (keystr == NULL) {
> +		DEBUG(0, ("Out of memory!\n"));
> +		ret = NT_STATUS_NO_MEMORY;
> +		goto done;
> +	}
> +
> +	DEBUG(10,("Running script to fetch mapping %s\n", keystr));
> +
> +	ret = idmap_script_script(ctx, map, "IDTOSID %s", keystr);
> +	if (!NT_STATUS_IS_OK(ret)) {
> +		goto done;
> +	}
> +
> +	sidstr = sid_string_talloc(keystr, map->sid);
> +	if (!sidstr) {
> +		ret = NT_STATUS_NO_MEMORY;
> +		goto done;
> +	}
> +
> +	DEBUG(10,("Found id %s:%d -> %s\n", keystr, map->xid.id, 
> +		  (const char *)sidstr));
> +	ret = NT_STATUS_OK;
> +
> +done:
> +	talloc_free(keystr);
> +	return ret;
> +}
> +
> +/*
> + Single sid to id lookup function. 
> +*/
> +static NTSTATUS idmap_script_sid_to_id(struct idmap_domain *dom,
> +				       struct id_map *map)
> +{
> +	NTSTATUS ret;
> +	char *keystr;
> +	struct idmap_script_context *ctx = dom->private_data;
> +	TALLOC_CTX *tmp_ctx = talloc_stackframe();
> +
> +	keystr = sid_string_talloc(tmp_ctx, map->sid);
> +	if (keystr == NULL) {
> +		DEBUG(0, ("Out of memory!\n"));
> +		ret = NT_STATUS_NO_MEMORY;
> +		goto done;
> +	}
> +
> +	DEBUG(10,("Fetching record %s\n", keystr));
> +
> +	if (ctx->script == NULL) {
> +		ret = NT_STATUS_NONE_MAPPED;
> +		goto done;
> +	}
> +
> +	ret = idmap_script_script(ctx, map, "SIDTOID %s", keystr);
> +	if (!NT_STATUS_IS_OK(ret)) {
> +		goto done;
> +	}
> +
> +	/* apply filters before returning result */
> +	if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
> +		DEBUG(5, ("Script returned id (%u) out of range (%u - %u)."
> +			  " Filtered!\n",
> +			  map->xid.id, dom->low_id, dom->high_id));
> +		ret = NT_STATUS_NONE_MAPPED;
> +		goto done;
> +	}
> +
> +done:
> +	talloc_free(tmp_ctx);
> +	return ret;
> +}
> +
> +NTSTATUS idmap_script_unixids_to_sids(struct idmap_domain *dom,
> +				      struct id_map **ids)
> +{
> +	NTSTATUS ret;
> +	int i, num_mapped = 0;
> +
> +	DEBUG(10, ("%s called ...\n", __func__));
> +	/* Init status to avoid surprise ... */
> +	for (i = 0; ids[i]; i++) {
> +		ids[i]->status = ID_UNKNOWN;
> +	}
> +
> +	for (i = 0; ids[i]; i++) {
> +		ret = idmap_script_id_to_sid(dom, ids[i]);
> +		if (!NT_STATUS_IS_OK(ret)) {
> +			if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
> +				ids[i]->status = ID_UNMAPPED;
> +				continue;
> +			}
> +
> +			/* 
> +			 * We cannot keep going if it is other than mapping 
> +			 * failed.
> +			 */
> +			goto done;
> +		}
> +
> +		ids[i]->status = ID_MAPPED;
> +		num_mapped++;
> +	}
> +
> +	ret = NT_STATUS_OK;
> +
> +done:
> +	if (NT_STATUS_IS_OK(ret)) {
> +		if (i == 0 || num_mapped == 0) {
> +			ret = NT_STATUS_NONE_MAPPED;
> +		}
> +		else if (num_mapped < i) {
> +			ret = STATUS_SOME_UNMAPPED;
> +		} else {
> +			DEBUG(10, ("Returning NT_STATUS_OK\n"));
> +			ret = NT_STATUS_OK;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +NTSTATUS idmap_script_sids_to_unixids(struct idmap_domain *dom,
> +				      struct id_map **ids)
> +{
> +	NTSTATUS ret;
> +	int i, num_mapped = 0;
> +
> +	DEBUG(10, ("%s called ...\n", __func__));
> +	/* Init status to avoid surprise ... */
> +	for (i = 0; ids[i]; i++) {
> +		ids[i]->status = ID_UNKNOWN;
> +	}
> +
> +	for (i = 0; ids[i]; i++) {
> +		ret = idmap_script_sid_to_id(dom, ids[i]);
> +		if (!NT_STATUS_IS_OK(ret)) {
> +			if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
> +				ids[i]->status = ID_UNMAPPED;
> +				continue;
> +			}
> +
> +			/* 
> +			 * We cannot keep going if it is other than mapping 
> +			 * failed.
> +			 */
> +			goto done;
> +		}
> +
> +		ids[i]->status = ID_MAPPED;
> +		num_mapped++;
> +	}
> +
> +	ret = NT_STATUS_OK;
> +
> +done:
> +	if (NT_STATUS_IS_OK(ret)) {
> +		if (i == 0 || num_mapped == 0) {
> +			ret = NT_STATUS_NONE_MAPPED;
> +		}
> +		else if (num_mapped < i) {
> +			ret = STATUS_SOME_UNMAPPED;
> +		} else {
> +			DEBUG(10, ("Returning NT_STATUS_OK\n"));
> +			ret = NT_STATUS_OK;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + *   Initialise idmap_script database.
> + */
> +static NTSTATUS idmap_script_db_init(struct idmap_domain *dom)
> +{
> +	NTSTATUS ret;
> +	struct idmap_script_context *ctx;
> +	char *config_option = NULL;
> +	const char * idmap_script = NULL;
> +
> +	DEBUG(10, ("%s called ...\n", __func__));
> +
> +	ctx = talloc_zero(dom, struct idmap_script_context);
> +	if (!ctx) {
> +		DEBUG(0, ("Out of memory!\n"));
> +		ret = NT_STATUS_NO_MEMORY;
> +		goto failed;
> +	}
> +
> +	config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
> +	if (config_option == NULL) {
> +		DEBUG(0, ("Out of memory!\n"));
> +		ret = NT_STATUS_NO_MEMORY;
> +		goto failed;
> +	}
> +	ctx->script = lp_parm_const_string(-1, config_option, "script", NULL);
> +	talloc_free(config_option);
> +
> +	/* Do we even need to handle this? */
> +	idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
> +	if (idmap_script != NULL) {
> +		DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
> +			  " Please use 'idmap config * : script' instead!\n"));
> +	}
> +
> +	if (strequal(dom->name, "*") && ctx->script == NULL) {
> +		/* fall back to idmap:script for backwards compatibility */
> +		ctx->script = idmap_script;
> +	}
> +
> +	if (ctx->script) {
> +		DEBUG(1, ("using idmap script '%s'\n", ctx->script));
> +	}
> +
> +	dom->private_data = ctx;
> +	dom->read_only = true; /* We do not allocate!*/
> +
> +	return NT_STATUS_OK;
> +
> +failed:
> +	talloc_free(ctx);
> +	return ret;
> +}
> +
> +static struct idmap_methods db_methods = {
> +	.init            = idmap_script_db_init,
> +	.unixids_to_sids = idmap_script_unixids_to_sids,
> +	.sids_to_unixids = idmap_script_sids_to_unixids,
> +};
> +
> +NTSTATUS idmap_script_init(void)
> +{
> +	return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "script", &db_methods);
> +}
> diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
> index 13658f8..2f7e74d 100644
> --- a/source3/winbindd/wscript_build
> +++ b/source3/winbindd/wscript_build
> @@ -160,3 +160,11 @@ bld.SAMBA3_MODULE('nss_info_sfu',
>                   init_function='',
>                   internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
>                   enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad') and bld.CONFIG_SET("HAVE_LDAP"))
> +
> +bld.SAMBA3_MODULE('idmap_script',
> +                 subsystem='idmap',
> +                 allow_undefined_symbols=True,
> +                 source='idmap_script.c',
> +                 init_function='',
> +                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_script'),
> +                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_script'))
> diff --git a/source3/wscript b/source3/wscript
> index bb57db5..8e04c9e 100644
> --- a/source3/wscript
> +++ b/source3/wscript
> @@ -1578,6 +1578,7 @@ main() {
>                                        vfs_crossrename vfs_linux_xfs_sgid
>                                        vfs_time_audit idmap_autorid idmap_tdb2
>                                        idmap_ad
> +				      idmap_script
>                                        idmap_rid idmap_hash idmap_rfc2307'''))
>  
>      if Options.options.developer:
> -- 
> 1.8.3.1
> 



More information about the samba-technical mailing list