From 5f216c753dbd2f2b25a011c5f705ee4f8ad924e6 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 2 Jan 2012 15:33:16 -0500 Subject: sss_client: Add common shared memory cache utils --- src/sss_client/nss_mc.h | 62 ++++++++++ src/sss_client/nss_mc_common.c | 272 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 src/sss_client/nss_mc.h create mode 100644 src/sss_client/nss_mc_common.c (limited to 'src/sss_client') diff --git a/src/sss_client/nss_mc.h b/src/sss_client/nss_mc.h new file mode 100644 index 000000000..35cefe5b6 --- /dev/null +++ b/src/sss_client/nss_mc.h @@ -0,0 +1,62 @@ +/* + * System Security Services Daemon. NSS client interface + * + * Copyright (C) Simo Sorce 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* NSS interfaces to mmap cache */ + +#ifndef _NSS_MC_H_ +#define _NSS_MC_H_ + +#include +#include +#include +#include "util/mmap_cache.h" + +#ifndef HAVE_ERRNO_T +#define HAVE_ERRNO_T +typedef int errno_t; +#endif + +/* common stuff */ +struct sss_cli_mc_ctx { + bool initialized; + int fd; + + uint32_t seed; /* seed from the tables header */ + + void *mmap_base; /* base address of mmap */ + size_t mmap_size; /* total size of mmap */ + + uint8_t *data_table; /* data table address (in mmap) */ + uint32_t dt_size; /* size of data table */ + + uint32_t *hash_table; /* hash table address (in mmap) */ + uint32_t ht_size; /* size of hash table */ +}; + +errno_t sss_nss_mc_get_ctx(const char *name, struct sss_cli_mc_ctx *ctx); +errno_t sss_nss_check_header(struct sss_cli_mc_ctx *ctx); +uint32_t sss_nss_mc_hash(struct sss_cli_mc_ctx *ctx, + const char *key, size_t len); +errno_t sss_nss_mc_get_record(struct sss_cli_mc_ctx *ctx, + uint32_t slot, struct sss_mc_rec **_rec); +errno_t sss_nss_str_ptr_from_buffer(char **str, void **cookie, + char *buf, size_t len); + +#endif /* _NSS_MC_H_ */ diff --git a/src/sss_client/nss_mc_common.c b/src/sss_client/nss_mc_common.c new file mode 100644 index 000000000..5391df979 --- /dev/null +++ b/src/sss_client/nss_mc_common.c @@ -0,0 +1,272 @@ +/* + * System Security Services Daemon. NSS client interface + * + * Copyright (C) Simo Sorce 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* NSS interfaces to mmap cache */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nss_mc.h" + +/* FIXME: hook up to library destructor to avoid leaks */ +/* FIXME: temporarily open passwd file on our own, later we will probably + * use socket passing from the main process */ +/* FIXME: handle name upper/lower casing ? Maybe a flag passed down by + * sssd or a flag in sss_mc_header ? per domain ? */ + +errno_t sss_nss_check_header(struct sss_cli_mc_ctx *ctx) +{ + struct sss_mc_header h; + int count; + + /* retry barrier protected reading max 5 times then give up */ + for (count = 5; count > 0; count--) { + memcpy(&h, ctx->mmap_base, sizeof(struct sss_mc_header)); + if (MC_VALID_BARRIER(h.b1) && h.b1 == h.b2) { + /* record is consistent so we can proceed */ + break; + } + } + if (count == 0) { + /* couldn't successfully read header we have to give up */ + return EIO; + } + + if (h.major_vno != SSS_MC_MAJOR_VNO || + h.minor_vno != SSS_MC_MINOR_VNO || + h.status == SSS_MC_HEADER_RECYCLED) { + return EINVAL; + } + + /* first time we check the header, let's fill our own struct */ + if (ctx->data_table == NULL) { + ctx->seed = h.seed; + ctx->data_table = MC_PTR_ADD(ctx->mmap_base, h.data_table); + ctx->hash_table = MC_PTR_ADD(ctx->mmap_base, h.hash_table); + ctx->dt_size = h.dt_size; + ctx->ht_size = h.ht_size; + } else { + if (ctx->seed != h.seed || + ctx->data_table != MC_PTR_ADD(ctx->mmap_base, h.data_table) || + ctx->hash_table != MC_PTR_ADD(ctx->mmap_base, h.hash_table) || + ctx->dt_size != h.dt_size || + ctx->ht_size != h.ht_size) { + return EINVAL; + } + } + + return 0; +} + +errno_t sss_nss_mc_get_ctx(const char *name, struct sss_cli_mc_ctx *ctx) +{ + struct stat fdstat; + char *file = NULL; + char *envval; + int ret; + + envval = getenv("_SSS_MC_SPECIAL"); + if (envval && strcmp(envval, "NO") == 0) { + return EPERM; + } + + if (ctx->initialized) { + ret = sss_nss_check_header(ctx); + goto done; + } + + ret = asprintf(&file, "%s/%s", SSS_NSS_MCACHE_DIR, name); + if (ret == -1) { + ret = ENOMEM; + goto done; + } + + ctx->fd = open(file, O_RDONLY); + if (ctx->fd == -1) { + ret = EIO; + goto done; + } + + ret = fstat(ctx->fd, &fdstat); + if (ret == -1) { + ret = EIO; + goto done; + } + + if (fdstat.st_size < MC_HEADER_SIZE) { + ret = ENOMEM; + goto done; + } + ctx->mmap_size = fdstat.st_size; + + ctx->mmap_base = mmap(NULL, ctx->mmap_size, + PROT_READ, MAP_SHARED, ctx->fd, 0); + if (ctx->mmap_base == MAP_FAILED) { + ret = ENOMEM; + goto done; + } + + ret = sss_nss_check_header(ctx); + if (ret != 0) { + goto done; + } + + ctx->initialized = true; + + ret = 0; + +done: + if (ret) { + if ((ctx->mmap_base != NULL) && (ctx->mmap_size != 0)) { + munmap(ctx->mmap_base, ctx->mmap_size); + } + if (ctx->fd != -1) { + close(ctx->fd); + } + memset(ctx, 0, sizeof(struct sss_cli_mc_ctx)); + } + free(file); + return ret; +} + +uint32_t sss_nss_mc_hash(struct sss_cli_mc_ctx *ctx, + const char *key, size_t len) +{ + return murmurhash3(key, len, ctx->seed) % MC_HT_ELEMS(ctx->ht_size); +} + +errno_t sss_nss_mc_get_record(struct sss_cli_mc_ctx *ctx, + uint32_t slot, struct sss_mc_rec **_rec) +{ + struct sss_mc_rec *rec; + void *rec_buf = NULL; + size_t buf_size = 0; + size_t rec_len; + uint32_t b1; + uint32_t b2; + int count; + int ret; + + /* try max 5 times */ + for (count = 5; count > 0; count--) { + rec = MC_SLOT_TO_PTR(ctx->data_table, slot, struct sss_mc_rec); + + /* fetch record length */ + b1 = rec->b1; + __sync_synchronize(); + rec_len = rec->len; + __sync_synchronize(); + b2 = rec->b1; + if (!MC_VALID_BARRIER(b1) || b1 != b2) { + /* record is inconsistent, retry */ + continue; + } + + if (rec_len > buf_size) { + free(rec_buf); + rec_buf = malloc(rec_len); + if (!rec_buf) { + ret = ENOMEM; + goto done; + } + buf_size = rec_len; + } + /* we cannot access data directly, we must copy data and then + * access the copy */ + memcpy(rec_buf, rec, rec_len); + rec = (struct sss_mc_rec *)rec_buf; + + /* we must check data is consistent again after the copy */ + if (MC_VALID_BARRIER(rec->b1) && + rec->b1 == rec->b2 && + rec->len == rec_len) { + /* record is consistent, use it */ + break; + } + } + if (count == 0) { + /* couldn't successfully read header we have to give up */ + ret = EIO; + goto done; + } + + *_rec = rec; + ret = 0; + +done: + if (ret) { + free(rec_buf); + *_rec = NULL; + } + return ret; +} + +/* + * returns strings froma a buffer. + * + * Call first time with *cookie set to null, then call again + * with the returned cookie. + * On the last string the cookie will be reset to null and + * all strings will have been returned. + * In case the last string is not zero terminated EINVAL is returned. + */ +errno_t sss_nss_str_ptr_from_buffer(char **str, void **cookie, + char *buf, size_t len) +{ + char *max = buf + len; + char *ret; + char *p; + + if (*cookie == NULL) { + p = buf; + } else { + p = *((char **)cookie); + } + + ret = p; + + while (p < max) { + if (*p == '\0') { + break; + } + p++; + } + if (p >= max) { + return EINVAL; + } + p++; + if (p == max) { + *cookie = NULL; + } else { + *cookie = p; + } + + *str = ret; + return 0; +} + -- cgit