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