LCOV - code coverage report
Current view: top level - sss_client - nss_mc_common.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 38 164 23.2 %
Date: 2016-06-29 Functions: 3 8 37.5 %

          Line data    Source code
       1             : /*
       2             :  * System Security Services Daemon. NSS client interface
       3             :  *
       4             :  * Copyright (C) Simo Sorce 2011
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU Lesser General Public License as
       8             :  * published by the Free Software Foundation; either version 2.1 of the
       9             :  * License, or (at your option) any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public License
      17             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : /* NSS interfaces to mmap cache */
      21             : 
      22             : #include "config.h"
      23             : 
      24             : #include <stdio.h>
      25             : #include <errno.h>
      26             : #include <sys/types.h>
      27             : #include <sys/stat.h>
      28             : #include <fcntl.h>
      29             : #include <unistd.h>
      30             : #include <sys/mman.h>
      31             : #include <string.h>
      32             : #include <stdlib.h>
      33             : #include "nss_mc.h"
      34             : #include "sss_cli.h"
      35             : #include "util/io.h"
      36             : 
      37             : /* FIXME: hook up to library destructor to avoid leaks */
      38             : /* FIXME: temporarily open passwd file on our own, later we will probably
      39             :  * use socket passing from the main process */
      40             : /* FIXME: handle name upper/lower casing ? Maybe a flag passed down by
      41             :  * sssd or a flag in sss_mc_header ? per domain ? */
      42             : 
      43             : #define MEMCPY_WITH_BARRIERS(res, dest, src, len) \
      44             : do { \
      45             :     uint32_t _b1; \
      46             :     res = false; \
      47             :     _b1 = (src)->b1; \
      48             :     if (MC_VALID_BARRIER(_b1)) { \
      49             :         __sync_synchronize(); \
      50             :         memcpy(dest, src, len); \
      51             :         __sync_synchronize(); \
      52             :         if ((src)->b2 == _b1) { \
      53             :             res = true; \
      54             :         } \
      55             :     } \
      56             : } while(0)
      57             : 
      58           0 : errno_t sss_nss_check_header(struct sss_cli_mc_ctx *ctx)
      59             : {
      60             :     struct sss_mc_header h;
      61             :     bool copy_ok;
      62             :     int count;
      63             :     int ret;
      64             :     struct stat fdstat;
      65             : 
      66             :     /* retry barrier protected reading max 5 times then give up */
      67           0 :     for (count = 5; count > 0; count--) {
      68           0 :         MEMCPY_WITH_BARRIERS(copy_ok, &h,
      69             :                              (struct sss_mc_header *)ctx->mmap_base,
      70             :                              sizeof(struct sss_mc_header));
      71           0 :         if (copy_ok) {
      72             :             /* record is consistent so we can proceed */
      73           0 :             break;
      74             :         }
      75             :     }
      76           0 :     if (count == 0) {
      77             :         /* couldn't successfully read header we have to give up */
      78           0 :         return EIO;
      79             :     }
      80             : 
      81           0 :     if (h.major_vno != SSS_MC_MAJOR_VNO ||
      82           0 :         h.minor_vno != SSS_MC_MINOR_VNO ||
      83           0 :         h.status == SSS_MC_HEADER_RECYCLED) {
      84           0 :         return EINVAL;
      85             :     }
      86             : 
      87             :     /* first time we check the header, let's fill our own struct */
      88           0 :     if (ctx->data_table == NULL) {
      89           0 :         ctx->seed = h.seed;
      90           0 :         ctx->data_table = MC_PTR_ADD(ctx->mmap_base, h.data_table);
      91           0 :         ctx->hash_table = MC_PTR_ADD(ctx->mmap_base, h.hash_table);
      92           0 :         ctx->dt_size = h.dt_size;
      93           0 :         ctx->ht_size = h.ht_size;
      94             :     } else {
      95           0 :         if (ctx->seed != h.seed ||
      96           0 :             ctx->data_table != MC_PTR_ADD(ctx->mmap_base, h.data_table) ||
      97           0 :             ctx->hash_table != MC_PTR_ADD(ctx->mmap_base, h.hash_table) ||
      98           0 :             ctx->dt_size != h.dt_size ||
      99           0 :             ctx->ht_size != h.ht_size) {
     100           0 :             return EINVAL;
     101             :         }
     102             :     }
     103             : 
     104           0 :     ret = fstat(ctx->fd, &fdstat);
     105           0 :     if (ret == -1) {
     106           0 :         return EIO;
     107             :     }
     108             : 
     109           0 :     if (fdstat.st_nlink == 0) {
     110             :         /* memory cache was removed; we need to reinitialize it. */
     111           0 :         return EINVAL;
     112             :     }
     113             : 
     114           0 :     return 0;
     115             : }
     116             : 
     117         130 : static void sss_nss_mc_destroy_ctx(struct sss_cli_mc_ctx *ctx)
     118             : {
     119         130 :     uint32_t active_threads = ctx->active_threads;
     120             : 
     121         130 :     if ((ctx->mmap_base != NULL) && (ctx->mmap_size != 0)) {
     122           0 :         munmap(ctx->mmap_base, ctx->mmap_size);
     123             :     }
     124         130 :     if (ctx->fd != -1) {
     125           0 :         close(ctx->fd);
     126             :     }
     127         130 :     memset(ctx, 0, sizeof(struct sss_cli_mc_ctx));
     128         130 :     ctx->fd = -1;
     129             : 
     130             :     /* restore count of active threads */
     131         130 :     ctx->active_threads = active_threads;
     132         130 : }
     133             : 
     134         130 : static errno_t sss_nss_mc_init_ctx(const char *name,
     135             :                                    struct sss_cli_mc_ctx *ctx)
     136             : {
     137             :     struct stat fdstat;
     138         130 :     char *file = NULL;
     139             :     int ret;
     140             : 
     141         130 :     sss_nss_mc_lock();
     142             :     /* check if ctx is initialised by previous thread. */
     143         130 :     if (ctx->initialized != UNINITIALIZED) {
     144           0 :         ret = sss_nss_check_header(ctx);
     145           0 :         goto done;
     146             :     }
     147             : 
     148         130 :     ret = asprintf(&file, "%s/%s", SSS_NSS_MCACHE_DIR, name);
     149         130 :     if (ret == -1) {
     150           0 :         ret = ENOMEM;
     151           0 :         goto done;
     152             :     }
     153             : 
     154         130 :     ctx->fd = sss_open_cloexec(file, O_RDONLY, &ret);
     155         130 :     if (ctx->fd == -1) {
     156         130 :         goto done;
     157             :     }
     158             : 
     159           0 :     ret = fstat(ctx->fd, &fdstat);
     160           0 :     if (ret == -1) {
     161           0 :         ret = EIO;
     162           0 :         goto done;
     163             :     }
     164             : 
     165           0 :     if (fdstat.st_size < MC_HEADER_SIZE) {
     166           0 :         ret = ENOMEM;
     167           0 :         goto done;
     168             :     }
     169           0 :     ctx->mmap_size = fdstat.st_size;
     170             : 
     171           0 :     ctx->mmap_base = mmap(NULL, ctx->mmap_size,
     172             :                           PROT_READ, MAP_SHARED, ctx->fd, 0);
     173           0 :     if (ctx->mmap_base == MAP_FAILED) {
     174           0 :         ret = ENOMEM;
     175           0 :         goto done;
     176             :     }
     177             : 
     178           0 :     ret = sss_nss_check_header(ctx);
     179           0 :     if (ret != 0) {
     180           0 :         goto done;
     181             :     }
     182             : 
     183           0 :     ctx->initialized = INITIALIZED;
     184             : 
     185           0 :     ret = 0;
     186             : 
     187             : done:
     188         130 :     if (ret) {
     189         130 :         sss_nss_mc_destroy_ctx(ctx);
     190             :     }
     191         130 :     free(file);
     192         130 :     sss_nss_mc_unlock();
     193             : 
     194         130 :     return ret;
     195             : }
     196             : 
     197         130 : errno_t sss_nss_mc_get_ctx(const char *name, struct sss_cli_mc_ctx *ctx)
     198             : {
     199             :     char *envval;
     200             :     int ret;
     201         130 :     bool need_decrement = false;
     202             : 
     203         130 :     envval = getenv("SSS_NSS_USE_MEMCACHE");
     204         130 :     if (envval && strcasecmp(envval, "NO") == 0) {
     205           0 :         return EPERM;
     206             :     }
     207             : 
     208         130 :     switch (ctx->initialized) {
     209             :     case UNINITIALIZED:
     210         130 :         __sync_add_and_fetch(&ctx->active_threads, 1);
     211         130 :         ret = sss_nss_mc_init_ctx(name, ctx);
     212         130 :         if (ret) {
     213         130 :             need_decrement = true;
     214             :         }
     215         130 :         break;
     216             :     case INITIALIZED:
     217           0 :         __sync_add_and_fetch(&ctx->active_threads, 1);
     218           0 :         ret = sss_nss_check_header(ctx);
     219           0 :         if (ret) {
     220           0 :             need_decrement = true;
     221             :         }
     222           0 :         break;
     223             :     case RECYCLED:
     224             :         /* we need to safely destroy memory cache */
     225           0 :         ret = EAGAIN;
     226           0 :         break;
     227             :     default:
     228           0 :         ret = EFAULT;
     229             :     }
     230             : 
     231         130 :     if (ret) {
     232         130 :         if (ctx->initialized == INITIALIZED) {
     233           0 :             ctx->initialized = RECYCLED;
     234             :         }
     235         130 :         if (ctx->initialized == RECYCLED && ctx->active_threads == 0) {
     236             :             /* just one thread should call munmap */
     237           0 :             sss_nss_mc_lock();
     238           0 :             if (ctx->initialized == RECYCLED) {
     239           0 :                 sss_nss_mc_destroy_ctx(ctx);
     240             :             }
     241           0 :             sss_nss_mc_unlock();
     242             :         }
     243         130 :         if (need_decrement) {
     244             :             /* In case of error, we will not touch mmapped area => decrement */
     245         130 :             __sync_sub_and_fetch(&ctx->active_threads, 1);
     246             :         }
     247             :     }
     248         130 :     return ret;
     249             : }
     250             : 
     251           0 : uint32_t sss_nss_mc_hash(struct sss_cli_mc_ctx *ctx,
     252             :                          const char *key, size_t len)
     253             : {
     254           0 :     return murmurhash3(key, len, ctx->seed) % MC_HT_ELEMS(ctx->ht_size);
     255             : }
     256             : 
     257           0 : errno_t sss_nss_mc_get_record(struct sss_cli_mc_ctx *ctx,
     258             :                               uint32_t slot, struct sss_mc_rec **_rec)
     259             : {
     260             :     struct sss_mc_rec *rec;
     261           0 :     struct sss_mc_rec *copy_rec = NULL;
     262           0 :     size_t buf_size = 0;
     263             :     size_t rec_len;
     264             :     uint32_t b1;
     265             :     uint32_t b2;
     266             :     bool copy_ok;
     267             :     int count;
     268             :     int ret;
     269             : 
     270             :     /* try max 5 times */
     271           0 :     for (count = 5; count > 0; count--) {
     272           0 :         rec = MC_SLOT_TO_PTR(ctx->data_table, slot, struct sss_mc_rec);
     273             : 
     274             :         /* fetch record length */
     275           0 :         b1 = rec->b1;
     276           0 :         __sync_synchronize();
     277           0 :         rec_len = rec->len;
     278           0 :         __sync_synchronize();
     279           0 :         b2 = rec->b2;
     280           0 :         if (!MC_VALID_BARRIER(b1) || b1 != b2) {
     281             :             /* record is inconsistent, retry */
     282           0 :             continue;
     283             :         }
     284             : 
     285           0 :         if (!MC_CHECK_RECORD_LENGTH(ctx, rec)) {
     286             :             /* record has invalid length */
     287           0 :             free(copy_rec);
     288           0 :             return EINVAL;
     289             :         }
     290             : 
     291           0 :         if (rec_len > buf_size) {
     292           0 :             free(copy_rec);
     293           0 :             copy_rec = malloc(rec_len);
     294           0 :             if (!copy_rec) {
     295           0 :                 ret = ENOMEM;
     296           0 :                 goto done;
     297             :             }
     298           0 :             buf_size = rec_len;
     299             :         }
     300             :         /* we cannot access data directly, we must copy data and then
     301             :          * access the copy */
     302           0 :         MEMCPY_WITH_BARRIERS(copy_ok, copy_rec, rec, rec_len);
     303             : 
     304             :         /* we must check data is consistent again after the copy */
     305           0 :         if (copy_ok && b1 == copy_rec->b2) {
     306             :             /* record is consistent, use it */
     307           0 :             break;
     308             :         }
     309             :     }
     310           0 :     if (count == 0) {
     311             :         /* couldn't successfully read header we have to give up */
     312           0 :         ret = EIO;
     313           0 :         goto done;
     314             :     }
     315             : 
     316           0 :     *_rec = copy_rec;
     317           0 :     ret = 0;
     318             : 
     319             : done:
     320           0 :     if (ret) {
     321           0 :         free(copy_rec);
     322           0 :         *_rec = NULL;
     323             :     }
     324           0 :     return ret;
     325             : }
     326             : 
     327             : /*
     328             :  * returns strings froma a buffer.
     329             :  *
     330             :  * Call first time with *cookie set to null, then call again
     331             :  * with the returned cookie.
     332             :  * On the last string the cookie will be reset to null and
     333             :  * all strings will have been returned.
     334             :  * In case the last string is not zero terminated EINVAL is returned.
     335             :  */
     336           0 : errno_t sss_nss_str_ptr_from_buffer(char **str, void **cookie,
     337             :                                     char *buf, size_t len)
     338             : {
     339           0 :     char *max = buf + len;
     340             :     char *ret;
     341             :     char *p;
     342             : 
     343           0 :     if (*cookie == NULL) {
     344           0 :         p = buf;
     345             :     } else {
     346           0 :         p = *((char **)cookie);
     347             :     }
     348             : 
     349           0 :     ret = p;
     350             : 
     351           0 :     while (p < max) {
     352           0 :         if (*p == '\0') {
     353           0 :             break;
     354             :         }
     355           0 :         p++;
     356             :     }
     357           0 :     if (p >= max) {
     358           0 :         return EINVAL;
     359             :     }
     360           0 :     p++;
     361           0 :     if (p == max) {
     362           0 :         *cookie = NULL;
     363             :     } else {
     364           0 :         *cookie = p;
     365             :     }
     366             : 
     367           0 :     *str = ret;
     368           0 :     return 0;
     369             : }
     370             : 
     371           0 : uint32_t sss_nss_mc_next_slot_with_hash(struct sss_mc_rec *rec,
     372             :                                         uint32_t hash)
     373             : {
     374           0 :     if (rec->hash1 == hash) {
     375           0 :         return rec->next1;
     376           0 :     } else if (rec->hash2 == hash) {
     377           0 :         return rec->next2;
     378             :     } else {
     379             :         /* it should never happen. */
     380           0 :         return MC_INVALID_VAL;
     381             :     }
     382             : 
     383             : }

Generated by: LCOV version 1.10