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