Updated to fedora-glibc-20060426T2000
[kopensolaris-gnu/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd lookup.
2    Copyright (C) 1998-2005, 2006 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 #if 0
444   uid_t oldeuid = 0;
445   if (db->secure)
446     {
447       oldeuid = geteuid ();
448       pthread_seteuid_np (c_uid);
449     }
450 #endif
451
452   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
453          && (errval = errno) == ERANGE)
454     {
455       char *old_buffer = buffer;
456       errno = 0;
457
458       if (__builtin_expect (buflen > 32768, 0))
459         {
460           buflen *= 2;
461           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
462           if (buffer == NULL)
463             {
464               /* We ran out of memory.  We cannot do anything but
465                  sending a negative response.  In reality this should
466                  never happen.  */
467               pwd = NULL;
468               buffer = old_buffer;
469
470               /* We set the error to indicate this is (possibly) a
471                  temporary error and that it does not mean the entry
472                  is not available at all.  */
473               errval = EAGAIN;
474               break;
475             }
476           use_malloc = true;
477         }
478       else
479         /* Allocate a new buffer on the stack.  If possible combine it
480            with the previously allocated buffer.  */
481         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
482     }
483
484 #if 0
485   if (db->secure)
486     pthread_seteuid_np (oldeuid);
487 #endif
488
489   /* Add the entry to the cache.  */
490   cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, errval);
491
492   if (use_malloc)
493     free (buffer);
494 }
495
496
497 void
498 addpwbyname (struct database_dyn *db, int fd, request_header *req,
499              void *key, uid_t c_uid)
500 {
501   union keytype u = { .v = key };
502
503   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
504 }
505
506
507 void
508 readdpwbyname (struct database_dyn *db, struct hashentry *he,
509                struct datahead *dh)
510 {
511   request_header req =
512     {
513       .type = GETPWBYNAME,
514       .key_len = he->len
515     };
516   union keytype u = { .v = db->data + he->key };
517
518   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
519 }
520
521
522 void
523 addpwbyuid (struct database_dyn *db, int fd, request_header *req,
524             void *key, uid_t c_uid)
525 {
526   char *ep;
527   uid_t uid = strtoul ((char *) key, &ep, 10);
528
529   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
530     {
531       if (debug_level > 0)
532         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
533
534       errno = EINVAL;
535       return;
536     }
537
538   union keytype u = { .u = uid };
539
540   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
541 }
542
543
544 void
545 readdpwbyuid (struct database_dyn *db, struct hashentry *he,
546               struct datahead *dh)
547 {
548   char *ep;
549   uid_t uid = strtoul (db->data + he->key, &ep, 10);
550
551   /* Since the key has been added before it must be OK.  */
552   assert (*(db->data + he->key) != '\0' && *ep == '\0');
553
554   request_header req =
555     {
556       .type = GETPWBYUID,
557       .key_len = he->len
558     };
559   union keytype u = { .u = uid };
560
561   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
562 }