(handle_request): Check access SELinux permissions before processing request.
[kopensolaris-gnu/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd lookup.
2    Copyright (C) 1998-2002, 2003, 2004 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    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library 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 GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <alloca.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <error.h>
25 #include <libintl.h>
26 #include <pwd.h>
27 #include <stdbool.h>
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <sys/mman.h>
35 #include <stackinfo.h>
36
37 #include "nscd.h"
38 #include "dbg_log.h"
39
40 /* This is the standard reply in case the service is disabled.  */
41 static const pw_response_header disabled =
42 {
43   .version = NSCD_VERSION,
44   .found = -1,
45   .pw_name_len = 0,
46   .pw_passwd_len = 0,
47   .pw_uid = -1,
48   .pw_gid = -1,
49   .pw_gecos_len = 0,
50   .pw_dir_len = 0,
51   .pw_shell_len = 0
52 };
53
54 /* This is the struct describing how to write this record.  */
55 const struct iovec pwd_iov_disabled =
56 {
57   .iov_base = (void *) &disabled,
58   .iov_len = sizeof (disabled)
59 };
60
61
62 /* This is the standard reply in case we haven't found the dataset.  */
63 static const pw_response_header notfound =
64 {
65   .version = NSCD_VERSION,
66   .found = 0,
67   .pw_name_len = 0,
68   .pw_passwd_len = 0,
69   .pw_uid = -1,
70   .pw_gid = -1,
71   .pw_gecos_len = 0,
72   .pw_dir_len = 0,
73   .pw_shell_len = 0
74 };
75
76
77 static void
78 cache_addpw (struct database_dyn *db, int fd, request_header *req,
79              const void *key, struct passwd *pwd, uid_t owner,
80              struct hashentry *he, struct datahead *dh, int errval)
81 {
82   ssize_t total;
83   ssize_t written;
84   time_t t = time (NULL);
85
86   /* We allocate all data in one memory block: the iov vector,
87      the response header and the dataset itself.  */
88   struct dataset
89   {
90     struct datahead head;
91     pw_response_header resp;
92     char strdata[0];
93   } *dataset;
94
95   assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
96
97   if (pwd == NULL)
98     {
99       if (he != NULL && errval == EAGAIN)
100         {
101           /* If we have an old record available but cannot find one
102              now because the service is not available we keep the old
103              record and make sure it does not get removed.  */
104           if (reload_count != UINT_MAX && dh->nreloads == reload_count)
105             /* Do not reset the value if we never not reload the record.  */
106             dh->nreloads = reload_count - 1;
107
108           written = total = 0;
109         }
110       else
111         {
112           /* We have no data.  This means we send the standard reply for this
113              case.  */
114           written = total = sizeof (notfound);
115
116           if (fd != -1)
117             written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
118
119           dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
120           /* If we cannot permanently store the result, so be it.  */
121           if (dataset != NULL)
122             {
123               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
124               dataset->head.recsize = total;
125               dataset->head.notfound = true;
126               dataset->head.nreloads = 0;
127               dataset->head.usable = true;
128
129               /* Compute the timeout time.  */
130               dataset->head.timeout = t + db->negtimeout;
131
132               /* This is the reply.  */
133               memcpy (&dataset->resp, &notfound, total);
134
135               /* Copy the key data.  */
136               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
137
138               /* If necessary, we also propagate the data to disk.  */
139               if (db->persistent)
140                 {
141                   // XXX async OK?
142                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
143                   msync ((void *) pval,
144                          ((uintptr_t) dataset & pagesize_m1)
145                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
146                 }
147
148               /* Now get the lock to safely insert the records.  */
149               pthread_rwlock_rdlock (&db->lock);
150
151               if (cache_add (req->type, key_copy, req->key_len,
152                              &dataset->head, true, db, owner) < 0)
153                 /* Ensure the data can be recovered.  */
154                 dataset->head.usable = false;
155
156
157               pthread_rwlock_unlock (&db->lock);
158
159               /* Mark the old entry as obsolete.  */
160               if (dh != NULL)
161                 dh->usable = false;
162             }
163           else
164             ++db->head->addfailed;
165         }
166     }
167   else
168     {
169       /* Determine the I/O structure.  */
170       size_t pw_name_len = strlen (pwd->pw_name) + 1;
171       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
172       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
173       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
174       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
175       char *cp;
176       const size_t key_len = strlen (key);
177       const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
178       char *buf = alloca (buf_len);
179       ssize_t n;
180
181       /* We need this to insert the `byuid' entry.  */
182       int key_offset;
183       n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
184                     &key_offset, (char *) key) + 1;
185
186       written = total = (sizeof (struct dataset) + pw_name_len + pw_passwd_len
187                          + pw_gecos_len + pw_dir_len + pw_shell_len);
188
189       /* If we refill the cache, first assume the reconrd did not
190          change.  Allocate memory on the cache since it is likely
191          discarded anyway.  If it turns out to be necessary to have a
192          new record we can still allocate real memory.  */
193       bool alloca_used = false;
194       dataset = NULL;
195
196       if (he == NULL)
197         {
198           dataset = (struct dataset *) mempool_alloc (db, total + n);
199           if (dataset == NULL)
200             ++db->head->addfailed;
201         }
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       dataset->head.allocsize = total + n;
215       dataset->head.recsize = total - offsetof (struct dataset, resp);
216       dataset->head.notfound = false;
217       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
218       dataset->head.usable = true;
219
220       /* Compute the timeout time.  */
221       dataset->head.timeout = t + db->postimeout;
222
223       dataset->resp.version = NSCD_VERSION;
224       dataset->resp.found = 1;
225       dataset->resp.pw_name_len = pw_name_len;
226       dataset->resp.pw_passwd_len = pw_passwd_len;
227       dataset->resp.pw_uid = pwd->pw_uid;
228       dataset->resp.pw_gid = pwd->pw_gid;
229       dataset->resp.pw_gecos_len = pw_gecos_len;
230       dataset->resp.pw_dir_len = pw_dir_len;
231       dataset->resp.pw_shell_len = pw_shell_len;
232
233       cp = dataset->strdata;
234
235       /* Copy the strings over into the buffer.  */
236       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
237       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
238       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
239       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
240       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
241
242       /* Finally the stringified UID value.  */
243       memcpy (cp, buf, n);
244       char *key_copy = cp + key_offset;
245       assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
246
247       /* Now we can determine whether on refill we have to create a new
248          record or not.  */
249       if (he != NULL)
250         {
251           assert (fd == -1);
252
253           if (total + n == dh->allocsize
254               && total - offsetof (struct dataset, resp) == dh->recsize
255               && memcmp (&dataset->resp, dh->data,
256                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
257             {
258               /* The data has not changed.  We will just bump the
259                  timeout value.  Note that the new record has been
260                  allocated on the stack and need not be freed.  */
261               dh->timeout = dataset->head.timeout;
262               ++dh->nreloads;
263             }
264           else
265             {
266               /* We have to create a new record.  Just allocate
267                  appropriate memory and copy it.  */
268               struct dataset *newp
269                 = (struct dataset *) mempool_alloc (db, total + n);
270               if (newp != NULL)
271                 {
272                   /* Adjust pointer into the memory block.  */
273                   cp = (char *) newp + (cp - (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           written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
291         }
292
293
294       /* Add the record to the database.  But only if it has not been
295          stored on the stack.  */
296       if (! alloca_used)
297         {
298           /* If necessary, we also propagate the data to disk.  */
299           if (db->persistent)
300             {
301               // XXX async OK?
302               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
303               msync ((void *) pval,
304                      ((uintptr_t) dataset & pagesize_m1) + total + n,
305                      MS_ASYNC);
306             }
307
308           /* Now get the lock to safely insert the records.  */
309           pthread_rwlock_rdlock (&db->lock);
310
311           /* NB: in the following code we always must add the entry
312              marked with FIRST first.  Otherwise we end up with
313              dangling "pointers" in case a latter hash entry cannot be
314              added.  */
315           bool first = req->type == GETPWBYNAME;
316
317           /* If the request was by UID, add that entry first.  */
318           if (req->type != GETPWBYNAME)
319             {
320               if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
321                              db, owner) < 0)
322                 {
323                   /* Could not allocate memory.  Make sure the data gets
324                      discarded.  */
325                   dataset->head.usable = false;
326                   goto out;
327                 }
328             }
329           /* If the key is different from the name add a separate entry.  */
330           else if (strcmp (key_copy, dataset->strdata) != 0)
331             {
332               if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
333                              &dataset->head, first, db, owner) < 0)
334                 {
335                   /* Could not allocate memory.  Make sure the data gets
336                      discarded.  */
337                   dataset->head.usable = false;
338                   goto out;
339                 }
340
341               first = false;
342             }
343
344           /* We have to add the value for both, byname and byuid.  */
345           if (__builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
346                                            pw_name_len, &dataset->head, first,
347                                            db, owner) == 0, 1))
348             {
349               if (req->type == GETPWBYNAME)
350                 (void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
351                                   req->type != GETPWBYNAME, db, owner);
352             }
353           else if (first)
354             /* Could not allocate memory.  Make sure the data gets
355                discarded.  */
356             dataset->head.usable = false;
357
358         out:
359           pthread_rwlock_unlock (&db->lock);
360         }
361     }
362
363   if (__builtin_expect (written != total, 0) && debug_level > 0)
364     {
365       char buf[256];
366       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
367                strerror_r (errno, buf, sizeof (buf)));
368     }
369 }
370
371
372 union keytype
373 {
374   void *v;
375   uid_t u;
376 };
377
378
379 static int
380 lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
381         size_t buflen, struct passwd **pwd)
382 {
383   if (type == GETPWBYNAME)
384     return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
385   else
386     return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
387 }
388
389
390 static void
391 addpwbyX (struct database_dyn *db, int fd, request_header *req,
392           union keytype key, const char *keystr, uid_t c_uid,
393           struct hashentry *he, struct datahead *dh)
394 {
395   /* Search for the entry matching the key.  Please note that we don't
396      look again in the table whether the dataset is now available.  We
397      simply insert it.  It does not matter if it is in there twice.  The
398      pruning function only will look at the timestamp.  */
399   size_t buflen = 1024;
400   char *buffer = (char *) alloca (buflen);
401   struct passwd resultbuf;
402   struct passwd *pwd;
403   uid_t oldeuid = 0;
404   bool use_malloc = false;
405   int errval = 0;
406
407   if (__builtin_expect (debug_level > 0, 0))
408     {
409       if (he == NULL)
410         dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
411       else
412         dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
413     }
414
415   if (db->secure)
416     {
417       oldeuid = geteuid ();
418       seteuid (c_uid);
419     }
420
421   while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
422          && (errval = errno) == ERANGE)
423     {
424       char *old_buffer = buffer;
425       errno = 0;
426 #define INCR 1024
427
428       if (__builtin_expect (buflen > 32768, 0))
429         {
430           buflen += INCR;
431           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
432           if (buffer == NULL)
433             {
434               /* We ran out of memory.  We cannot do anything but
435                  sending a negative response.  In reality this should
436                  never happen.  */
437               pwd = NULL;
438               buffer = old_buffer;
439
440               /* We set the error to indicate this is (possibly) a
441                  temporary error and that it does not mean the entry
442                  is not available at all.  */
443               errval = EAGAIN;
444               break;
445             }
446           use_malloc = true;
447         }
448       else
449         /* Allocate a new buffer on the stack.  If possible combine it
450            with the previously allocated buffer.  */
451         buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
452     }
453
454   if (db->secure)
455     seteuid (oldeuid);
456
457   /* Add the entry to the cache.  */
458   cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, errval);
459
460   if (use_malloc)
461     free (buffer);
462 }
463
464
465 void
466 addpwbyname (struct database_dyn *db, int fd, request_header *req,
467              void *key, uid_t c_uid)
468 {
469   union keytype u = { .v = key };
470
471   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
472 }
473
474
475 void
476 readdpwbyname (struct database_dyn *db, struct hashentry *he,
477                struct datahead *dh)
478 {
479   request_header req =
480     {
481       .type = GETPWBYNAME,
482       .key_len = he->len
483     };
484   union keytype u = { .v = db->data + he->key };
485
486   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
487 }
488
489
490 void
491 addpwbyuid (struct database_dyn *db, int fd, request_header *req,
492             void *key, uid_t c_uid)
493 {
494   char *ep;
495   uid_t uid = strtoul ((char *) key, &ep, 10);
496
497   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
498     {
499       if (debug_level > 0)
500         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
501
502       errno = EINVAL;
503       return;
504     }
505
506   union keytype u = { .u = uid };
507
508   addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
509 }
510
511
512 void
513 readdpwbyuid (struct database_dyn *db, struct hashentry *he,
514               struct datahead *dh)
515 {
516   char *ep;
517   uid_t uid = strtoul (db->data + he->key, &ep, 10);
518
519   /* Since the key has been added before it must be OK.  */
520   assert (*(db->data + he->key) != '\0' && *ep == '\0');
521
522   request_header req =
523     {
524       .type = GETPWBYUID,
525       .key_len = he->len
526     };
527   union keytype u = { .u = uid };
528
529   addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
530 }