Revert "Properly check for short writes when sending the response in nscd"
[platform/upstream/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd lookup.
2    Copyright (C) 1998-2013 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   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   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           written = total = 0;
115         }
116       else
117         {
118           /* We have no data.  This means we send the standard reply for this
119              case.  */
120           written = total = sizeof (notfound);
121
122           if (fd != -1)
123             written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
124                                                 MSG_NOSIGNAL));
125
126           /* If we have a transient error or cannot permanently store
127              the result, so be it.  */
128           if (errno == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
129             {
130               /* Mark the old entry as obsolete.  */
131               if (dh != NULL)
132                 dh->usable = false;
133             }
134           else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
135                                                   + req->key_len), 1)) != NULL)
136             {
137               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
138               dataset->head.recsize = total;
139               dataset->head.notfound = true;
140               dataset->head.nreloads = 0;
141               dataset->head.usable = true;
142
143               /* Compute the timeout time.  */
144               timeout = dataset->head.timeout = t + db->negtimeout;
145
146               /* This is the reply.  */
147               memcpy (&dataset->resp, &notfound, total);
148
149               /* Copy the key data.  */
150               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
151
152               /* If necessary, we also propagate the data to disk.  */
153               if (db->persistent)
154                 {
155                   // XXX async OK?
156                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
157                   msync ((void *) pval,
158                          ((uintptr_t) dataset & pagesize_m1)
159                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
160                 }
161
162               (void) cache_add (req->type, key_copy, req->key_len,
163                                 &dataset->head, true, db, owner, he == NULL);
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         }
172     }
173   else
174     {
175       /* Determine the I/O structure.  */
176       size_t pw_name_len = strlen (pwd->pw_name) + 1;
177       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
178       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
179       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
180       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
181       char *cp;
182       const size_t key_len = strlen (key);
183       const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
184       char *buf = alloca (buf_len);
185       ssize_t n;
186
187       /* We need this to insert the `byuid' entry.  */
188       int key_offset;
189       n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
190                     &key_offset, (char *) key) + 1;
191
192       written = total = (offsetof (struct dataset, strdata)
193                          + pw_name_len + pw_passwd_len
194                          + pw_gecos_len + pw_dir_len + pw_shell_len);
195
196       /* If we refill the cache, first assume the reconrd did not
197          change.  Allocate memory on the cache since it is likely
198          discarded anyway.  If it turns out to be necessary to have a
199          new record we can still allocate real memory.  */
200       bool alloca_used = false;
201       dataset = NULL;
202
203       if (he == NULL)
204         dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
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       timeout = 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       assert (cp == dataset->strdata + total - offsetof (struct dataset,
251                                                          strdata));
252
253       /* Now we can determine whether on refill we have to create a new
254          record or not.  */
255       if (he != NULL)
256         {
257           assert (fd == -1);
258
259           if (dataset->head.allocsize == dh->allocsize
260               && dataset->head.recsize == dh->recsize
261               && memcmp (&dataset->resp, dh->data,
262                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
263             {
264               /* The data has not changed.  We will just bump the
265                  timeout value.  Note that the new record has been
266                  allocated on the stack and need not be freed.  */
267               dh->timeout = dataset->head.timeout;
268               ++dh->nreloads;
269             }
270           else
271             {
272               /* We have to create a new record.  Just allocate
273                  appropriate memory and copy it.  */
274               struct dataset *newp
275                 = (struct dataset *) mempool_alloc (db, total + n, 1);
276               if (newp != NULL)
277                 {
278                   /* Adjust pointer into the memory block.  */
279                   cp = (char *) newp + (cp - (char *) dataset);
280                   key_copy = (char *) newp + (key_copy - (char *) dataset);
281
282                   dataset = memcpy (newp, dataset, total + n);
283                   alloca_used = false;
284                 }
285
286               /* Mark the old record as obsolete.  */
287               dh->usable = false;
288             }
289         }
290       else
291         {
292           /* We write the dataset before inserting it to the database
293              since while inserting this thread might block and so would
294              unnecessarily let the receiver wait.  */
295           assert (fd != -1);
296
297 #ifdef HAVE_SENDFILE
298           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
299             {
300               assert (db->wr_fd != -1);
301               assert ((char *) &dataset->resp > (char *) db->data);
302               assert ((char *) dataset - (char *) db->head
303                       + total
304                       <= (sizeof (struct database_pers_head)
305                           + db->head->module * sizeof (ref_t)
306                           + db->head->data_size));
307               written = sendfileall (fd, db->wr_fd,
308                                      (char *) &dataset->resp
309                                      - (char *) db->head, dataset->head.recsize );
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, dataset->head.recsize);
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 (written != total, 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 (__builtin_expect (debug_level > 0, 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 (__builtin_expect (buflen > 32768, 0))
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 }