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