Updated to fedora-glibc-20071212T1051
[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 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           /* If we cannot permanently store the result, so be it.  */
125           if (dataset != NULL)
126             {
127               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
128               dataset->head.recsize = total;
129               dataset->head.notfound = true;
130               dataset->head.nreloads = 0;
131               dataset->head.usable = true;
132
133               /* Compute the timeout time.  */
134               dataset->head.timeout = t + db->negtimeout;
135
136               /* This is the reply.  */
137               memcpy (&dataset->resp, &notfound, total);
138
139               /* Copy the key data.  */
140               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
141
142               /* If necessary, we also propagate the data to disk.  */
143               if (db->persistent)
144                 {
145                   // XXX async OK?
146                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
147                   msync ((void *) pval,
148                          ((uintptr_t) dataset & pagesize_m1)
149                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
150                 }
151
152               /* Now get the lock to safely insert the records.  */
153               pthread_rwlock_rdlock (&db->lock);
154
155               if (cache_add (req->type, key_copy, req->key_len,
156                              &dataset->head, true, db, owner) < 0)
157                 /* Ensure the data can be recovered.  */
158                 dataset->head.usable = false;
159
160
161               pthread_rwlock_unlock (&db->lock);
162
163               /* Mark the old entry as obsolete.  */
164               if (dh != NULL)
165                 dh->usable = false;
166             }
167           else
168             ++db->head->addfailed;
169         }
170     }
171   else
172     {
173       /* Determine the I/O structure.  */
174       size_t pw_name_len = strlen (pwd->pw_name) + 1;
175       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
176       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
177       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
178       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
179       char *cp;
180       const size_t key_len = strlen (key);
181       const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
182       char *buf = alloca (buf_len);
183       ssize_t n;
184
185       /* We need this to insert the `byuid' entry.  */
186       int key_offset;
187       n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
188                     &key_offset, (char *) key) + 1;
189
190       written = total = (sizeof (struct dataset) + pw_name_len + pw_passwd_len
191                          + pw_gecos_len + pw_dir_len + pw_shell_len);
192
193       /* If we refill the cache, first assume the reconrd did not
194          change.  Allocate memory on the cache since it is likely
195          discarded anyway.  If it turns out to be necessary to have a
196          new record we can still allocate real memory.  */
197       bool alloca_used = false;
198       dataset = NULL;
199
200       if (he == NULL)
201         {
202           dataset = (struct dataset *) mempool_alloc (db, total + n);
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       /* 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 pointer into the memory block.  */
277                   cp = (char *) newp + (cp - (char *) dataset);
278                   key_copy = (char *) newp + (key_copy - (char *) dataset);
279
280                   dataset = memcpy (newp, dataset, total + n);
281                   alloca_used = false;
282                 }
283               else
284                 ++db->head->addfailed;
285
286               /* Mark the old record as obsolete.  */
287               dh->usable = false;
288             }
289         }
290       else
291         {
292           /* We write the dataset before inserting it to the database
293              since while inserting this thread might block and so would
294              unnecessarily let the receiver wait.  */
295           assert (fd != -1);
296
297 #ifdef HAVE_SENDFILE
298           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
299             {
300               assert (db->wr_fd != -1);
301               assert ((char *) &dataset->resp > (char *) db->data);
302               assert ((char *) &dataset->resp - (char *) db->head
303                       + total
304                       <= (sizeof (struct database_pers_head)
305                           + db->head->module * sizeof (ref_t)
306                           + db->head->data_size));
307               written = sendfileall (fd, db->wr_fd,
308                                      (char *) &dataset->resp
309                                      - (char *) db->head, total);
310 # ifndef __ASSUME_SENDFILE
311               if (written == -1 && errno == ENOSYS)
312                 goto use_write;
313 # endif
314             }
315           else
316 # ifndef __ASSUME_SENDFILE
317           use_write:
318 # endif
319 #endif
320             written = writeall (fd, &dataset->resp, total);
321         }
322
323
324       /* Add the record to the database.  But only if it has not been
325          stored on the stack.  */
326       if (! alloca_used)
327         {
328           /* If necessary, we also propagate the data to disk.  */
329           if (db->persistent)
330             {
331               // XXX async OK?
332               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
333               msync ((void *) pval,
334                      ((uintptr_t) dataset & pagesize_m1) + total + n,
335                      MS_ASYNC);
336             }
337
338           /* Now get the lock to safely insert the records.  */
339           pthread_rwlock_rdlock (&db->lock);
340
341           /* NB: in the following code we always must add the entry
342              marked with FIRST first.  Otherwise we end up with
343              dangling "pointers" in case a latter hash entry cannot be
344              added.  */
345           bool first = true;
346
347           /* If the request was by UID, add that entry first.  */
348           if (req->type == GETPWBYUID)
349             {
350               if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
351                              db, owner) < 0)
352                 {
353                   /* Could not allocate memory.  Make sure the data gets
354                      discarded.  */
355                   dataset->head.usable = false;
356                   goto out;
357                 }
358
359               first = false;
360             }
361           /* If the key is different from the name add a separate entry.  */
362           else if (strcmp (key_copy, dataset->strdata) != 0)
363             {
364               if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
365                              &dataset->head, true, db, owner) < 0)
366                 {
367                   /* Could not allocate memory.  Make sure the data gets
368                      discarded.  */
369                   dataset->head.usable = false;
370                   goto out;
371                 }
372
373               first = false;
374             }
375
376           /* We have to add the value for both, byname and byuid.  */
377           if ((req->type == GETPWBYNAME || db->propagate)
378               && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
379                                               pw_name_len, &dataset->head,
380                                               first, db, owner) == 0, 1))
381             {
382               if (req->type == GETPWBYNAME && db->propagate)
383                 (void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
384                                   req->type != GETPWBYNAME, db, owner);
385             }
386           else if (first)
387             /* Could not allocate memory.  Make sure the data gets
388                discarded.  */
389             dataset->head.usable = false;
390
391         out:
392           pthread_rwlock_unlock (&db->lock);
393         }
394     }
395
396   if (__builtin_expect (written != total, 0) && debug_level > 0)
397     {
398       char buf[256];
399       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
400                strerror_r (errno, buf, sizeof (buf)));
401     }
402 }
403
404
405 union keytype
406 {
407   void *v;
408   uid_t u;
409 };
410
411
412 static int
413 lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
414         size_t buflen, struct passwd **pwd)
415 {
416   if (type == GETPWBYNAME)
417     return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
418   else
419     return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
420 }
421
422
423 static void
424 addpwbyX (struct database_dyn *db, int fd, request_header *req,
425           union keytype key, const char *keystr, uid_t c_uid,
426           struct hashentry *he, struct datahead *dh)
427 {
428   /* Search for the entry matching the key.  Please note that we don't
429      look again in the table whether the dataset is now available.  We
430      simply insert it.  It does not matter if it is in there twice.  The
431      pruning function only will look at the timestamp.  */
432   size_t buflen = 1024;
433   char *buffer = (char *) alloca (buflen);
434   struct passwd resultbuf;
435   struct passwd *pwd;
436   bool use_malloc = false;
437   int errval = 0;
438
439   if (__builtin_expect (debug_level > 0, 0))
440     {
441       if (he == NULL)
442         dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
443       else
444         dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
445     }
446
447   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
448          && (errval = errno) == ERANGE)
449     {
450       errno = 0;
451
452       if (__builtin_expect (buflen > 32768, 0))
453         {
454           char *old_buffer = buffer;
455           buflen *= 2;
456           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
457           if (buffer == NULL)
458             {
459               /* We ran out of memory.  We cannot do anything but
460                  sending a negative response.  In reality this should
461                  never happen.  */
462               pwd = NULL;
463               buffer = old_buffer;
464
465               /* We set the error to indicate this is (possibly) a
466                  temporary error and that it does not mean the entry
467                  is not available at all.  */
468               errval = EAGAIN;
469               break;
470             }
471           use_malloc = true;
472         }
473       else
474         /* Allocate a new buffer on the stack.  If possible combine it
475            with the previously allocated buffer.  */
476         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
477     }
478
479   /* Add the entry to the cache.  */
480   cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, errval);
481
482   if (use_malloc)
483     free (buffer);
484 }
485
486
487 void
488 addpwbyname (struct database_dyn *db, int fd, request_header *req,
489              void *key, uid_t c_uid)
490 {
491   union keytype u = { .v = key };
492
493   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
494 }
495
496
497 void
498 readdpwbyname (struct database_dyn *db, struct hashentry *he,
499                struct datahead *dh)
500 {
501   request_header req =
502     {
503       .type = GETPWBYNAME,
504       .key_len = he->len
505     };
506   union keytype u = { .v = db->data + he->key };
507
508   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
509 }
510
511
512 void
513 addpwbyuid (struct database_dyn *db, int fd, request_header *req,
514             void *key, uid_t c_uid)
515 {
516   char *ep;
517   uid_t uid = strtoul ((char *) key, &ep, 10);
518
519   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
520     {
521       if (debug_level > 0)
522         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
523
524       errno = EINVAL;
525       return;
526     }
527
528   union keytype u = { .u = uid };
529
530   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
531 }
532
533
534 void
535 readdpwbyuid (struct database_dyn *db, struct hashentry *he,
536               struct datahead *dh)
537 {
538   char *ep;
539   uid_t uid = strtoul (db->data + he->key, &ep, 10);
540
541   /* Since the key has been added before it must be OK.  */
542   assert (*(db->data + he->key) != '\0' && *ep == '\0');
543
544   request_header req =
545     {
546       .type = GETPWBYUID,
547       .key_len = he->len
548     };
549   union keytype u = { .u = uid };
550
551   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
552 }