From af4ffe1001adcc0a96897e426d26444f07af9aa1 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Thu, 26 Sep 2013 10:27:33 +0200 Subject: Add CIFS idmap plugin https://fedorahosted.org/sssd/ticket/1534 --- src/conf_macros.m4 | 14 ++ src/external/cifsidmap.m4 | 16 ++ src/lib/cifs_idmap_sss/cifs_idmap_sss.c | 340 ++++++++++++++++++++++++++++++++ src/tests/dlopen-tests.c | 3 + 4 files changed, 373 insertions(+) create mode 100644 src/external/cifsidmap.m4 create mode 100644 src/lib/cifs_idmap_sss/cifs_idmap_sss.c (limited to 'src') diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 index 1aecaea4..4be819d4 100644 --- a/src/conf_macros.m4 +++ b/src/conf_macros.m4 @@ -265,6 +265,20 @@ AC_DEFUN([WITH_KRB5_PLUGIN_PATH], AC_SUBST(krb5pluginpath) ]) +AC_DEFUN([WITH_CIFS_PLUGIN_PATH], + [ AC_ARG_WITH([cifs-plugin-path], + [AC_HELP_STRING([--with-cifs-plugin-path=PATH], + [Path to cifs-utils plugin store [/usr/lib/cifs-utils]] + ) + ] + ) + cifspluginpath="${libdir}/cifs-utils" + if test x"$with_cifs_plugin_path" != x; then + cifspluginpath=$with_cifs_plugin_path + fi + AC_SUBST(cifspluginpath) + ]) + AC_DEFUN([WITH_KRB5_RCACHE_DIR], [ AC_ARG_WITH([krb5-rcache-dir], [AC_HELP_STRING([--with-krb5-rcache-dir=PATH], diff --git a/src/external/cifsidmap.m4 b/src/external/cifsidmap.m4 new file mode 100644 index 00000000..53cb8b77 --- /dev/null +++ b/src/external/cifsidmap.m4 @@ -0,0 +1,16 @@ +AC_ARG_ENABLE([cifs-idmap-plugin], + [AS_HELP_STRING([--disable-cifs-idmap-plugin], + [do not build CIFS idmap plugin])], + [build_cifs_idmap_plugin=$enableval], + [build_cifs_idmap_plugin=yes]) + +AS_IF([test x$build_cifs_idmap_plugin = xyes], + [AC_CHECK_HEADER([cifsidmap.h], [], + [AC_MSG_ERROR([you must have the cifsidmap header installed to build the idmap plugin])]) + ]) + +AM_CONDITIONAL([BUILD_CIFS_IDMAP_PLUGIN], + [test x$build_cifs_idmap_plugin = xyes]) + +AM_COND_IF([BUILD_CIFS_IDMAP_PLUGIN], + [AC_DEFINE_UNQUOTED(HAVE_CIFS_IDMAP_PLUGIN, 1, [Build with cifs idmap plugin])]) diff --git a/src/lib/cifs_idmap_sss/cifs_idmap_sss.c b/src/lib/cifs_idmap_sss/cifs_idmap_sss.c new file mode 100644 index 00000000..f9149177 --- /dev/null +++ b/src/lib/cifs_idmap_sss/cifs_idmap_sss.c @@ -0,0 +1,340 @@ +/* + Authors: + Benjamin Franzke + + Copyright (C) 2013 Benjamin Franzke + + 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 . +*/ + +/* TODO: Support well known SIDs as in samba's + * - librpc/idl/security.idl or + * - source4/rpc_server/lsa/lsa_lookup.c? + */ + +/* TODO: Support of [all] samba's Unix SIDs: + * Users: S-1-22-1-%UID + * Groups: S-1-22-2-%GID + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/idmap/sss_idmap.h" +#include "sss_client/idmap/sss_nss_idmap.h" + +#define WORLD_SID "S-1-1-0" + +#ifdef DEBUG +#include +#define debug(str, ...) \ + syslog(0, "%s: " str "\n", \ + __FUNCTION__, ##__VA_ARGS__) +#else +#define debug(...) do { } while(0) +#endif + +struct sssd_ctx { + struct sss_idmap_ctx *idmap; + const char **errmsg; +}; + +#define ctx_set_error(ctx, error) \ + do { \ + *ctx->errmsg = error; \ + debug("%s", error ? error : ""); \ + } while (0); + +int cifs_idmap_init_plugin(void **handle, const char **errmsg) +{ + struct sssd_ctx *ctx; + enum idmap_error_code err; + + if (handle == NULL || errmsg == NULL) + return EINVAL; + + ctx = malloc(sizeof *ctx); + if (!ctx) { + *errmsg = "Failed to allocate context"; + return -1; + } + ctx->errmsg = errmsg; + ctx_set_error(ctx, NULL); + + err = sss_idmap_init(NULL, NULL, NULL, &ctx->idmap); + if (err != IDMAP_SUCCESS) { + ctx_set_error(ctx, idmap_error_string(err)); + free(ctx); + return -1; + } + + *handle = ctx; + return 0; +} + +void cifs_idmap_exit_plugin(void *handle) +{ + struct sssd_ctx *ctx = handle; + + debug("exit"); + + if (ctx == NULL) + return; + + sss_idmap_free(ctx->idmap); + + free(ctx); +} + + +/* Test with `getcifsacl file` on client. */ +int cifs_idmap_sid_to_str(void *handle, const struct cifs_sid *csid, + char **name) +{ + struct sssd_ctx *ctx = handle; + enum idmap_error_code iderr; + char *sid; + enum sss_id_type id_type; + int err; + + iderr = sss_idmap_bin_sid_to_sid(ctx->idmap, (const uint8_t *) csid, + sizeof(*csid), &sid); + if (iderr != IDMAP_SUCCESS) { + ctx_set_error(ctx, idmap_error_string(iderr)); + *name = NULL; + return -1; + } + + debug("sid: %s", sid); + + if (strcmp(sid, WORLD_SID) == 0) { + *name = strdup("\\Everyone"); + if (!*name) { + ctx_set_error(ctx, strerror(ENOMEM)); + return ENOMEM; + } + return 0; + } + + err = sss_nss_getnamebysid(sid, name, &id_type); + if (err != 0) { + ctx_set_error(ctx, strerror(err)); + *name = NULL; + return -err; + } + + /* FIXME: Map Samba Unix SIDs? (sid->id and use getpwuid)? */ + + debug("name: %s", *name); + + return 0; +} + +static int sid_to_cifs_sid(struct sssd_ctx *ctx, const char *sid, + struct cifs_sid *csid) +{ + uint8_t *bsid = NULL; + enum idmap_error_code err; + size_t length; + + err = sss_idmap_sid_to_bin_sid(ctx->idmap, + sid, &bsid, &length); + if (err != IDMAP_SUCCESS) { + ctx_set_error(ctx, idmap_error_string(err)); + return -1; + } + if (length > sizeof(struct cifs_sid)) { + ctx_set_error(ctx, "too large sid length"); + free(bsid); + return -1; + } + + memcpy(csid, bsid, length); + free(bsid); + + return 0; +} + +/* Test with setcifsacl -a */ +int cifs_idmap_str_to_sid(void *handle, const char *name, + struct cifs_sid *csid) +{ + struct sssd_ctx *ctx = handle; + int err; + enum sss_id_type id_type; + char *sid = NULL; + int success = 0; + + debug("%s", name); + + err = sss_nss_getsidbyname(name, &sid, &id_type); + if (err != 0) { + /* Might be a raw string representation of SID, + * try converting that before returning an error. */ + if (sid_to_cifs_sid(ctx, name, csid) == 0) + return 0; + + ctx_set_error(ctx, strerror(err)); + return -err; + } + + if (sid_to_cifs_sid(ctx, sid, csid) != 0) + success = -1; + + free(sid); + + return success; +} + +static int samba_unix_sid_to_id(const char *sid, struct cifs_uxid *cuxid) +{ + id_t id; + uint8_t type; + + if (sscanf(sid, "S-1-22-%hhu-%u", &type, &id) != 2) + return -1; + + switch (type) { + case 1: + cuxid->type = CIFS_UXID_TYPE_UID; + cuxid->id.uid = id; + break; + case 2: + cuxid->type = CIFS_UXID_TYPE_GID; + cuxid->id.gid = id; + break; + default: + cuxid->type = CIFS_UXID_TYPE_UNKNOWN; + return -1; + } + + return 0; +} + +static int sss_sid_to_id(struct sssd_ctx *ctx, const char *sid, + struct cifs_uxid *cuxid) +{ + int err; + enum sss_id_type id_type; + + err = sss_nss_getidbysid(sid, (uint32_t *)&cuxid->id.uid, &id_type); + if (err != 0) { + ctx_set_error(ctx, strerror(err)); + return -1; + } + + switch (id_type) { + case SSS_ID_TYPE_UID: + cuxid->type = CIFS_UXID_TYPE_UID; + break; + case SSS_ID_TYPE_GID: + cuxid->type = CIFS_UXID_TYPE_GID; + break; + case SSS_ID_TYPE_BOTH: + cuxid->type = CIFS_UXID_TYPE_BOTH; + break; + case SSS_ID_TYPE_NOT_SPECIFIED: + default: + return -1; + } + + return 0; +} + +/** + * cifs_idmap_sids_to_ids - convert struct cifs_sids to struct cifs_uxids + * usecase: mount.cifs -o sec=krb5,multiuser,cifsacl,nounix + * test: ls -n on mounted share + */ +int cifs_idmap_sids_to_ids(void *handle, const struct cifs_sid *csid, + const size_t num, struct cifs_uxid *cuxid) +{ + struct sssd_ctx *ctx = handle; + enum idmap_error_code err; + int success = -1; + size_t i; + char *sid; + + debug("num: %zd", num); + + if (num > UINT_MAX) { + ctx_set_error(ctx, "num is too large."); + return EINVAL; + } + + for (i = 0; i < num; ++i) { + err = sss_idmap_bin_sid_to_sid(ctx->idmap, (const uint8_t *) &csid[i], + sizeof(csid[i]), &sid); + if (err != IDMAP_SUCCESS) { + ctx_set_error(ctx, idmap_error_string(err)); + continue; + } + + cuxid[i].type = CIFS_UXID_TYPE_UNKNOWN; + + if (sss_sid_to_id(ctx, sid, &cuxid[i]) == 0 || + samba_unix_sid_to_id(sid, &cuxid[i]) == 0) { + + debug("setting uid of %s to %d", sid, cuxid[i].id.uid); + success = 0; + } + + free(sid); + } + + return success; +} + + +int cifs_idmap_ids_to_sids(void *handle, const struct cifs_uxid *cuxid, + const size_t num, struct cifs_sid *csid) +{ + struct sssd_ctx *ctx = handle; + int err, success = -1; + char *sid; + enum sss_id_type id_type; + size_t i; + + debug("num ids: %zd", num); + + if (num > UINT_MAX) { + ctx_set_error(ctx, "num is too large."); + return EINVAL; + } + + for (i = 0; i < num; ++i) { + err = sss_nss_getsidbyid((uint32_t)cuxid[i].id.uid, &sid, &id_type); + if (err != 0) { + ctx_set_error(ctx, strerror(err)); + csid[i].revision = 0; + /* FIXME: would it be safe to map *any* uid/gids unknown by sssd to + * SAMBA's UNIX SIDs? */ + continue; + } + + if (sid_to_cifs_sid(ctx, sid, csid) == 0) + success = 0; + else + csid[i].revision = 0; + free(sid); + } + + return success; +} diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c index 67fc41c7..40e02daa 100644 --- a/src/tests/dlopen-tests.c +++ b/src/tests/dlopen-tests.c @@ -55,6 +55,9 @@ struct so { #endif #ifdef HAVE_PAC_RESPONDER { "sssd_pac_plugin.so", { LIBPFX"sssd_pac_plugin.so", NULL } }, +#endif +#ifdef HAVE_CIFS_IDMAP_PLUGIN + { "cifs_idmap_sss.so", { LIBPFX"cifs_idmap_sss.so", NULL } }, #endif { "memberof.so", { LIBPFX"memberof.so", NULL } }, { "libsss_child.so", { "libtevent.so", -- cgit