1 /* Cache handling for group lookup.
2    Copyright (C) 1998-2022 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
cache_addgr(struct database_dyn * db,int fd,request_header * req,const void * key,struct group * grp,uid_t owner,struct hashentry * const he,struct datahead * dh,int errval)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, &notfound, 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 (errno == EAGAIN || __builtin_expect (db->negtimeout == 0, 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, &notfound, 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 == (char *) rawmemchr (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
lookup(int type,union keytype key,struct group * resultbufp,char * buffer,size_t buflen,struct group ** grp)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
addgrbyX(struct database_dyn * db,int fd,request_header * req,union keytype key,const char * keystr,uid_t uid,struct hashentry * he,struct datahead * dh)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
addgrbyname(struct database_dyn * db,int fd,request_header * req,void * key,uid_t uid)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
readdgrbyname(struct database_dyn * db,struct hashentry * he,struct datahead * dh)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
addgrbygid(struct database_dyn * db,int fd,request_header * req,void * key,uid_t uid)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
readdgrbygid(struct database_dyn * db,struct hashentry * he,struct datahead * dh)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 }
522