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