* manual/arith.texi: Document MTASC-safety properties.
[jlayton/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               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
139               dataset->head.recsize = total;
140               dataset->head.notfound = true;
141               dataset->head.nreloads = 0;
142               dataset->head.usable = true;
143
144               /* Compute the timeout time.  */
145               timeout = dataset->head.timeout = t + db->negtimeout;
146
147               /* This is the reply.  */
148               memcpy (&dataset->resp, &notfound, total);
149
150               /* Copy the key data.  */
151               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
152
153               /* If necessary, we also propagate the data to disk.  */
154               if (db->persistent)
155                 {
156                   // XXX async OK?
157                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
158                   msync ((void *) pval,
159                          ((uintptr_t) dataset & pagesize_m1)
160                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
161                 }
162
163               (void) cache_add (req->type, key_copy, req->key_len,
164                                 &dataset->head, true, db, owner, he == NULL);
165
166               pthread_rwlock_unlock (&db->lock);
167
168               /* Mark the old entry as obsolete.  */
169               if (dh != NULL)
170                 dh->usable = false;
171             }
172         }
173     }
174   else
175     {
176       /* Determine the I/O structure.  */
177       size_t pw_name_len = strlen (pwd->pw_name) + 1;
178       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
179       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
180       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
181       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
182       char *cp;
183       const size_t key_len = strlen (key);
184       const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
185       char *buf = alloca (buf_len);
186       ssize_t n;
187
188       /* We need this to insert the `byuid' entry.  */
189       int key_offset;
190       n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
191                     &key_offset, (char *) key) + 1;
192
193       total = (offsetof (struct dataset, strdata)
194                + 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         dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
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       timeout = 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       assert (cp == dataset->strdata + total - offsetof (struct dataset,
252                                                          strdata));
253
254       /* Now we can determine whether on refill we have to create a new
255          record or not.  */
256       if (he != NULL)
257         {
258           assert (fd == -1);
259
260           if (dataset->head.allocsize == dh->allocsize
261               && dataset->head.recsize == dh->recsize
262               && memcmp (&dataset->resp, dh->data,
263                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
264             {
265               /* The data has not changed.  We will just bump the
266                  timeout value.  Note that the new record has been
267                  allocated on the stack and need not be freed.  */
268               dh->timeout = dataset->head.timeout;
269               ++dh->nreloads;
270             }
271           else
272             {
273               /* We have to create a new record.  Just allocate
274                  appropriate memory and copy it.  */
275               struct dataset *newp
276                 = (struct dataset *) mempool_alloc (db, total + n, 1);
277               if (newp != NULL)
278                 {
279                   /* Adjust pointer into the memory block.  */
280                   cp = (char *) newp + (cp - (char *) dataset);
281                   key_copy = (char *) newp + (key_copy - (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) && !alloca_used)
300             {
301               assert (db->wr_fd != -1);
302               assert ((char *) &dataset->resp > (char *) db->data);
303               assert ((char *) dataset - (char *) db->head
304                       + total
305                       <= (sizeof (struct database_pers_head)
306                           + db->head->module * sizeof (ref_t)
307                           + db->head->data_size));
308               ssize_t written = sendfileall (fd, db->wr_fd,
309                                              (char *) &dataset->resp
310                                              - (char *) db->head,
311                                              dataset->head.recsize);
312               if (written != dataset->head.recsize)
313                 {
314 # ifndef __ASSUME_SENDFILE
315                   if (written == -1 && errno == ENOSYS)
316                     goto use_write;
317 # endif
318                   all_written = false;
319                 }
320             }
321           else
322 # ifndef __ASSUME_SENDFILE
323           use_write:
324 # endif
325 #endif
326             if (writeall (fd, &dataset->resp, dataset->head.recsize)
327                 != dataset->head.recsize)
328               all_written = false;
329         }
330
331
332       /* Add the record to the database.  But only if it has not been
333          stored on the stack.  */
334       if (! alloca_used)
335         {
336           /* If necessary, we also propagate the data to disk.  */
337           if (db->persistent)
338             {
339               // XXX async OK?
340               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
341               msync ((void *) pval,
342                      ((uintptr_t) dataset & pagesize_m1) + total + n,
343                      MS_ASYNC);
344             }
345
346           /* NB: in the following code we always must add the entry
347              marked with FIRST first.  Otherwise we end up with
348              dangling "pointers" in case a latter hash entry cannot be
349              added.  */
350           bool first = true;
351
352           /* If the request was by UID, add that entry first.  */
353           if (req->type == GETPWBYUID)
354             {
355               if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
356                              db, owner, he == NULL) < 0)
357                 goto out;
358
359               first = false;
360             }
361           /* If the key is different from the name add a separate entry.  */
362           else if (strcmp (key_copy, dataset->strdata) != 0)
363             {
364               if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
365                              &dataset->head, true, db, owner, he == NULL) < 0)
366                 goto out;
367
368               first = false;
369             }
370
371           /* We have to add the value for both, byname and byuid.  */
372           if ((req->type == GETPWBYNAME || db->propagate)
373               && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
374                                               pw_name_len, &dataset->head,
375                                               first, db, owner, he == NULL)
376                                    == 0, 1))
377             {
378               if (req->type == GETPWBYNAME && db->propagate)
379                 (void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
380                                   false, db, owner, false);
381             }
382
383         out:
384           pthread_rwlock_unlock (&db->lock);
385         }
386     }
387
388   if (__builtin_expect (!all_written, 0) && debug_level > 0)
389     {
390       char buf[256];
391       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
392                strerror_r (errno, buf, sizeof (buf)));
393     }
394
395   return timeout;
396 }
397
398
399 union keytype
400 {
401   void *v;
402   uid_t u;
403 };
404
405
406 static int
407 lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
408         size_t buflen, struct passwd **pwd)
409 {
410   if (type == GETPWBYNAME)
411     return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
412   else
413     return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
414 }
415
416
417 static time_t
418 addpwbyX (struct database_dyn *db, int fd, request_header *req,
419           union keytype key, const char *keystr, uid_t c_uid,
420           struct hashentry *he, struct datahead *dh)
421 {
422   /* Search for the entry matching the key.  Please note that we don't
423      look again in the table whether the dataset is now available.  We
424      simply insert it.  It does not matter if it is in there twice.  The
425      pruning function only will look at the timestamp.  */
426   size_t buflen = 1024;
427   char *buffer = (char *) alloca (buflen);
428   struct passwd resultbuf;
429   struct passwd *pwd;
430   bool use_malloc = false;
431   int errval = 0;
432
433   if (__builtin_expect (debug_level > 0, 0))
434     {
435       if (he == NULL)
436         dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
437       else
438         dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
439     }
440
441   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
442          && (errval = errno) == ERANGE)
443     {
444       errno = 0;
445
446       if (__builtin_expect (buflen > 32768, 0))
447         {
448           char *old_buffer = buffer;
449           buflen *= 2;
450           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
451           if (buffer == NULL)
452             {
453               /* We ran out of memory.  We cannot do anything but
454                  sending a negative response.  In reality this should
455                  never happen.  */
456               pwd = NULL;
457               buffer = old_buffer;
458
459               /* We set the error to indicate this is (possibly) a
460                  temporary error and that it does not mean the entry
461                  is not available at all.  */
462               errval = EAGAIN;
463               break;
464             }
465           use_malloc = true;
466         }
467       else
468         /* Allocate a new buffer on the stack.  If possible combine it
469            with the previously allocated buffer.  */
470         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
471     }
472
473   /* Add the entry to the cache.  */
474   time_t timeout = cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh,
475                                 errval);
476
477   if (use_malloc)
478     free (buffer);
479
480   return timeout;
481 }
482
483
484 void
485 addpwbyname (struct database_dyn *db, int fd, request_header *req,
486              void *key, uid_t c_uid)
487 {
488   union keytype u = { .v = key };
489
490   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
491 }
492
493
494 time_t
495 readdpwbyname (struct database_dyn *db, struct hashentry *he,
496                struct datahead *dh)
497 {
498   request_header req =
499     {
500       .type = GETPWBYNAME,
501       .key_len = he->len
502     };
503   union keytype u = { .v = db->data + he->key };
504
505   return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
506 }
507
508
509 void
510 addpwbyuid (struct database_dyn *db, int fd, request_header *req,
511             void *key, uid_t c_uid)
512 {
513   char *ep;
514   uid_t uid = strtoul ((char *) key, &ep, 10);
515
516   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
517     {
518       if (debug_level > 0)
519         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
520
521       errno = EINVAL;
522       return;
523     }
524
525   union keytype u = { .u = uid };
526
527   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
528 }
529
530
531 time_t
532 readdpwbyuid (struct database_dyn *db, struct hashentry *he,
533               struct datahead *dh)
534 {
535   char *ep;
536   uid_t uid = strtoul (db->data + he->key, &ep, 10);
537
538   /* Since the key has been added before it must be OK.  */
539   assert (*(db->data + he->key) != '\0' && *ep == '\0');
540
541   request_header req =
542     {
543       .type = GETPWBYUID,
544       .key_len = he->len
545     };
546   union keytype u = { .u = uid };
547
548   return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
549 }