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