Avoid warnings for unused results in nscd/connections.c.
[platform/upstream/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd lookup.
2    Copyright (C) 1998-2014 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, see <http://www.gnu.org/licenses/>.  */
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 time_t
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 *const he, struct datahead *dh, int errval)
83 {
84   bool all_written = true;
85   ssize_t total;
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   time_t timeout = MAX_TIMEOUT_VALUE;
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           /* Reload with the same time-to-live value.  */
112           timeout = dh->timeout = t + db->postimeout;
113
114           total = 0;
115         }
116       else
117         {
118           /* We have no data.  This means we send the standard reply for this
119              case.  */
120           total = sizeof (notfound);
121
122           if (fd != -1
123               && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
124                                            MSG_NOSIGNAL)) != total)
125             all_written = false;
126
127           /* If we have a transient error or cannot permanently store
128              the result, so be it.  */
129           if (errno == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
130             {
131               /* Mark the old entry as obsolete.  */
132               if (dh != NULL)
133                 dh->usable = false;
134             }
135           else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
136                                                   + req->key_len), 1)) != NULL)
137             {
138               timeout = datahead_init_neg (&dataset->head,
139                                            (sizeof (struct dataset)
140                                             + req->key_len), total,
141                                            db->negtimeout);
142
143               /* This is the reply.  */
144               memcpy (&dataset->resp, &notfound, total);
145
146               /* Copy the key data.  */
147               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
148
149               /* If necessary, we also propagate the data to disk.  */
150               if (db->persistent)
151                 {
152                   // XXX async OK?
153                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
154                   msync ((void *) pval,
155                          ((uintptr_t) dataset & pagesize_m1)
156                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
157                 }
158
159               (void) cache_add (req->type, key_copy, req->key_len,
160                                 &dataset->head, true, db, owner, he == NULL);
161
162               pthread_rwlock_unlock (&db->lock);
163
164               /* Mark the old entry as obsolete.  */
165               if (dh != NULL)
166                 dh->usable = false;
167             }
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       total = (offsetof (struct dataset, strdata)
190                + 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         dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
202
203       if (dataset == NULL)
204         {
205           /* We cannot permanently add the result in the moment.  But
206              we can provide the result as is.  Store the data in some
207              temporary memory.  */
208           dataset = (struct dataset *) alloca (total + n);
209
210           /* We cannot add this record to the permanent database.  */
211           alloca_used = true;
212         }
213
214       timeout = datahead_init_pos (&dataset->head, total + n,
215                                    total - offsetof (struct dataset, resp),
216                                    he == NULL ? 0 : dh->nreloads + 1,
217                                    db->postimeout);
218
219       dataset->resp.version = NSCD_VERSION;
220       dataset->resp.found = 1;
221       dataset->resp.pw_name_len = pw_name_len;
222       dataset->resp.pw_passwd_len = pw_passwd_len;
223       dataset->resp.pw_uid = pwd->pw_uid;
224       dataset->resp.pw_gid = pwd->pw_gid;
225       dataset->resp.pw_gecos_len = pw_gecos_len;
226       dataset->resp.pw_dir_len = pw_dir_len;
227       dataset->resp.pw_shell_len = pw_shell_len;
228
229       cp = dataset->strdata;
230
231       /* Copy the strings over into the buffer.  */
232       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
233       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
234       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
235       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
236       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
237
238       /* Finally the stringified UID value.  */
239       memcpy (cp, buf, n);
240       char *key_copy = cp + key_offset;
241       assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
242
243       assert (cp == dataset->strdata + total - offsetof (struct dataset,
244                                                          strdata));
245
246       /* Now we can determine whether on refill we have to create a new
247          record or not.  */
248       if (he != NULL)
249         {
250           assert (fd == -1);
251
252           if (dataset->head.allocsize == dh->allocsize
253               && dataset->head.recsize == dh->recsize
254               && memcmp (&dataset->resp, dh->data,
255                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
256             {
257               /* The data has not changed.  We will just bump the
258                  timeout value.  Note that the new record has been
259                  allocated on the stack and need not be freed.  */
260               dh->timeout = dataset->head.timeout;
261               ++dh->nreloads;
262             }
263           else
264             {
265               /* We have to create a new record.  Just allocate
266                  appropriate memory and copy it.  */
267               struct dataset *newp
268                 = (struct dataset *) mempool_alloc (db, total + n, 1);
269               if (newp != NULL)
270                 {
271                   /* Adjust pointer into the memory block.  */
272                   cp = (char *) newp + (cp - (char *) dataset);
273                   key_copy = (char *) newp + (key_copy - (char *) dataset);
274
275                   dataset = memcpy (newp, dataset, total + n);
276                   alloca_used = false;
277                 }
278
279               /* Mark the old record as obsolete.  */
280               dh->usable = false;
281             }
282         }
283       else
284         {
285           /* We write the dataset before inserting it to the database
286              since while inserting this thread might block and so would
287              unnecessarily let the receiver wait.  */
288           assert (fd != -1);
289
290 #ifdef HAVE_SENDFILE
291           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
292             {
293               assert (db->wr_fd != -1);
294               assert ((char *) &dataset->resp > (char *) db->data);
295               assert ((char *) dataset - (char *) db->head
296                       + total
297                       <= (sizeof (struct database_pers_head)
298                           + db->head->module * sizeof (ref_t)
299                           + db->head->data_size));
300               ssize_t written = sendfileall (fd, db->wr_fd,
301                                              (char *) &dataset->resp
302                                              - (char *) db->head,
303                                              dataset->head.recsize);
304               if (written != dataset->head.recsize)
305                 {
306 # ifndef __ASSUME_SENDFILE
307                   if (written == -1 && errno == ENOSYS)
308                     goto use_write;
309 # endif
310                   all_written = false;
311                 }
312             }
313           else
314 # ifndef __ASSUME_SENDFILE
315           use_write:
316 # endif
317 #endif
318             if (writeall (fd, &dataset->resp, dataset->head.recsize)
319                 != dataset->head.recsize)
320               all_written = false;
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           /* 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, he == NULL) < 0)
349                 goto out;
350
351               first = false;
352             }
353           /* If the key is different from the name add a separate entry.  */
354           else if (strcmp (key_copy, dataset->strdata) != 0)
355             {
356               if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
357                              &dataset->head, true, db, owner, he == NULL) < 0)
358                 goto out;
359
360               first = false;
361             }
362
363           /* We have to add the value for both, byname and byuid.  */
364           if ((req->type == GETPWBYNAME || db->propagate)
365               && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
366                                               pw_name_len, &dataset->head,
367                                               first, db, owner, he == NULL)
368                                    == 0, 1))
369             {
370               if (req->type == GETPWBYNAME && db->propagate)
371                 (void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
372                                   false, db, owner, false);
373             }
374
375         out:
376           pthread_rwlock_unlock (&db->lock);
377         }
378     }
379
380   if (__builtin_expect (!all_written, 0) && debug_level > 0)
381     {
382       char buf[256];
383       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
384                strerror_r (errno, buf, sizeof (buf)));
385     }
386
387   return timeout;
388 }
389
390
391 union keytype
392 {
393   void *v;
394   uid_t u;
395 };
396
397
398 static int
399 lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
400         size_t buflen, struct passwd **pwd)
401 {
402   if (type == GETPWBYNAME)
403     return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
404   else
405     return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
406 }
407
408
409 static time_t
410 addpwbyX (struct database_dyn *db, int fd, request_header *req,
411           union keytype key, const char *keystr, uid_t c_uid,
412           struct hashentry *he, struct datahead *dh)
413 {
414   /* Search for the entry matching the key.  Please note that we don't
415      look again in the table whether the dataset is now available.  We
416      simply insert it.  It does not matter if it is in there twice.  The
417      pruning function only will look at the timestamp.  */
418   size_t buflen = 1024;
419   char *buffer = (char *) alloca (buflen);
420   struct passwd resultbuf;
421   struct passwd *pwd;
422   bool use_malloc = false;
423   int errval = 0;
424
425   if (__glibc_unlikely (debug_level > 0))
426     {
427       if (he == NULL)
428         dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
429       else
430         dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
431     }
432
433   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
434          && (errval = errno) == ERANGE)
435     {
436       errno = 0;
437
438       if (__glibc_unlikely (buflen > 32768))
439         {
440           char *old_buffer = buffer;
441           buflen *= 2;
442           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
443           if (buffer == NULL)
444             {
445               /* We ran out of memory.  We cannot do anything but
446                  sending a negative response.  In reality this should
447                  never happen.  */
448               pwd = NULL;
449               buffer = old_buffer;
450
451               /* We set the error to indicate this is (possibly) a
452                  temporary error and that it does not mean the entry
453                  is not available at all.  */
454               errval = EAGAIN;
455               break;
456             }
457           use_malloc = true;
458         }
459       else
460         /* Allocate a new buffer on the stack.  If possible combine it
461            with the previously allocated buffer.  */
462         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
463     }
464
465   /* Add the entry to the cache.  */
466   time_t timeout = cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh,
467                                 errval);
468
469   if (use_malloc)
470     free (buffer);
471
472   return timeout;
473 }
474
475
476 void
477 addpwbyname (struct database_dyn *db, int fd, request_header *req,
478              void *key, uid_t c_uid)
479 {
480   union keytype u = { .v = key };
481
482   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
483 }
484
485
486 time_t
487 readdpwbyname (struct database_dyn *db, struct hashentry *he,
488                struct datahead *dh)
489 {
490   request_header req =
491     {
492       .type = GETPWBYNAME,
493       .key_len = he->len
494     };
495   union keytype u = { .v = db->data + he->key };
496
497   return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
498 }
499
500
501 void
502 addpwbyuid (struct database_dyn *db, int fd, request_header *req,
503             void *key, uid_t c_uid)
504 {
505   char *ep;
506   uid_t uid = strtoul ((char *) key, &ep, 10);
507
508   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
509     {
510       if (debug_level > 0)
511         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
512
513       errno = EINVAL;
514       return;
515     }
516
517   union keytype u = { .u = uid };
518
519   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
520 }
521
522
523 time_t
524 readdpwbyuid (struct database_dyn *db, struct hashentry *he,
525               struct datahead *dh)
526 {
527   char *ep;
528   uid_t uid = strtoul (db->data + he->key, &ep, 10);
529
530   /* Since the key has been added before it must be OK.  */
531   assert (*(db->data + he->key) != '\0' && *ep == '\0');
532
533   request_header req =
534     {
535       .type = GETPWBYUID,
536       .key_len = he->len
537     };
538   union keytype u = { .u = uid };
539
540   return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
541 }