Updated to fedora-glibc-20051119T1959
[kopensolaris-gnu/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd lookup.
2    Copyright (C) 1998-2002, 2003, 2004, 2005 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 #ifdef HAVE_SENDFILE
36 # include <sys/sendfile.h>
37 #endif
38 #include <sys/socket.h>
39 #include <stackinfo.h>
40
41 #include "nscd.h"
42 #include "dbg_log.h"
43 #ifdef HAVE_SENDFILE
44 # include <kernel-features.h>
45 #endif
46
47 /* This is the standard reply in case the service is disabled.  */
48 static const pw_response_header disabled =
49 {
50   .version = NSCD_VERSION,
51   .found = -1,
52   .pw_name_len = 0,
53   .pw_passwd_len = 0,
54   .pw_uid = -1,
55   .pw_gid = -1,
56   .pw_gecos_len = 0,
57   .pw_dir_len = 0,
58   .pw_shell_len = 0
59 };
60
61 /* This is the struct describing how to write this record.  */
62 const struct iovec pwd_iov_disabled =
63 {
64   .iov_base = (void *) &disabled,
65   .iov_len = sizeof (disabled)
66 };
67
68
69 /* This is the standard reply in case we haven't found the dataset.  */
70 static const pw_response_header notfound =
71 {
72   .version = NSCD_VERSION,
73   .found = 0,
74   .pw_name_len = 0,
75   .pw_passwd_len = 0,
76   .pw_uid = -1,
77   .pw_gid = -1,
78   .pw_gecos_len = 0,
79   .pw_dir_len = 0,
80   .pw_shell_len = 0
81 };
82
83
84 static void
85 cache_addpw (struct database_dyn *db, int fd, request_header *req,
86              const void *key, struct passwd *pwd, uid_t owner,
87              struct hashentry *he, struct datahead *dh, int errval)
88 {
89   ssize_t total;
90   ssize_t written;
91   time_t t = time (NULL);
92
93   /* We allocate all data in one memory block: the iov vector,
94      the response header and the dataset itself.  */
95   struct dataset
96   {
97     struct datahead head;
98     pw_response_header resp;
99     char strdata[0];
100   } *dataset;
101
102   assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
103
104   if (pwd == NULL)
105     {
106       if (he != NULL && errval == EAGAIN)
107         {
108           /* If we have an old record available but cannot find one
109              now because the service is not available we keep the old
110              record and make sure it does not get removed.  */
111           if (reload_count != UINT_MAX && dh->nreloads == reload_count)
112             /* Do not reset the value if we never not reload the record.  */
113             dh->nreloads = reload_count - 1;
114
115           written = total = 0;
116         }
117       else
118         {
119           /* We have no data.  This means we send the standard reply for this
120              case.  */
121           written = total = sizeof (notfound);
122
123           if (fd != -1)
124             written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
125                                                 MSG_NOSIGNAL));
126
127           dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
128           /* If we cannot permanently store the result, so be it.  */
129           if (dataset != NULL)
130             {
131               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
132               dataset->head.recsize = total;
133               dataset->head.notfound = true;
134               dataset->head.nreloads = 0;
135               dataset->head.usable = true;
136
137               /* Compute the timeout time.  */
138               dataset->head.timeout = t + db->negtimeout;
139
140               /* This is the reply.  */
141               memcpy (&dataset->resp, &notfound, total);
142
143               /* Copy the key data.  */
144               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
145
146               /* If necessary, we also propagate the data to disk.  */
147               if (db->persistent)
148                 {
149                   // XXX async OK?
150                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
151                   msync ((void *) pval,
152                          ((uintptr_t) dataset & pagesize_m1)
153                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
154                 }
155
156               /* Now get the lock to safely insert the records.  */
157               pthread_rwlock_rdlock (&db->lock);
158
159               if (cache_add (req->type, key_copy, req->key_len,
160                              &dataset->head, true, db, owner) < 0)
161                 /* Ensure the data can be recovered.  */
162                 dataset->head.usable = false;
163
164
165               pthread_rwlock_unlock (&db->lock);
166
167               /* Mark the old entry as obsolete.  */
168               if (dh != NULL)
169                 dh->usable = false;
170             }
171           else
172             ++db->head->addfailed;
173         }
174     }
175   else
176     {
177       /* Determine the I/O structure.  */
178       size_t pw_name_len = strlen (pwd->pw_name) + 1;
179       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
180       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
181       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
182       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
183       char *cp;
184       const size_t key_len = strlen (key);
185       const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
186       char *buf = alloca (buf_len);
187       ssize_t n;
188
189       /* We need this to insert the `byuid' entry.  */
190       int key_offset;
191       n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
192                     &key_offset, (char *) key) + 1;
193
194       written = total = (sizeof (struct dataset) + pw_name_len + pw_passwd_len
195                          + pw_gecos_len + pw_dir_len + pw_shell_len);
196
197       /* If we refill the cache, first assume the reconrd did not
198          change.  Allocate memory on the cache since it is likely
199          discarded anyway.  If it turns out to be necessary to have a
200          new record we can still allocate real memory.  */
201       bool alloca_used = false;
202       dataset = NULL;
203
204       if (he == NULL)
205         {
206           dataset = (struct dataset *) mempool_alloc (db, total + n);
207           if (dataset == NULL)
208             ++db->head->addfailed;
209         }
210
211       if (dataset == NULL)
212         {
213           /* We cannot permanently add the result in the moment.  But
214              we can provide the result as is.  Store the data in some
215              temporary memory.  */
216           dataset = (struct dataset *) alloca (total + n);
217
218           /* We cannot add this record to the permanent database.  */
219           alloca_used = true;
220         }
221
222       dataset->head.allocsize = total + n;
223       dataset->head.recsize = total - offsetof (struct dataset, resp);
224       dataset->head.notfound = false;
225       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
226       dataset->head.usable = true;
227
228       /* Compute the timeout time.  */
229       dataset->head.timeout = t + db->postimeout;
230
231       dataset->resp.version = NSCD_VERSION;
232       dataset->resp.found = 1;
233       dataset->resp.pw_name_len = pw_name_len;
234       dataset->resp.pw_passwd_len = pw_passwd_len;
235       dataset->resp.pw_uid = pwd->pw_uid;
236       dataset->resp.pw_gid = pwd->pw_gid;
237       dataset->resp.pw_gecos_len = pw_gecos_len;
238       dataset->resp.pw_dir_len = pw_dir_len;
239       dataset->resp.pw_shell_len = pw_shell_len;
240
241       cp = dataset->strdata;
242
243       /* Copy the strings over into the buffer.  */
244       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
245       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
246       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
247       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
248       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
249
250       /* Finally the stringified UID value.  */
251       memcpy (cp, buf, n);
252       char *key_copy = cp + key_offset;
253       assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
254
255       /* Now we can determine whether on refill we have to create a new
256          record or not.  */
257       if (he != NULL)
258         {
259           assert (fd == -1);
260
261           if (total + n == dh->allocsize
262               && total - offsetof (struct dataset, resp) == dh->recsize
263               && memcmp (&dataset->resp, dh->data,
264                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
265             {
266               /* The data has not changed.  We will just bump the
267                  timeout value.  Note that the new record has been
268                  allocated on the stack and need not be freed.  */
269               dh->timeout = dataset->head.timeout;
270               ++dh->nreloads;
271             }
272           else
273             {
274               /* We have to create a new record.  Just allocate
275                  appropriate memory and copy it.  */
276               struct dataset *newp
277                 = (struct dataset *) mempool_alloc (db, total + n);
278               if (newp != NULL)
279                 {
280                   /* Adjust pointer into the memory block.  */
281                   cp = (char *) newp + (cp - (char *) dataset);
282
283                   dataset = memcpy (newp, dataset, total + n);
284                   alloca_used = false;
285                 }
286
287               /* Mark the old record as obsolete.  */
288               dh->usable = false;
289             }
290         }
291       else
292         {
293           /* We write the dataset before inserting it to the database
294              since while inserting this thread might block and so would
295              unnecessarily let the receiver wait.  */
296           assert (fd != -1);
297
298 #ifdef HAVE_SENDFILE
299           if (__builtin_expect (db->mmap_used, 1))
300             {
301               assert (db->wr_fd != -1);
302               assert ((char *) &dataset->resp > (char *) db->data);
303               assert ((char *) &dataset->resp - (char *) db->head
304                       + total
305                       <= (sizeof (struct database_pers_head)
306                           + db->head->module * sizeof (ref_t)
307                           + db->head->data_size));
308               off_t off = (char *) &dataset->resp - (char *) db->head;
309               written = sendfile (fd, db->wr_fd, &off, 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 = req->type == GETPWBYNAME;
346
347           /* If the request was by UID, add that entry first.  */
348           if (req->type != GETPWBYNAME)
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           /* If the key is different from the name add a separate entry.  */
360           else if (strcmp (key_copy, dataset->strdata) != 0)
361             {
362               if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
363                              &dataset->head, first, db, owner) < 0)
364                 {
365                   /* Could not allocate memory.  Make sure the data gets
366                      discarded.  */
367                   dataset->head.usable = false;
368                   goto out;
369                 }
370
371               first = false;
372             }
373
374           /* We have to add the value for both, byname and byuid.  */
375           if (__builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
376                                            pw_name_len, &dataset->head, first,
377                                            db, owner) == 0, 1))
378             {
379               if (req->type == GETPWBYNAME)
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 #if 0
445   uid_t oldeuid = 0;
446   if (db->secure)
447     {
448       oldeuid = geteuid ();
449       pthread_seteuid_np (c_uid);
450     }
451 #endif
452
453   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
454          && (errval = errno) == ERANGE)
455     {
456       char *old_buffer = buffer;
457       errno = 0;
458
459       if (__builtin_expect (buflen > 32768, 0))
460         {
461           buflen *= 2;
462           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
463           if (buffer == NULL)
464             {
465               /* We ran out of memory.  We cannot do anything but
466                  sending a negative response.  In reality this should
467                  never happen.  */
468               pwd = NULL;
469               buffer = old_buffer;
470
471               /* We set the error to indicate this is (possibly) a
472                  temporary error and that it does not mean the entry
473                  is not available at all.  */
474               errval = EAGAIN;
475               break;
476             }
477           use_malloc = true;
478         }
479       else
480         /* Allocate a new buffer on the stack.  If possible combine it
481            with the previously allocated buffer.  */
482         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
483     }
484
485 #if 0
486   if (db->secure)
487     pthread_seteuid_np (oldeuid);
488 #endif
489
490   /* Add the entry to the cache.  */
491   cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, errval);
492
493   if (use_malloc)
494     free (buffer);
495 }
496
497
498 void
499 addpwbyname (struct database_dyn *db, int fd, request_header *req,
500              void *key, uid_t c_uid)
501 {
502   union keytype u = { .v = key };
503
504   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
505 }
506
507
508 void
509 readdpwbyname (struct database_dyn *db, struct hashentry *he,
510                struct datahead *dh)
511 {
512   request_header req =
513     {
514       .type = GETPWBYNAME,
515       .key_len = he->len
516     };
517   union keytype u = { .v = db->data + he->key };
518
519   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
520 }
521
522
523 void
524 addpwbyuid (struct database_dyn *db, int fd, request_header *req,
525             void *key, uid_t c_uid)
526 {
527   char *ep;
528   uid_t uid = strtoul ((char *) key, &ep, 10);
529
530   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
531     {
532       if (debug_level > 0)
533         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
534
535       errno = EINVAL;
536       return;
537     }
538
539   union keytype u = { .u = uid };
540
541   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
542 }
543
544
545 void
546 readdpwbyuid (struct database_dyn *db, struct hashentry *he,
547               struct datahead *dh)
548 {
549   char *ep;
550   uid_t uid = strtoul (db->data + he->key, &ep, 10);
551
552   /* Since the key has been added before it must be OK.  */
553   assert (*(db->data + he->key) != '\0' && *ep == '\0');
554
555   request_header req =
556     {
557       .type = GETPWBYUID,
558       .key_len = he->len
559     };
560   union keytype u = { .u = uid };
561
562   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
563 }