1  /* Cache handling for group lookup.
       2     Copyright (C) 1998-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     This program is free software; you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published
       7     by the Free Software Foundation; version 2 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <assert.h>
      19  #include <errno.h>
      20  #include <error.h>
      21  #include <grp.h>
      22  #include <libintl.h>
      23  #include <stdbool.h>
      24  #include <stddef.h>
      25  #include <stdio.h>
      26  #include <stdint.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  #include <unistd.h>
      30  #include <sys/mman.h>
      31  #include <sys/socket.h>
      32  #include <stackinfo.h>
      33  #include <scratch_buffer.h>
      34  
      35  #include "nscd.h"
      36  #include "dbg_log.h"
      37  
      38  /* This is the standard reply in case the service is disabled.  */
      39  static const gr_response_header disabled =
      40  {
      41    .version = NSCD_VERSION,
      42    .found = -1,
      43    .gr_name_len = 0,
      44    .gr_passwd_len = 0,
      45    .gr_gid = -1,
      46    .gr_mem_cnt = 0,
      47  };
      48  
      49  /* This is the struct describing how to write this record.  */
      50  const struct iovec grp_iov_disabled =
      51  {
      52    .iov_base = (void *) &disabled,
      53    .iov_len = sizeof (disabled)
      54  };
      55  
      56  
      57  /* This is the standard reply in case we haven't found the dataset.  */
      58  static const gr_response_header notfound =
      59  {
      60    .version = NSCD_VERSION,
      61    .found = 0,
      62    .gr_name_len = 0,
      63    .gr_passwd_len = 0,
      64    .gr_gid = -1,
      65    .gr_mem_cnt = 0,
      66  };
      67  
      68  
      69  static time_t
      70  cache_addgr (struct database_dyn *db, int fd, request_header *req,
      71  	     const void *key, struct group *grp, uid_t owner,
      72  	     struct hashentry *const he, struct datahead *dh, int errval)
      73  {
      74    bool all_written = true;
      75    ssize_t total;
      76    time_t t = time (NULL);
      77  
      78    /* We allocate all data in one memory block: the iov vector,
      79       the response header and the dataset itself.  */
      80    struct dataset
      81    {
      82      struct datahead head;
      83      gr_response_header resp;
      84      char strdata[0];
      85    } *dataset;
      86  
      87    assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
      88  
      89    time_t timeout = MAX_TIMEOUT_VALUE;
      90    if (grp == NULL)
      91      {
      92        if (he != NULL && errval == EAGAIN)
      93  	{
      94  	  /* If we have an old record available but cannot find one
      95  	     now because the service is not available we keep the old
      96  	     record and make sure it does not get removed.  */
      97  	  if (reload_count != UINT_MAX)
      98  	    /* Do not reset the value if we never not reload the record.  */
      99  	    dh->nreloads = reload_count - 1;
     100  
     101  	  /* Reload with the same time-to-live value.  */
     102  	  timeout = dh->timeout = t + db->postimeout;
     103  
     104  	  total = 0;
     105  	}
     106        else
     107  	{
     108  	  /* We have no data.  This means we send the standard reply for this
     109  	     case.  */
     110  	  total = sizeof (notfound);
     111  
     112  	  if (fd != -1
     113  	      && TEMP_FAILURE_RETRY (send (fd, ¬found, total,
     114  					   MSG_NOSIGNAL)) != total)
     115  	    all_written = false;
     116  
     117  	  /* If we have a transient error or cannot permanently store
     118  	     the result, so be it.  */
     119  	  if (errval == EAGAIN || __glibc_unlikely (db->negtimeout == 0))
     120  	    {
     121  	      /* Mark the old entry as obsolete.  */
     122  	      if (dh != NULL)
     123  		dh->usable = false;
     124  	    }
     125  	  else if ((dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1)) != NULL)
     126  	    {
     127  	      timeout = datahead_init_neg (&dataset->head,
     128  					   (sizeof (struct dataset)
     129  					    + req->key_len), total,
     130  					   db->negtimeout);
     131  
     132  	      /* This is the reply.  */
     133  	      memcpy (&dataset->resp, ¬found, total);
     134  
     135  	      /* Copy the key data.  */
     136  	      memcpy (dataset->strdata, key, req->key_len);
     137  
     138  	      /* If necessary, we also propagate the data to disk.  */
     139  	      if (db->persistent)
     140  		{
     141  		  // XXX async OK?
     142  		  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
     143  		  msync ((void *) pval,
     144  			 ((uintptr_t) dataset & pagesize_m1)
     145  			 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
     146  		}
     147  
     148  	      (void) cache_add (req->type, &dataset->strdata, req->key_len,
     149  				&dataset->head, true, db, owner, he == NULL);
     150  
     151  	      pthread_rwlock_unlock (&db->lock);
     152  
     153  	      /* Mark the old entry as obsolete.  */
     154  	      if (dh != NULL)
     155  		dh->usable = false;
     156  	    }
     157  	}
     158      }
     159    else
     160      {
     161        /* Determine the I/O structure.  */
     162        size_t gr_name_len = strlen (grp->gr_name) + 1;
     163        size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
     164        size_t gr_mem_cnt = 0;
     165        uint32_t *gr_mem_len;
     166        size_t gr_mem_len_total = 0;
     167        char *gr_name;
     168        char *cp;
     169        const size_t key_len = strlen (key);
     170        const size_t buf_len = 3 * sizeof (grp->gr_gid) + key_len + 1;
     171        size_t alloca_used = 0;
     172        char *buf = alloca_account (buf_len, alloca_used);
     173        ssize_t n;
     174        size_t cnt;
     175  
     176        /* We need this to insert the `bygid' entry.  */
     177        int key_offset;
     178        n = snprintf (buf, buf_len, "%d%c%n%s", grp->gr_gid, '\0',
     179  		    &key_offset, (char *) key) + 1;
     180  
     181        /* Determine the length of all members.  */
     182        while (grp->gr_mem[gr_mem_cnt])
     183  	++gr_mem_cnt;
     184        gr_mem_len = alloca_account (gr_mem_cnt * sizeof (uint32_t), alloca_used);
     185        for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
     186  	{
     187  	  gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
     188  	  gr_mem_len_total += gr_mem_len[gr_mem_cnt];
     189  	}
     190  
     191        total = (offsetof (struct dataset, strdata)
     192  	       + gr_mem_cnt * sizeof (uint32_t)
     193  	       + gr_name_len + gr_passwd_len + gr_mem_len_total);
     194  
     195        /* If we refill the cache, first assume the reconrd did not
     196  	 change.  Allocate memory on the cache since it is likely
     197  	 discarded anyway.  If it turns out to be necessary to have a
     198  	 new record we can still allocate real memory.  */
     199        bool dataset_temporary = false;
     200        bool dataset_malloced = false;
     201        dataset = NULL;
     202  
     203        if (he == NULL)
     204  	{
     205  	  /* Prevent an INVALIDATE request from pruning the data between
     206  	     the two calls to cache_add.  */
     207  	  if (db->propagate)
     208  	    pthread_mutex_lock (&db->prune_run_lock);
     209  	  dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
     210  	}
     211  
     212        if (dataset == NULL)
     213  	{
     214  	  if (he == NULL && db->propagate)
     215  	    pthread_mutex_unlock (&db->prune_run_lock);
     216  
     217  	  /* We cannot permanently add the result in the moment.  But
     218  	     we can provide the result as is.  Store the data in some
     219  	     temporary memory.  */
     220  	  if (! __libc_use_alloca (alloca_used + total + n))
     221  	    {
     222  	      dataset = malloc (total + n);
     223  	      /* Perhaps we should log a message that we were unable
     224  		 to allocate memory for a large request.  */
     225  	      if (dataset == NULL)
     226  		goto out;
     227  	      dataset_malloced = true;
     228  	    }
     229  	  else
     230  	    dataset = alloca_account (total + n, alloca_used);
     231  
     232  	  /* We cannot add this record to the permanent database.  */
     233  	  dataset_temporary = true;
     234  	}
     235  
     236        timeout = datahead_init_pos (&dataset->head, total + n,
     237  				   total - offsetof (struct dataset, resp),
     238  				   he == NULL ? 0 : dh->nreloads + 1,
     239  				   db->postimeout);
     240  
     241        dataset->resp.version = NSCD_VERSION;
     242        dataset->resp.found = 1;
     243        dataset->resp.gr_name_len = gr_name_len;
     244        dataset->resp.gr_passwd_len = gr_passwd_len;
     245        dataset->resp.gr_gid = grp->gr_gid;
     246        dataset->resp.gr_mem_cnt = gr_mem_cnt;
     247  
     248        cp = dataset->strdata;
     249  
     250        /* This is the member string length array.  */
     251        cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
     252        gr_name = cp;
     253        cp = mempcpy (cp, grp->gr_name, gr_name_len);
     254        cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
     255  
     256        for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
     257  	cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
     258  
     259        /* Finally the stringified GID value.  */
     260        memcpy (cp, buf, n);
     261        char *key_copy = cp + key_offset;
     262        assert (key_copy == strchr (cp, '\0') + 1);
     263  
     264        assert (cp == dataset->strdata + total - offsetof (struct dataset,
     265  							 strdata));
     266  
     267        /* Now we can determine whether on refill we have to create a new
     268  	 record or not.  */
     269        if (he != NULL)
     270  	{
     271  	  assert (fd == -1);
     272  
     273  	  if (total + n == dh->allocsize
     274  	      && total - offsetof (struct dataset, resp) == dh->recsize
     275  	      && memcmp (&dataset->resp, dh->data,
     276  			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
     277  	    {
     278  	      /* The data has not changed.  We will just bump the
     279  		 timeout value.  Note that the new record has been
     280  		 allocated on the stack and need not be freed.  */
     281  	      dh->timeout = dataset->head.timeout;
     282  	      ++dh->nreloads;
     283  
     284  	      /* If the new record was allocated via malloc, then we must free
     285  		 it here.  */
     286  	      if (dataset_malloced)
     287  		free (dataset);
     288  	    }
     289  	  else
     290  	    {
     291  	      /* We have to create a new record.  Just allocate
     292  		 appropriate memory and copy it.  */
     293  	      struct dataset *newp
     294  		= (struct dataset *) mempool_alloc (db, total + n, 1);
     295  	      if (newp != NULL)
     296  		{
     297  		  /* Adjust pointers into the memory block.  */
     298  		  gr_name = (char *) newp + (gr_name - (char *) dataset);
     299  		  cp = (char *) newp + (cp - (char *) dataset);
     300  		  key_copy = (char *) newp + (key_copy - (char *) dataset);
     301  
     302  		  dataset = memcpy (newp, dataset, total + n);
     303  		  dataset_temporary = false;
     304  		}
     305  
     306  	      /* Mark the old record as obsolete.  */
     307  	      dh->usable = false;
     308  	    }
     309  	}
     310        else
     311  	{
     312  	  /* We write the dataset before inserting it to the database
     313  	     since while inserting this thread might block and so would
     314  	     unnecessarily let the receiver wait.  */
     315  	  assert (fd != -1);
     316  
     317  	  if (writeall (fd, &dataset->resp, dataset->head.recsize)
     318  	      != dataset->head.recsize)
     319  	    all_written = false;
     320  	}
     321  
     322        /* Add the record to the database.  But only if it has not been
     323  	 stored on the stack.  */
     324        if (! dataset_temporary)
     325  	{
     326  	  /* If necessary, we also propagate the data to disk.  */
     327  	  if (db->persistent)
     328  	    {
     329  	      // XXX async OK?
     330  	      uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
     331  	      msync ((void *) pval,
     332  		     ((uintptr_t) dataset & pagesize_m1) + total + n,
     333  		     MS_ASYNC);
     334  	    }
     335  
     336  	  /* NB: in the following code we always must add the entry
     337  	     marked with FIRST first.  Otherwise we end up with
     338  	     dangling "pointers" in case a latter hash entry cannot be
     339  	     added.  */
     340  	  bool first = true;
     341  
     342  	  /* If the request was by GID, add that entry first.  */
     343  	  if (req->type == GETGRBYGID)
     344  	    {
     345  	      if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
     346  			     db, owner, he == NULL) < 0)
     347  		goto out;
     348  
     349  	      first = false;
     350  	    }
     351  	  /* If the key is different from the name add a separate entry.  */
     352  	  else if (strcmp (key_copy, gr_name) != 0)
     353  	    {
     354  	      if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
     355  			     &dataset->head, true, db, owner, he == NULL) < 0)
     356  		goto out;
     357  
     358  	      first = false;
     359  	    }
     360  
     361  	  /* We have to add the value for both, byname and byuid.  */
     362  	  if ((req->type == GETGRBYNAME || db->propagate)
     363  	      && __builtin_expect (cache_add (GETGRBYNAME, gr_name,
     364  					      gr_name_len,
     365  					      &dataset->head, first, db, owner,
     366  					      he == NULL)
     367  				   == 0, 1))
     368  	    {
     369  	      if (req->type == GETGRBYNAME && db->propagate)
     370  		(void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
     371  				  false, db, owner, false);
     372  	    }
     373  
     374  	out:
     375  	  pthread_rwlock_unlock (&db->lock);
     376  	  if (he == NULL && db->propagate)
     377  	    pthread_mutex_unlock (&db->prune_run_lock);
     378  	}
     379      }
     380  
     381    if (__builtin_expect (!all_written, 0) && debug_level > 0)
     382      {
     383        char buf[256];
     384        dbg_log (_("short write in %s: %s"),  __FUNCTION__,
     385  	       strerror_r (errno, buf, sizeof (buf)));
     386      }
     387  
     388    return timeout;
     389  }
     390  
     391  
     392  union keytype
     393  {
     394    void *v;
     395    gid_t g;
     396  };
     397  
     398  
     399  static int
     400  lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
     401  	size_t buflen, struct group **grp)
     402  {
     403    if (type == GETGRBYNAME)
     404      return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
     405    else
     406      return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
     407  }
     408  
     409  
     410  static time_t
     411  addgrbyX (struct database_dyn *db, int fd, request_header *req,
     412  	  union keytype key, const char *keystr, uid_t uid,
     413  	  struct hashentry *he, struct datahead *dh)
     414  {
     415    /* Search for the entry matching the key.  Please note that we don't
     416       look again in the table whether the dataset is now available.  We
     417       simply insert it.  It does not matter if it is in there twice.  The
     418       pruning function only will look at the timestamp.  */
     419  
     420    struct group resultbuf;
     421    struct group *grp;
     422    int errval = 0;
     423    struct scratch_buffer tmpbuf;
     424    scratch_buffer_init (&tmpbuf);
     425  
     426    if (__glibc_unlikely (debug_level > 0))
     427      {
     428        if (he == NULL)
     429  	dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
     430        else
     431  	dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
     432      }
     433  
     434    while (lookup (req->type, key, &resultbuf,
     435  		 tmpbuf.data, tmpbuf.length, &grp) != 0
     436  	 && (errval = errno) == ERANGE)
     437      if (!scratch_buffer_grow (&tmpbuf))
     438        {
     439  	/* We ran out of memory.  We cannot do anything but sending a
     440  	   negative response.  In reality this should never
     441  	   happen.  */
     442  	grp = NULL;
     443  	/* We set the error to indicate this is (possibly) a temporary
     444  	   error and that it does not mean the entry is not available
     445  	   at all.  */
     446  	errval = EAGAIN;
     447  	break;
     448        }
     449  
     450    time_t timeout = cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
     451    scratch_buffer_free (&tmpbuf);
     452    return timeout;
     453  }
     454  
     455  
     456  void
     457  addgrbyname (struct database_dyn *db, int fd, request_header *req,
     458  	     void *key, uid_t uid)
     459  {
     460    union keytype u = { .v = key };
     461  
     462    addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
     463  }
     464  
     465  
     466  time_t
     467  readdgrbyname (struct database_dyn *db, struct hashentry *he,
     468  	       struct datahead *dh)
     469  {
     470    request_header req =
     471      {
     472        .type = GETGRBYNAME,
     473        .key_len = he->len
     474      };
     475    union keytype u = { .v = db->data + he->key };
     476  
     477    return addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
     478  }
     479  
     480  
     481  void
     482  addgrbygid (struct database_dyn *db, int fd, request_header *req,
     483  	    void *key, uid_t uid)
     484  {
     485    char *ep;
     486    gid_t gid = strtoul ((char *) key, &ep, 10);
     487  
     488    if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
     489      {
     490        if (debug_level > 0)
     491  	dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
     492  
     493        errno = EINVAL;
     494        return;
     495      }
     496  
     497    union keytype u = { .g = gid };
     498  
     499    addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
     500  }
     501  
     502  
     503  time_t
     504  readdgrbygid (struct database_dyn *db, struct hashentry *he,
     505  	      struct datahead *dh)
     506  {
     507    char *ep;
     508    gid_t gid = strtoul (db->data + he->key, &ep, 10);
     509  
     510    /* Since the key has been added before it must be OK.  */
     511    assert (*(db->data + he->key) != '\0' && *ep == '\0');
     512  
     513    request_header req =
     514      {
     515        .type = GETGRBYGID,
     516        .key_len = he->len
     517      };
     518    union keytype u = { .g = gid };
     519  
     520    return addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
     521  }