From eb2e21b764d03544d8161e9956d7f70b07b75f77 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 29 Dec 2011 02:17:15 -0500 Subject: nsssrv: shared memory cache server initialization --- Makefile.am | 5 + configure.ac | 1 + contrib/sssd.spec.in | 3 + src/conf_macros.m4 | 17 ++ src/responder/nss/nsssrv.c | 10 ++ src/responder/nss/nsssrv.h | 3 + src/responder/nss/nsssrv_mmap_cache.c | 300 ++++++++++++++++++++++++++++++++++ src/responder/nss/nsssrv_mmap_cache.h | 37 +++++ 8 files changed, 376 insertions(+) create mode 100644 src/responder/nss/nsssrv_mmap_cache.c create mode 100644 src/responder/nss/nsssrv_mmap_cache.h diff --git a/Makefile.am b/Makefile.am index ab5b6c1ad..8067c9800 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,6 +36,7 @@ dbpath = @dbpath@ pluginpath = @pluginpath@ pidpath = @pidpath@ pipepath = @pipepath@ +mcpath = @mcpath@ initdir = @initdir@ systemdunitdir = @systemdunitdir@ logpath = @logpath@ @@ -228,6 +229,7 @@ AM_CPPFLAGS = \ -DSSSD_LIBEXEC_PATH=\"$(sssdlibexecdir)\" \ -DSSSD_INTROSPECT_PATH=\"$(dbusinstropectdir)\" \ -DSSSD_CONF_DIR=\"$(sssdconfdir)\" \ + -DSSS_NSS_MCACHE_DIR=\"$(mcpath)\" \ -DSSS_NSS_SOCKET_NAME=\"$(pipepath)/nss\" \ -DSSS_PAM_SOCKET_NAME=\"$(pipepath)/pam\" \ -DSSS_PAM_PRIV_SOCKET_NAME=\"$(pipepath)/private/pam\" \ @@ -339,6 +341,7 @@ dist_noinst_HEADERS = \ src/responder/nss/nsssrv_private.h \ src/responder/nss/nsssrv_netgroup.h \ src/responder/nss/nsssrv_services.h \ + src/responder/nss/nsssrv_mmap_cache.h \ src/responder/common/negcache.h \ src/responder/sudo/sudosrv_private.h \ src/responder/autofs/autofs_private.h \ @@ -488,6 +491,7 @@ sssd_nss_SOURCES = \ src/responder/nss/nsssrv_cmd.c \ src/responder/nss/nsssrv_netgroup.c \ src/responder/nss/nsssrv_services.c \ + src/responder/nss/nsssrv_mmap_cache.c \ $(SSSD_RESPONDER_OBJ) sssd_nss_LDADD = \ $(TDB_LIBS) \ @@ -1363,6 +1367,7 @@ installsssddirs:: $(DESTDIR)$(sssdconfdir) \ $(DESTDIR)$(sssddatadir) \ $(DESTDIR)$(dbpath) \ + $(DESTDIR)$(mcpath) \ $(DESTDIR)$(pidpath) \ $(DESTDIR)$(logpath) \ $(DESTDIR)$(pubconfpath) \ diff --git a/configure.ac b/configure.ac index 70830580d..4e969aa53 100644 --- a/configure.ac +++ b/configure.ac @@ -81,6 +81,7 @@ WITH_PID_PATH WITH_LOG_PATH WITH_PUBCONF_PATH WITH_PIPE_PATH +WITH_MCACHE_PATH WITH_INIT_DIR WITH_TEST_DIR WITH_MANPAGES diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in index 583ccbff4..40e68a6ad 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -54,6 +54,7 @@ Requires(postun): initscripts chkconfig /sbin/ldconfig %global sssdstatedir %{_localstatedir}/lib/sss %global dbpath %{sssdstatedir}/db %global pipepath %{sssdstatedir}/pipes +%global mcpath %{sssdstatedir}/mc %global pubconfpath %{sssdstatedir}/pubconf ### Build Dependencies ### @@ -202,6 +203,7 @@ autoreconf -ivf %configure \ --with-db-path=%{dbpath} \ + --with-mcache-path=%(mcpath) \ --with-pipe-path=%{pipepath} \ --with-pubconf-path=%{pubconfpath} \ --with-init-dir=%{_initrddir} \ @@ -315,6 +317,7 @@ rm -rf $RPM_BUILD_ROOT %dir %{sssdstatedir} %dir %{_localstatedir}/cache/krb5rcache %attr(700,root,root) %dir %{dbpath} +%attr(755,root,root) %dir %{mcpath} %attr(755,root,root) %dir %{pipepath} %attr(755,root,root) %dir %{pubconfpath} %attr(700,root,root) %dir %{pipepath}/private diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 index 145cdd795..8c13e5dba 100644 --- a/src/conf_macros.m4 +++ b/src/conf_macros.m4 @@ -111,6 +111,23 @@ AC_DEFUN([WITH_PIPE_PATH], AC_DEFINE_UNQUOTED(PIPE_PATH, "$config_pipepath", [Where to store pipe files for the SSSD interconnects]) ]) +AC_DEFUN([WITH_MCACHE_PATH], + [ AC_ARG_WITH([mcache-path], + [AC_HELP_STRING([--with-mcache-path=PATH], + [Where to store mmap cache files for the SSSD interconnects [/var/lib/sss/mc]] + ) + ] + ) + config_mcpath="\"VARDIR\"/lib/sss/mc" + mcpath="${localstatedir}/lib/sss/mc" + if test x"$with_mcache_path" != x; then + config_mcpath=$with_mcache_path + mcpath=$with_mcache_path + fi + AC_SUBST(mcpath) + AC_DEFINE_UNQUOTED(MCACHE_PATH, "$config_mcpath", [Where to store mmap cache files for the SSSD interconnects]) + ]) + AC_DEFUN([WITH_INITSCRIPT], [ AC_ARG_WITH([initscript], [AC_HELP_STRING([--with-initscript=INITSCRIPT_TYPE], diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c index bff8e3cd7..5ba91f3c3 100644 --- a/src/responder/nss/nsssrv.c +++ b/src/responder/nss/nsssrv.c @@ -33,6 +33,7 @@ #include "popt.h" #include "util/util.h" #include "responder/nss/nsssrv.h" +#include "responder/nss/nsssrv_mmap_cache.h" #include "responder/common/negcache.h" #include "db/sysdb.h" #include "confdb/confdb.h" @@ -309,6 +310,15 @@ int nss_process_init(TALLOC_CTX *mem_ctx, return EIO; } + /* create mmap caches */ + /* TODO: read cache sizes from configuration */ + ret = sss_mmap_cache_init(nctx, "passwd", SSS_MC_PASSWD, + 50000, + &nctx->pwd_mc_ctx); + if (ret) { + DEBUG(SSSDBG_CRIT_FAILURE, ("mmap cache is DISABLED")); + } + /* Set up file descriptor limits */ ret = confdb_get_int(nctx->rctx->cdb, CONFDB_NSS_CONF_ENTRY, diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h index 8fc0809aa..ba52f5827 100644 --- a/src/responder/nss/nsssrv.h +++ b/src/responder/nss/nsssrv.h @@ -39,6 +39,7 @@ #define NSS_PACKET_MAX_RECV_SIZE 1024 struct getent_ctx; +struct sss_mc_ctx; struct nss_ctx { struct resp_ctx *rctx; @@ -64,6 +65,8 @@ struct nss_ctx { char **vetoed_shells; char **etc_shells; char *shell_fallback; + + struct sss_mc_ctx *pwd_mc_ctx; }; struct nss_packet; diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c new file mode 100644 index 000000000..65939f4cd --- /dev/null +++ b/src/responder/nss/nsssrv_mmap_cache.c @@ -0,0 +1,300 @@ +/* + SSSD + + NSS Responder - Mmap Cache + + Copyright (C) Simo Sorce 2011 + + 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 . +*/ + +#include "util/util.h" +#include "confdb/confdb.h" +#include +#include +#include "util/mmap_cache.h" +#include "responder/nss/nsssrv.h" +#include "responder/nss/nsssrv_mmap_cache.h" + +/* arbitrary (avg of my /etc/passwd) */ +#define SSS_AVG_PASSWD_PAYLOAD (MC_SLOT_SIZE * 4) +/* short group name and no gids (private user group */ +#define SSS_AVG_GROUP_PAYLOAD (MC_SLOT_SIZE * 3) + +#define MC_NEXT_BARRIER(val) ((((val) + 1) & 0x00ffffff) | 0xf0000000) + +#define MC_RAISE_BARRIER(m) do { \ + m->b2 = MC_NEXT_BARRIER(m->b1); \ + __sync_synchronize(); \ +} while (0) + +#define MC_LOWER_BARRIER(m) do { \ + __sync_synchronize(); \ + m->b1 = m->b2; \ +} while (0) + +struct sss_mc_ctx { + char *name; /* mmap cache name */ + enum sss_mc_type type; /* mmap cache type */ + char *file; /* mmap cache file name */ + int fd; /* file descriptor */ + + uint32_t seed; /* pseudo-random seed to avoid collision attacks */ + time_t valid_time_slot; /* maximum time the entry is valid in seconds */ + + void *mmap_base; /* base address of mmap */ + size_t mmap_size; /* total size of mmap */ + + uint32_t *hash_table; /* hash table address (in mmap) */ + uint32_t ht_size; /* size of hash table */ + + uint8_t *free_table; /* free list bitmaps */ + uint32_t ft_size; /* size of free table */ + uint32_t next_slot; /* the next slot after last allocation */ + + uint8_t *data_table; /* data table address (in mmap) */ + uint32_t dt_size; /* size of data table */ +}; + + +/*************************************************************************** + * initialization + ***************************************************************************/ + +static errno_t sss_mc_set_recycled(int fd) +{ + uint32_t w = SSS_MC_HEADER_RECYCLED; + struct sss_mc_header h; + off_t offset; + off_t pos; + int ret; + + offset = MC_PTR_DIFF(&h.status, &h); + + pos = lseek(fd, offset, SEEK_SET); + if (pos == -1) { + /* What do we do now ? */ + return errno; + } + pos = 0; + while (pos < sizeof(h.status)) { + ret = write(fd, ((uint8_t *)&w) + pos, sizeof(h.status) - pos); + if (ret == -1) { + if (errno != EINTR) { + return errno; + } + continue; + } + pos += ret; + } + + return EOK; +} + +/* + * When we (re)create a new file we must mark the current file as recycled + * so active clients will abandon its use asap. + * We unlink the current file and make a new one + */ +static errno_t sss_mc_create_file(struct sss_mc_ctx *mc_ctx) +{ + mode_t old_mask; + int ofd; + int ret; + + ofd = open(mc_ctx->file, O_RDWR); + if (ofd != -1) { + ret = sss_mc_set_recycled(ofd); + if (ret) { + DEBUG(SSSDBG_TRACE_FUNC, ("Failed to mark mmap file %s as" + " recycled: %d(%s)\n", + mc_ctx->file, ret, strerror(ret))); + } + + close(ofd); + } + + ret = unlink(mc_ctx->file); + if (ret == -1) { + DEBUG(SSSDBG_TRACE_FUNC, ("Failed to rm mmap file %s: %d(%s)\n", + mc_ctx->file, ret, strerror(ret))); + } + + /* temporarily relax umask as we need the file to be readable + * by everyone for now */ + old_mask = umask(0022); + + ret = 0; + mc_ctx->fd = open(mc_ctx->file, O_CREAT | O_EXCL | O_RDWR, 0644); + if (mc_ctx->fd == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to open mmap file %s: %d(%s)\n", + mc_ctx->file, ret, strerror(ret))); + } + + /* reset mask back */ + umask(old_mask); + + return ret; +} + +static void sss_mc_header_update(struct sss_mc_ctx *mc_ctx, int status) +{ + struct sss_mc_header *h; + + /* update header using barriers */ + h = (struct sss_mc_header *)mc_ctx->mmap_base; + MC_RAISE_BARRIER(h); + if (status != SSS_MC_HEADER_RECYCLED) { + /* no reason to update anything else if the file is recycled */ + h->hash_table = MC_PTR_DIFF(mc_ctx->hash_table, mc_ctx->mmap_base); + h->free_table = MC_PTR_DIFF(mc_ctx->free_table, mc_ctx->mmap_base); + h->data_table = MC_PTR_DIFF(mc_ctx->data_table, mc_ctx->mmap_base); + h->ht_size = mc_ctx->ht_size; + h->ft_size = mc_ctx->ft_size; + h->dt_size = mc_ctx->dt_size; + h->major_vno = SSS_MC_MAJOR_VNO; + h->minor_vno = SSS_MC_MINOR_VNO; + h->seed = mc_ctx->seed; + h->reserved = 0; + } + h->status = status; + MC_LOWER_BARRIER(h); +} + +errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name, + enum sss_mc_type type, size_t n_elem, + struct sss_mc_ctx **mcc) +{ + struct sss_mc_ctx *mc_ctx = NULL; + unsigned int rseed; + int payload; + int ret; + + switch (type) { + case SSS_MC_PASSWD: + payload = SSS_AVG_PASSWD_PAYLOAD; + break; + case SSS_MC_GROUP: + payload = SSS_AVG_GROUP_PAYLOAD; + break; + default: + return EINVAL; + } + + mc_ctx = talloc_zero(mem_ctx, struct sss_mc_ctx); + if (!mc_ctx) { + return ENOMEM; + } + mc_ctx->fd = -1; + + mc_ctx->name = talloc_strdup(mem_ctx, name); + if (!mc_ctx->name) { + ret = ENOMEM; + goto done; + } + + mc_ctx->type = type; + + mc_ctx->valid_time_slot = 300; /* 5 min. FIXME: parametrize */ + + mc_ctx->file = talloc_asprintf(mc_ctx, "%s/%s", + SSS_NSS_MCACHE_DIR, name); + if (!mc_ctx->file) { + ret = ENOMEM; + goto done; + } + + /* elements must always be multiple of 8 to make things easier to handle, + * so we increase by the necessary amount if they are not a multiple */ + /* We can use MC_ALIGN64 for this */ + n_elem = MC_ALIGN64(n_elem); + + /* hash table is double the size because it will store both forward and + * reverse keys (name/uid, name/gid, ..) */ + mc_ctx->ht_size = MC_HT_SIZE(n_elem * 2); + mc_ctx->dt_size = MC_DT_SIZE(n_elem, payload); + mc_ctx->ft_size = MC_FT_SIZE(n_elem); + mc_ctx->mmap_size = MC_HEADER_SIZE + + MC_ALIGN64(mc_ctx->dt_size) + + MC_ALIGN64(mc_ctx->ft_size) + + MC_ALIGN64(mc_ctx->ht_size); + + + /* for now ALWAYS create a new file on restart */ + + ret = sss_mc_create_file(mc_ctx); + if (ret) { + goto done; + } + + ret = ftruncate(mc_ctx->fd, mc_ctx->mmap_size); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to resize file %s: %d(%s)\n", + mc_ctx->file, ret, strerror(ret))); + goto done; + } + + mc_ctx->mmap_base = mmap(NULL, mc_ctx->mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, mc_ctx->fd, 0); + if (mc_ctx->mmap_base == MAP_FAILED) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to mmap file %s(%ld): %d(%s)\n", + mc_ctx->file, mc_ctx->mmap_size, + ret, strerror(ret))); + goto done; + } + + mc_ctx->data_table = MC_PTR_ADD(mc_ctx->mmap_base, MC_HEADER_SIZE); + mc_ctx->free_table = MC_PTR_ADD(mc_ctx->data_table, + MC_ALIGN64(mc_ctx->dt_size)); + mc_ctx->hash_table = MC_PTR_ADD(mc_ctx->free_table, + MC_ALIGN64(mc_ctx->ft_size)); + + memset(mc_ctx->data_table, 0x00, mc_ctx->dt_size); + memset(mc_ctx->free_table, 0x00, mc_ctx->ft_size); + memset(mc_ctx->hash_table, 0xff, mc_ctx->ht_size); + + /* generate a pseudo-random seed. + * Needed to fend off dictionary based collision attacks */ + rseed = time(NULL) * getpid(); + mc_ctx->seed = rand_r(&rseed); + + sss_mc_header_update(mc_ctx, SSS_MC_HEADER_ALIVE); + + ret = EOK; + +done: + if (ret) { + if (mc_ctx && mc_ctx->mmap_base) { + munmap(mc_ctx->mmap_base, mc_ctx->mmap_size); + } + if (mc_ctx && mc_ctx->fd != -1) { + close(mc_ctx->fd); + ret = unlink(mc_ctx->file); + if (ret == -1) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to rm mmap file %s: %d(%s)\n", + mc_ctx->file, ret, strerror(ret))); + } + } + + talloc_free(mc_ctx); + } else { + *mcc = mc_ctx; + } + return ret; +} + diff --git a/src/responder/nss/nsssrv_mmap_cache.h b/src/responder/nss/nsssrv_mmap_cache.h new file mode 100644 index 000000000..6f49b8b76 --- /dev/null +++ b/src/responder/nss/nsssrv_mmap_cache.h @@ -0,0 +1,37 @@ +/* + SSSD + + NSS Responder - Mmap Cache + + Copyright (C) Simo Sorce 2011 + + 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 . +*/ + +#ifndef _NSSSRV_MMAP_CACHE_H_ +#define _NSSSRV_MMAP_CACHE_H_ + +struct sss_mc_ctx; + +enum sss_mc_type { + SSS_MC_NONE = 0, + SSS_MC_PASSWD, + SSS_MC_GROUP, +}; + +errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name, + enum sss_mc_type type, size_t n_elem, + struct sss_mc_ctx **mcc); + +#endif /* _NSSSRV_MMAP_CACHE_H_ */ -- cgit