[PATCH v2 2/5] cifs-utils: new plugin architecture for ID mapping code
Jeff Layton
jlayton at samba.org
Tue Dec 18 07:20:16 MST 2012
On Tue, 18 Dec 2012 09:10:45 -0500
Jeff Layton <jlayton at samba.org> wrote:
> Currently, the ACL-related tools in cifs-utils call into the wbclient
> libs directly in order to do their bidding. The wbclient developers want
> to get away from needing to configure winbind on the clients and instead
> allow sssd to handle the mapping in most cases.
>
> This patch represents an initial step in that direction. It adds a
> plugin architecture for cifs-utils, adds wrappers around the calls into
> libwbclient that find an idmap plugin library to use and then has it
> call into that plugin to do the actual ID mapping.
>
> The application will call into a set of routines that find the correct
> plugin and dlopen() it. Currently the plugin is located in a well-known
> location that is settable via autoconf. That location is intended to be
> a symlink that points to the real plugin (generally under $pkglibdir).
>
> The plugin will export a number of functions with well-known names. The
> wrappers find those by using dlsym() and then call them.
>
> Signed-off-by: Jeff Layton <jlayton at samba.org>
> ---
> Makefile.am | 13 +++++--
> cifsidmap.h | 45 ++++++++++++++++++++++
> configure.ac | 9 +++++
> getcifsacl.c | 98 +++++++++++++++++++++---------------------------
> idmap_plugin.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++
> idmap_plugin.h | 46 +++++++++++++++++++++++
> idmapwb.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 367 insertions(+), 58 deletions(-)
> create mode 100644 idmap_plugin.c
> create mode 100644 idmap_plugin.h
> create mode 100644 idmapwb.c
>
> diff --git a/Makefile.am b/Makefile.am
> index 8964b37..bc5e517 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -57,9 +57,8 @@ endif
>
> if CONFIG_CIFSACL
> bin_PROGRAMS += getcifsacl
> -getcifsacl_SOURCES = getcifsacl.c
> -getcifsacl_LDADD = $(WBCLIENT_LIBS)
> -getcifsacl_CFLAGS = $(WBCLIENT_CFLAGS)
> +getcifsacl_SOURCES = getcifsacl.c idmap_plugin.c
> +getcifsacl_LDADD = -ldl
> man_MANS += getcifsacl.1
>
> bin_PROGRAMS += setcifsacl
> @@ -69,4 +68,12 @@ setcifsacl_CFLAGS = $(WBCLIENT_CFLAGS)
> man_MANS += setcifsacl.1
> endif
>
> +if CONFIG_PLUGIN
> +plugindir = $(pkglibdir)
> +plugin_PROGRAMS = idmapwb.so
> +
> +idmapwb.so: idmapwb.c
> + $(CC) $(CFLAGS) $(AM_CFLAGS) $(WBCLIENT_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ $(WBCLIENT_LIBS)
> +endif
> +
> SUBDIRS = contrib
> diff --git a/cifsidmap.h b/cifsidmap.h
> index 9907618..c307333 100644
> --- a/cifsidmap.h
> +++ b/cifsidmap.h
> @@ -34,4 +34,49 @@ struct cifs_sid {
> uint32_t sub_auth[SID_MAX_SUB_AUTHORITIES];
> } __attribute__((packed));
>
> +/* Plugins should implement the following functions: */
> +
> +/**
> + * cifs_idmap_init_plugin - Initialize the plugin interface
> + * @handle - return pointer for an opaque handle
> + * @errmsg - pointer to error message pointer
> + *
> + * This function should do whatever is required to establish a context
> + * for later ID mapping operations. The "handle" is an opaque context
> + * cookie that will be passed in on subsequent ID mapping operations.
> + * The errmsg is used to pass back an error string both during the init
> + * and in subsequent idmapping functions. On any error, the plugin
> + * should point *errmsg at a string describing that error. Returns 0
> + * on success and non-zero on error.
> + *
> + * int cifs_idmap_init_plugin(void **handle, const char **errmsg);
> + */
> +
> +/**
> + * cifs_idmap_exit_plugin - Destroy an idmapping context
> + * @handle - context handle that should be destroyed
> + *
> + * When programs are finished with the idmapping plugin, they'll call
> + * this function to destroy any context that was created during the
> + * init_plugin. The handle passed back in was the one given by the init
> + * routine.
> + *
> + * void cifs_idmap_exit_plugin(void *handle);
> + */
> +
> +/**
> + * cifs_idmap_sid_to_str - convert cifs_sid to a string
> + * @handle - context handle
> + * @sid - pointer to a cifs_sid
> + * @name - return pointer for the name
> + *
> + * This function should convert the given cifs_sid to a string
> + * representation in a heap-allocated buffer. The caller of this
> + * function is expected to free "name" on success. Returns 0 on
> + * success and non-zero on error.
> + *
> + * int cifs_idmap_sid_to_str(void *handle, const struct cifs_sid *sid,
> + * char **name);
> + */
> +
> #endif /* _CIFSIDMAP_H */
> diff --git a/configure.ac b/configure.ac
> index b6791ab..9652ad2 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -52,6 +52,14 @@ AC_ARG_ENABLE(systemd,
> enable_systemd=$enableval,
> enable_systemd="maybe")
>
> +# "with" options
> +AC_ARG_WITH(idmap-plugin,
> + [AC_HELP_STRING([--with-idmap-plugin=/path/to/plugin],
> + [Define the path to the plugin that the idmapping infrastructure should use @<:@default=/etc/cifs-utils/idmap-plugin@:>@])],
> + pluginpath=$withval,
> + pluginpath="/etc/cifs-utils/idmap-plugin")
> +AC_DEFINE_UNQUOTED(IDMAP_PLUGIN_PATH, "$pluginpath", [Location of plugin that ID mapping infrastructure should use. (usually a symlink to real plugin)])
> +
> # check for ROOTSBINDIR environment var
> if test -z $ROOTSBINDIR; then
> ROOTSBINDIR="/sbin"
> @@ -230,6 +238,7 @@ AM_CONDITIONAL(CONFIG_CIFSUPCALL, [test "$enable_cifsupcall" != "no"])
> AM_CONDITIONAL(CONFIG_CIFSCREDS, [test "$enable_cifscreds" != "no"])
> AM_CONDITIONAL(CONFIG_CIFSIDMAP, [test "$enable_cifsidmap" != "no"])
> AM_CONDITIONAL(CONFIG_CIFSACL, [test "$enable_cifsacl" != "no"])
> +AM_CONDITIONAL(CONFIG_PLUGIN, [test "$enable_cifsidmap" != "no" -o "$enable_cifsacl" != "no"])
>
> LIBCAP_NG_PATH
>
> diff --git a/getcifsacl.c b/getcifsacl.c
> index 550429c..c0b051d 100644
> --- a/getcifsacl.c
> +++ b/getcifsacl.c
> @@ -33,10 +33,13 @@
> #include <stddef.h>
> #include <errno.h>
> #include <limits.h>
> -#include <wbclient.h>
> #include <ctype.h>
> #include <sys/xattr.h>
> #include "cifsacl.h"
> +#include "idmap_plugin.h"
> +
> +static void *plugin_handle;
> +static bool plugin_loaded;
>
> static void
> print_each_ace_mask(uint32_t mask)
> @@ -169,61 +172,33 @@ print_ace_type(uint8_t acetype, int raw)
> }
> }
>
> -/*
> - * Winbind keeps wbcDomainSid fields in host-endian. Copy fields from the
> - * csid to the wsid, while converting the subauthority fields from LE.
> - */
> static void
> -csid_to_wsid(struct wbcDomainSid *wsid, const struct cifs_sid *csid)
> +print_sid(struct cifs_sid *csid, int raw)
> {
> - int i;
> - uint8_t num_subauth = (csid->num_subauth <= WBC_MAXSUBAUTHS) ?
> - csid->num_subauth : WBC_MAXSUBAUTHS;
> -
> - wsid->sid_rev_num = csid->revision;
> - wsid->num_auths = num_subauth;
> - for (i = 0; i < NUM_AUTHS; i++)
> - wsid->id_auth[i] = csid->authority[i];
> - for (i = 0; i < num_subauth; i++)
> - wsid->sub_auths[i] = le32toh(csid->sub_auth[i]);
> -}
> -
> -static void
> -print_sid(struct cifs_sid *sidptr, int raw)
> -{
> - int i;
> - wbcErr rc;
> - char *domain_name = NULL;
> - char *sidname = NULL;
> - enum wbcSidType sntype;
> + int i, rc;
> + char *name;
> unsigned long long id_auth_val;
> - struct wbcDomainSid wsid;
>
> - csid_to_wsid(&wsid, sidptr);
> + if (raw || !plugin_loaded)
> + goto print_sid_raw;
>
> - if (raw)
> + rc = sid_to_str(plugin_handle, csid, &name);
> + if (rc)
> goto print_sid_raw;
>
> - rc = wbcLookupSid(&wsid, &domain_name, &sidname, &sntype);
> - if (WBC_ERROR_IS_OK(rc)) {
> - printf("%s", domain_name);
> - if (strlen(domain_name))
> - printf("%c", '\\');
> - printf("%s", sidname);
> - wbcFreeMemory(domain_name);
> - wbcFreeMemory(sidname);
> - return;
> - }
> + printf("%s", name);
> + free(name);
> + return;
>
> print_sid_raw:
> - printf("S-%hhu", wsid.sid_rev_num);
> + printf("S-%hhu", csid->revision);
>
> - id_auth_val = (unsigned long long)wsid.id_auth[5];
> - id_auth_val += (unsigned long long)wsid.id_auth[4] << 8;
> - id_auth_val += (unsigned long long)wsid.id_auth[3] << 16;
> - id_auth_val += (unsigned long long)wsid.id_auth[2] << 24;
> - id_auth_val += (unsigned long long)wsid.id_auth[1] << 32;
> - id_auth_val += (unsigned long long)wsid.id_auth[0] << 48;
> + id_auth_val = (unsigned long long)csid->authority[5];
> + id_auth_val += (unsigned long long)csid->authority[4] << 8;
> + id_auth_val += (unsigned long long)csid->authority[3] << 16;
> + id_auth_val += (unsigned long long)csid->authority[2] << 24;
> + id_auth_val += (unsigned long long)csid->authority[1] << 32;
> + id_auth_val += (unsigned long long)csid->authority[0] << 48;
>
> /*
> * MS-DTYP states that if the authority is >= 2^32, then it should be
> @@ -234,8 +209,8 @@ print_sid_raw:
> else
> printf("-0x%llx", id_auth_val);
>
> - for (i = 0; i < wsid.num_auths; i++)
> - printf("-%u", wsid.sub_auths[i]);
> + for (i = 0; i < csid->num_subauth; i++)
> + printf("-%u", csid->sub_auth[i]);
^^^^^^^^^^^
Hah, found a bug already. We need to convert the endianness of
csid->sub_auth[i] before printing it. Now fixed in my private tree, but
I'm not going to bother to resend unless there are larger changes
needed.
> }
>
> static void
> @@ -368,7 +343,8 @@ getcifsacl_usage(const char *prog)
> int
> main(const int argc, char *const argv[])
> {
> - int c, raw = 0;
> + int c, ret = 0;
> + bool raw = false;
> ssize_t attrlen;
> size_t bufsize = BUFSIZE;
> char *filename, *attrval;
> @@ -379,7 +355,7 @@ main(const int argc, char *const argv[])
> printf("Version: %s\n", VERSION);
> goto out;
> case 'r':
> - raw = 1;
> + raw = true;
> break;
> default:
> break;
> @@ -392,20 +368,31 @@ main(const int argc, char *const argv[])
> filename = argv[1];
> else {
> getcifsacl_usage(basename(argv[0]));
> - return 0;
> + goto out;
> + }
> +
> + if (!raw && !plugin_loaded) {
> + ret = init_plugin(&plugin_handle);
> + if (ret)
> + printf("WARNING: unable to initialize idmapping plugin: %s\n",
> + plugin_errmsg);
> + else
> + plugin_loaded = true;
> }
>
> cifsacl:
> if (bufsize >= XATTR_SIZE_MAX) {
> printf("buffer to allocate exceeds max size of %d\n",
> XATTR_SIZE_MAX);
> - return -1;
> + ret = -1;
> + goto out;
> }
>
> attrval = malloc(bufsize * sizeof(char));
> if (!attrval) {
> printf("error allocating memory for attribute value buffer\n");
> - return -1;
> + ret = -1;
> + goto out;
> }
>
> attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
> @@ -421,7 +408,8 @@ cifsacl:
> parse_sec_desc((struct cifs_ntsd *)attrval, attrlen, raw);
>
> free(attrval);
> -
> out:
> - return 0;
> + if (plugin_loaded)
> + exit_plugin(plugin_handle);
> + return ret;
> }
> diff --git a/idmap_plugin.c b/idmap_plugin.c
> new file mode 100644
> index 0000000..4dbeca7
> --- /dev/null
> +++ b/idmap_plugin.c
> @@ -0,0 +1,99 @@
> +/*
> + * ID Mapping Plugin interface for cifs-utils
> + * Copyright (C) 2012 Jeff Layton (jlayton at 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/>.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include "config.h"
> +#endif /* HAVE_CONFIG_H */
> +
> +#include <dlfcn.h>
> +#include <errno.h>
> +#include <stdint.h>
> +
> +#include "cifsacl.h"
> +
> +const char *plugin_errmsg;
> +static void *plugin;
> +
> +static void *
> +resolve_symbol(const char *symbol_name)
> +{
> + void *symbol;
> +
> + dlerror();
> + symbol = dlsym(plugin, symbol_name);
> + if (!symbol)
> + plugin_errmsg = dlerror();
> + return symbol;
> +}
> +
> +/*
> + * open the plugin. Note that we leave it open over the life of the
> + * program. It gets closed on exit.
> + */
> +static int
> +open_plugin(void)
> +{
> + if (plugin)
> + return 0;
> +
> + plugin = dlopen(IDMAP_PLUGIN_PATH, RTLD_LAZY);
> + if (!plugin) {
> + plugin_errmsg = dlerror();
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +int
> +init_plugin(void **handle)
> +{
> + int ret;
> + int (*init)(void **, const char **);
> +
> + ret = open_plugin();
> + if (ret)
> + return ret;
> +
> + init = resolve_symbol("cifs_idmap_init_plugin");
> + if (!init)
> + return -ENOSYS;
> + return (*init)(handle, &plugin_errmsg);
> +}
> +
> +void
> +exit_plugin(void *handle)
> +{
> + int (*exit)(void *);
> +
> + exit = resolve_symbol("cifs_idmap_exit_plugin");
> + if (exit)
> + (*exit)(handle);
> +}
> +
> +int
> +sid_to_str(void *handle, const struct cifs_sid *sid, char **name)
> +{
> + int (*entry)(void *, const struct cifs_sid *, char **);
> +
> + *(void **)(&entry) = resolve_symbol("cifs_idmap_sid_to_str");
> + if (!entry)
> + return -ENOSYS;
> +
> + return (*entry)(handle, sid, name);
> +}
> diff --git a/idmap_plugin.h b/idmap_plugin.h
> new file mode 100644
> index 0000000..277bb12
> --- /dev/null
> +++ b/idmap_plugin.h
> @@ -0,0 +1,46 @@
> +/*
> + * ID Mapping Plugin interface for cifs-utils
> + * Copyright (C) 2012 Jeff Layton (jlayton at 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 "cifsidmap.h"
> +
> +#ifndef _IDMAP_PLUGIN_H
> +#define _IDMAP_PLUGIN_H
> +
> +/*
> + * On error, plugin functions will set this pointer to a string description
> + * of the error. The string should not be freed.
> + */
> +extern const char *plugin_errmsg;
> +
> +/*
> + * External API. Programs should call this to use the plugin functionality.
> + */
> +
> +/*
> + * Initialize plugin. Returns an opaque handle that should be passed to
> + * other idmapping functions.
> + */
> +extern int init_plugin(void **handle);
> +
> +/* Close out an init'ed handle */
> +extern void exit_plugin(void *handle);
> +
> +/* Convert cifs_sid to a string. Caller must free *name on success */
> +extern int sid_to_str(void *handle, const struct cifs_sid *sid, char **name);
> +
> +#endif /* _IDMAP_PLUGIN_H */
> diff --git a/idmapwb.c b/idmapwb.c
> new file mode 100644
> index 0000000..858028f
> --- /dev/null
> +++ b/idmapwb.c
> @@ -0,0 +1,115 @@
> +/*
> + * Winbind ID Mapping Plugin
> + * Copyright (C) 2012 Jeff Layton (jlayton at 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/>.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include "config.h"
> +#endif /* HAVE_CONFIG_H */
> +
> +#include <stdint.h>
> +#include <endian.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <wbclient.h>
> +
> +#include "cifsidmap.h"
> +
> +static const char **plugin_errmsg;
> +
> +/*
> + * Winbind keeps wbcDomainSid fields in host-endian. Copy fields from the
> + * csid to the wsid, while converting the subauthority fields from LE.
> + */
> +static void
> +csid_to_wsid(struct wbcDomainSid *wsid, const struct cifs_sid *csid)
> +{
> + int i;
> + uint8_t num_subauth = (csid->num_subauth <= WBC_MAXSUBAUTHS) ?
> + csid->num_subauth : WBC_MAXSUBAUTHS;
> +
> + wsid->sid_rev_num = csid->revision;
> + wsid->num_auths = num_subauth;
> + for (i = 0; i < NUM_AUTHS; i++)
> + wsid->id_auth[i] = csid->authority[i];
> + for (i = 0; i < num_subauth; i++)
> + wsid->sub_auths[i] = le32toh(csid->sub_auth[i]);
> +}
> +
> +int
> +cifs_idmap_sid_to_str(void *handle __attribute__ ((unused)),
> + const struct cifs_sid *csid, char **string)
> +{
> + int rc;
> + wbcErr wbcrc;
> + char *domain = NULL;
> + char *name = NULL;
> + enum wbcSidType sntype;
> + struct wbcDomainSid wsid;
> + size_t len;
> +
> + csid_to_wsid(&wsid, csid);
> +
> + wbcrc = wbcLookupSid(&wsid, &domain, &name, &sntype);
> + if (!WBC_ERROR_IS_OK(wbcrc)) {
> + *plugin_errmsg = wbcErrorString(wbcrc);
> + return -EIO;
> + }
> +
> + /* +1 for '\\' and +1 for NULL terminator */
> + len = strlen(domain) + 1 + strlen(name) + 1;
> +
> + *string = malloc(len);
> + if (!*string) {
> + *plugin_errmsg = "Unable to allocate memory";
> + rc = -ENOMEM;
> + goto out;
> + }
> +
> + rc = snprintf(*string, len, "%s\\%s", domain, name);
> + if (rc >= (long)len) {
> + free(*string);
> + *plugin_errmsg = "Resulting string was truncated";
> + *string = NULL;
> + rc = -EIO;
> + } else {
> + rc = 0;
> + }
> +out:
> + wbcFreeMemory(domain);
> + wbcFreeMemory(name);
> + return rc;
> +}
> +
> +/*
> + * For the winbind plugin, we don't need to do anything special on
> + * init or exit
> + */
> +int
> +cifs_idmap_init_plugin(void **handle __attribute__((unused)), const char **errmsg)
> +{
> + plugin_errmsg = errmsg;
> + return 0;
> +}
> +
> +void
> +cifs_idmap_exit_plugin(void *handle __attribute__((unused)))
> +{
> + return;
> +}
--
Jeff Layton <jlayton at samba.org>
More information about the samba-technical
mailing list