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