LCOV - code coverage report
Current view: top level - sss_client - nss_mc_common.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 60 157 38.2 %
Date: 2015-10-19 Functions: 4 8 50.0 %

          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           2 : 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             : 
      64             :     /* retry barrier protected reading max 5 times then give up */
      65           2 :     for (count = 5; count > 0; count--) {
      66           2 :         MEMCPY_WITH_BARRIERS(copy_ok, &h,
      67             :                              (struct sss_mc_header *)ctx->mmap_base,
      68             :                              sizeof(struct sss_mc_header));
      69           2 :         if (copy_ok) {
      70             :             /* record is consistent so we can proceed */
      71           2 :             break;
      72             :         }
      73             :     }
      74           2 :     if (count == 0) {
      75             :         /* couldn't successfully read header we have to give up */
      76           0 :         return EIO;
      77             :     }
      78             : 
      79           4 :     if (h.major_vno != SSS_MC_MAJOR_VNO ||
      80           4 :         h.minor_vno != SSS_MC_MINOR_VNO ||
      81           2 :         h.status == SSS_MC_HEADER_RECYCLED) {
      82           0 :         return EINVAL;
      83             :     }
      84             : 
      85             :     /* first time we check the header, let's fill our own struct */
      86           2 :     if (ctx->data_table == NULL) {
      87           1 :         ctx->seed = h.seed;
      88           1 :         ctx->data_table = MC_PTR_ADD(ctx->mmap_base, h.data_table);
      89           1 :         ctx->hash_table = MC_PTR_ADD(ctx->mmap_base, h.hash_table);
      90           1 :         ctx->dt_size = h.dt_size;
      91           1 :         ctx->ht_size = h.ht_size;
      92             :     } else {
      93           2 :         if (ctx->seed != h.seed ||
      94           2 :             ctx->data_table != MC_PTR_ADD(ctx->mmap_base, h.data_table) ||
      95           2 :             ctx->hash_table != MC_PTR_ADD(ctx->mmap_base, h.hash_table) ||
      96           2 :             ctx->dt_size != h.dt_size ||
      97           1 :             ctx->ht_size != h.ht_size) {
      98           0 :             return EINVAL;
      99             :         }
     100             :     }
     101             : 
     102           2 :     return 0;
     103             : }
     104             : 
     105           0 : static void sss_nss_mc_destroy_ctx(struct sss_cli_mc_ctx *ctx)
     106             : {
     107           0 :     if ((ctx->mmap_base != NULL) && (ctx->mmap_size != 0)) {
     108           0 :         munmap(ctx->mmap_base, ctx->mmap_size);
     109             :     }
     110           0 :     if (ctx->fd != -1) {
     111           0 :         close(ctx->fd);
     112             :     }
     113           0 :     memset(ctx, 0, sizeof(struct sss_cli_mc_ctx));
     114           0 :     ctx->fd = -1;
     115           0 : }
     116             : 
     117           1 : static errno_t sss_nss_mc_init_ctx(const char *name,
     118             :                                    struct sss_cli_mc_ctx *ctx)
     119             : {
     120             :     struct stat fdstat;
     121           1 :     char *file = NULL;
     122             :     int ret;
     123             : 
     124           1 :     sss_nss_mc_lock();
     125             :     /* check if ctx is initialised by previous thread. */
     126           1 :     if (ctx->initialized != UNINITIALIZED) {
     127           0 :         ret = sss_nss_check_header(ctx);
     128           0 :         goto done;
     129             :     }
     130             : 
     131           1 :     ret = asprintf(&file, "%s/%s", SSS_NSS_MCACHE_DIR, name);
     132           1 :     if (ret == -1) {
     133           0 :         ret = ENOMEM;
     134           0 :         goto done;
     135             :     }
     136             : 
     137           1 :     ctx->fd = sss_open_cloexec(file, O_RDONLY, &ret);
     138           1 :     if (ctx->fd == -1) {
     139           0 :         goto done;
     140             :     }
     141             : 
     142           1 :     ret = fstat(ctx->fd, &fdstat);
     143           1 :     if (ret == -1) {
     144           0 :         ret = EIO;
     145           0 :         goto done;
     146             :     }
     147             : 
     148           1 :     if (fdstat.st_size < MC_HEADER_SIZE) {
     149           0 :         ret = ENOMEM;
     150           0 :         goto done;
     151             :     }
     152           1 :     ctx->mmap_size = fdstat.st_size;
     153             : 
     154           1 :     ctx->mmap_base = mmap(NULL, ctx->mmap_size,
     155             :                           PROT_READ, MAP_SHARED, ctx->fd, 0);
     156           1 :     if (ctx->mmap_base == MAP_FAILED) {
     157           0 :         ret = ENOMEM;
     158           0 :         goto done;
     159             :     }
     160             : 
     161           1 :     ret = sss_nss_check_header(ctx);
     162           1 :     if (ret != 0) {
     163           0 :         goto done;
     164             :     }
     165             : 
     166           1 :     ctx->initialized = INITIALIZED;
     167             : 
     168           1 :     ret = 0;
     169             : 
     170             : done:
     171           1 :     if (ret) {
     172           0 :         sss_nss_mc_destroy_ctx(ctx);
     173             :     }
     174           1 :     free(file);
     175           1 :     sss_nss_mc_unlock();
     176             : 
     177           1 :     return ret;
     178             : }
     179             : 
     180           2 : errno_t sss_nss_mc_get_ctx(const char *name, struct sss_cli_mc_ctx *ctx)
     181             : {
     182             :     char *envval;
     183             :     int ret;
     184           2 :     bool need_decrement = false;
     185             : 
     186           2 :     envval = getenv("SSS_NSS_USE_MEMCACHE");
     187           2 :     if (envval && strcasecmp(envval, "NO") == 0) {
     188           0 :         return EPERM;
     189             :     }
     190             : 
     191           2 :     switch (ctx->initialized) {
     192             :     case UNINITIALIZED:
     193           1 :         __sync_add_and_fetch(&ctx->active_threads, 1);
     194           1 :         ret = sss_nss_mc_init_ctx(name, ctx);
     195           1 :         if (ret) {
     196           0 :             need_decrement = true;
     197             :         }
     198           1 :         break;
     199             :     case INITIALIZED:
     200           1 :         __sync_add_and_fetch(&ctx->active_threads, 1);
     201           1 :         ret = sss_nss_check_header(ctx);
     202           1 :         if (ret) {
     203           0 :             need_decrement = true;
     204             :         }
     205           1 :         break;
     206             :     case RECYCLED:
     207             :         /* we need to safely destroy memory cache */
     208           0 :         ret = EAGAIN;
     209           0 :         break;
     210             :     default:
     211           0 :         ret = EFAULT;
     212             :     }
     213             : 
     214           2 :     if (ret) {
     215           0 :         if (ctx->initialized == INITIALIZED) {
     216           0 :             ctx->initialized = RECYCLED;
     217             :         }
     218           0 :         if (ctx->initialized == RECYCLED && ctx->active_threads == 0) {
     219             :             /* just one thread should call munmap */
     220           0 :             sss_nss_mc_lock();
     221           0 :             if (ctx->initialized == RECYCLED) {
     222           0 :                 sss_nss_mc_destroy_ctx(ctx);
     223             :             }
     224           0 :             sss_nss_mc_unlock();
     225             :         }
     226           0 :         if (need_decrement) {
     227             :             /* In case of error, we will not touch mmapped area => decrement */
     228           0 :             __sync_sub_and_fetch(&ctx->active_threads, 1);
     229             :         }
     230             :     }
     231           2 :     return ret;
     232             : }
     233             : 
     234           2 : uint32_t sss_nss_mc_hash(struct sss_cli_mc_ctx *ctx,
     235             :                          const char *key, size_t len)
     236             : {
     237           2 :     return murmurhash3(key, len, ctx->seed) % MC_HT_ELEMS(ctx->ht_size);
     238             : }
     239             : 
     240           0 : errno_t sss_nss_mc_get_record(struct sss_cli_mc_ctx *ctx,
     241             :                               uint32_t slot, struct sss_mc_rec **_rec)
     242             : {
     243             :     struct sss_mc_rec *rec;
     244           0 :     struct sss_mc_rec *copy_rec = NULL;
     245           0 :     size_t buf_size = 0;
     246             :     size_t rec_len;
     247             :     uint32_t b1;
     248             :     uint32_t b2;
     249             :     bool copy_ok;
     250             :     int count;
     251             :     int ret;
     252             : 
     253             :     /* try max 5 times */
     254           0 :     for (count = 5; count > 0; count--) {
     255           0 :         rec = MC_SLOT_TO_PTR(ctx->data_table, slot, struct sss_mc_rec);
     256             : 
     257             :         /* fetch record length */
     258           0 :         b1 = rec->b1;
     259           0 :         __sync_synchronize();
     260           0 :         rec_len = rec->len;
     261           0 :         __sync_synchronize();
     262           0 :         b2 = rec->b2;
     263           0 :         if (!MC_VALID_BARRIER(b1) || b1 != b2) {
     264             :             /* record is inconsistent, retry */
     265           0 :             continue;
     266             :         }
     267             : 
     268           0 :         if (!MC_CHECK_RECORD_LENGTH(ctx, rec)) {
     269             :             /* record has invalid length */
     270           0 :             free(copy_rec);
     271           0 :             return EINVAL;
     272             :         }
     273             : 
     274           0 :         if (rec_len > buf_size) {
     275           0 :             free(copy_rec);
     276           0 :             copy_rec = malloc(rec_len);
     277           0 :             if (!copy_rec) {
     278           0 :                 ret = ENOMEM;
     279           0 :                 goto done;
     280             :             }
     281           0 :             buf_size = rec_len;
     282             :         }
     283             :         /* we cannot access data directly, we must copy data and then
     284             :          * access the copy */
     285           0 :         MEMCPY_WITH_BARRIERS(copy_ok, copy_rec, rec, rec_len);
     286             : 
     287             :         /* we must check data is consistent again after the copy */
     288           0 :         if (copy_ok && b1 == copy_rec->b2) {
     289             :             /* record is consistent, use it */
     290           0 :             break;
     291             :         }
     292             :     }
     293           0 :     if (count == 0) {
     294             :         /* couldn't successfully read header we have to give up */
     295           0 :         ret = EIO;
     296           0 :         goto done;
     297             :     }
     298             : 
     299           0 :     *_rec = copy_rec;
     300           0 :     ret = 0;
     301             : 
     302             : done:
     303           0 :     if (ret) {
     304           0 :         free(copy_rec);
     305           0 :         *_rec = NULL;
     306             :     }
     307           0 :     return ret;
     308             : }
     309             : 
     310             : /*
     311             :  * returns strings froma a buffer.
     312             :  *
     313             :  * Call first time with *cookie set to null, then call again
     314             :  * with the returned cookie.
     315             :  * On the last string the cookie will be reset to null and
     316             :  * all strings will have been returned.
     317             :  * In case the last string is not zero terminated EINVAL is returned.
     318             :  */
     319           0 : errno_t sss_nss_str_ptr_from_buffer(char **str, void **cookie,
     320             :                                     char *buf, size_t len)
     321             : {
     322           0 :     char *max = buf + len;
     323             :     char *ret;
     324             :     char *p;
     325             : 
     326           0 :     if (*cookie == NULL) {
     327           0 :         p = buf;
     328             :     } else {
     329           0 :         p = *((char **)cookie);
     330             :     }
     331             : 
     332           0 :     ret = p;
     333             : 
     334           0 :     while (p < max) {
     335           0 :         if (*p == '\0') {
     336           0 :             break;
     337             :         }
     338           0 :         p++;
     339             :     }
     340           0 :     if (p >= max) {
     341           0 :         return EINVAL;
     342             :     }
     343           0 :     p++;
     344           0 :     if (p == max) {
     345           0 :         *cookie = NULL;
     346             :     } else {
     347           0 :         *cookie = p;
     348             :     }
     349             : 
     350           0 :     *str = ret;
     351           0 :     return 0;
     352             : }
     353             : 
     354           0 : uint32_t sss_nss_mc_next_slot_with_hash(struct sss_mc_rec *rec,
     355             :                                         uint32_t hash)
     356             : {
     357           0 :     if (rec->hash1 == hash) {
     358           0 :         return rec->next1;
     359           0 :     } else if (rec->hash2 == hash) {
     360           0 :         return rec->next2;
     361             :     } else {
     362             :         /* it should never happen. */
     363           0 :         return MC_INVALID_VAL;
     364             :     }
     365             : 
     366             : }

Generated by: LCOV version 1.10