Update.
[platform/upstream/glibc.git] / nscd / grpcache.c
1 /* Cache handling for group lookup.
2    Copyright (C) 1998, 1999, 2000, 2001, 2002 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 <errno.h>
22 #include <error.h>
23 #include <grp.h>
24 #include <stdbool.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <libintl.h>
32 #include <stackinfo.h>
33
34 #include "nscd.h"
35 #include "dbg_log.h"
36
37 /* This is the standard reply in case the service is disabled.  */
38 static const gr_response_header disabled =
39 {
40   version: NSCD_VERSION,
41   found: -1,
42   gr_name_len: 0,
43   gr_passwd_len: 0,
44   gr_gid: -1,
45   gr_mem_cnt: 0,
46 };
47
48 /* This is the struct describing how to write this record.  */
49 const struct iovec grp_iov_disabled =
50 {
51   iov_base: (void *) &disabled,
52   iov_len: sizeof (disabled)
53 };
54
55
56 /* This is the standard reply in case we haven't found the dataset.  */
57 static const gr_response_header notfound =
58 {
59   version: NSCD_VERSION,
60   found: 0,
61   gr_name_len: 0,
62   gr_passwd_len: 0,
63   gr_gid: -1,
64   gr_mem_cnt: 0,
65 };
66
67 /* This is the struct describing how to write this record.  */
68 static const struct iovec iov_notfound =
69 {
70   iov_base: (void *) &notfound,
71   iov_len: sizeof (notfound)
72 };
73
74
75 struct groupdata
76 {
77   gr_response_header resp;
78   char strdata[0];
79 };
80
81
82 static void
83 cache_addgr (struct database *db, int fd, request_header *req, void *key,
84              struct group *grp, uid_t owner)
85 {
86   ssize_t total;
87   ssize_t written;
88   time_t t = time (NULL);
89
90   if (grp == NULL)
91     {
92       /* We have no data.  This means we send the standard reply for this
93          case.  */
94       void *copy;
95
96       total = sizeof (notfound);
97
98       written = writev (fd, &iov_notfound, 1);
99
100       copy = malloc (req->key_len);
101       if (copy == NULL)
102         error (EXIT_FAILURE, errno, _("while allocating key copy"));
103       memcpy (copy, key, req->key_len);
104
105       /* Compute the timeout time.  */
106       t += db->negtimeout;
107
108       /* Now get the lock to safely insert the records.  */
109       pthread_rwlock_rdlock (&db->lock);
110
111       cache_add (req->type, copy, req->key_len, &notfound,
112                  sizeof (notfound), (void *) -1, 0, t, db, owner);
113
114       pthread_rwlock_unlock (&db->lock);
115     }
116   else
117     {
118       /* Determine the I/O structure.  */
119       struct groupdata *data;
120       size_t gr_name_len = strlen (grp->gr_name) + 1;
121       size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
122       size_t gr_mem_cnt = 0;
123       uint32_t *gr_mem_len;
124       size_t gr_mem_len_total = 0;
125       char *gr_name;
126       char *cp;
127       char buf[12];
128       ssize_t n;
129       size_t cnt;
130
131       /* We need this to insert the `bygid' entry.  */
132       n = snprintf (buf, sizeof (buf), "%d", grp->gr_gid) + 1;
133
134       /* Determine the length of all members.  */
135       while (grp->gr_mem[gr_mem_cnt])
136         ++gr_mem_cnt;
137       gr_mem_len = (uint32_t *) alloca (gr_mem_cnt * sizeof (uint32_t));
138       for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
139         {
140           gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
141           gr_mem_len_total += gr_mem_len[gr_mem_cnt];
142         }
143
144       /* We allocate all data in one memory block: the iov vector,
145          the response header and the dataset itself.  */
146       total = (sizeof (struct groupdata)
147                + gr_mem_cnt * sizeof (uint32_t)
148                + gr_name_len + gr_passwd_len + gr_mem_len_total);
149       data = (struct groupdata *) malloc (total + n);
150       if (data == NULL)
151         /* There is no reason to go on.  */
152         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
153
154       data->resp.found = 1;
155       data->resp.gr_name_len = gr_name_len;
156       data->resp.gr_passwd_len = gr_passwd_len;
157       data->resp.gr_gid = grp->gr_gid;
158       data->resp.gr_mem_cnt = gr_mem_cnt;
159
160       cp = data->strdata;
161
162       /* This is the member string length array.  */
163       cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
164       gr_name = cp;
165       cp = mempcpy (cp, grp->gr_name, gr_name_len);
166       cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
167
168       for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
169         cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
170
171       /* Finally the stringified GID value.  */
172       memcpy (cp, buf, n);
173
174       /* Write the result.  */
175       written = TEMP_FAILURE_RETRY (write (fd, &data->resp, total));
176
177       /* Compute the timeout time.  */
178       t += db->postimeout;
179
180       /* Now get the lock to safely insert the records.  */
181       pthread_rwlock_rdlock (&db->lock);
182
183       /* We have to add the value for both, byname and byuid.  */
184       cache_add (GETGRBYNAME, gr_name, gr_name_len, data,
185                  total, data, 0, t, db, owner);
186
187       cache_add (GETGRBYGID, cp, n, data, total, data, 1, t, db, owner);
188
189       pthread_rwlock_unlock (&db->lock);
190     }
191
192   if (__builtin_expect (written != total, 0) && debug_level > 0)
193     {
194       char buf[256];
195       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
196                strerror_r (errno, buf, sizeof (buf)));
197     }
198 }
199
200
201 void
202 addgrbyname (struct database *db, int fd, request_header *req,
203              void *key, uid_t uid)
204 {
205   /* Search for the entry matching the key.  Please note that we don't
206      look again in the table whether the dataset is now available.  We
207      simply insert it.  It does not matter if it is in there twice.  The
208      pruning function only will look at the timestamp.  */
209   int buflen = 1024;
210   char *buffer = (char *) alloca (buflen);
211   struct group resultbuf;
212   struct group *grp;
213   uid_t oldeuid = 0;
214   bool use_malloc = false;
215
216   if (__builtin_expect (debug_level > 0, 0))
217     dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key);
218
219   if (secure[grpdb])
220     {
221       oldeuid = geteuid ();
222       seteuid (uid);
223     }
224
225   while (__getgrnam_r (key, &resultbuf, buffer, buflen, &grp) != 0
226          && errno == ERANGE)
227     {
228       char *old_buffer = buffer;
229       errno = 0;
230       buflen += 1024;
231
232       if (__builtin_expect (buflen > 32768, 0))
233         {
234           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
235           if (buffer == NULL)
236             {
237               /* We ran out of memory.  We cannot do anything but
238                  sending a negative response.  In reality this should
239                  never happen.  */
240               grp = NULL;
241               buffer = old_buffer;
242               break;
243             }
244           use_malloc = true;
245         }
246       else
247         {
248           buffer = (char *) alloca (buflen);
249 #if _STACK_GROWS_DOWN
250           if (buffer + buflen == old_buffer)
251             buflen = 2 * buflen - 1024;
252 #elif _STACK_GROWS_UP
253           if (old_buffer + buflen - 1024 == buffer)
254             {
255               buffer = old_buffer;
256               buflen = 2 * buflen - 1024;
257             }
258 #endif
259         }
260     }
261
262   if (secure[grpdb])
263     seteuid (oldeuid);
264
265   cache_addgr (db, fd, req, key, grp, uid);
266
267   if (use_malloc)
268     free (buffer);
269 }
270
271
272 void
273 addgrbygid (struct database *db, int fd, request_header *req,
274             void *key, uid_t uid)
275 {
276   /* Search for the entry matching the key.  Please note that we don't
277      look again in the table whether the dataset is now available.  We
278      simply insert it.  It does not matter if it is in there twice.  The
279      pruning function only will look at the timestamp.  */
280   int buflen = 1024;
281   char *buffer = (char *) alloca (buflen);
282   struct group resultbuf;
283   struct group *grp;
284   uid_t oldeuid = 0;
285   char *ep;
286   gid_t gid = strtoul ((char *)key, &ep, 10);
287   bool use_malloc = false;
288
289   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric gid */
290     {
291       if (debug_level > 0)
292         dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
293
294       errno = EINVAL;
295       return;
296     }
297
298   if (__builtin_expect (debug_level > 0, 0))
299     dbg_log (_("Haven't found \"%d\" in group cache!"), gid);
300
301   if (secure[grpdb])
302     {
303       oldeuid = geteuid ();
304       seteuid (uid);
305     }
306
307   while (__getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0
308          && errno == ERANGE)
309     {
310       char *old_buffer = buffer;
311       errno = 0;
312       buflen += 1024;
313
314       if (__builtin_expect (buflen > 32768, 0))
315         {
316           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
317           if (buffer == NULL)
318             {
319               /* We ran out of memory.  We cannot do anything but
320                  sending a negative response.  In reality this should
321                  never happen.  */
322               grp = NULL;
323               buffer = old_buffer;
324               break;
325             }
326           use_malloc = true;
327         }
328       else
329         {
330           buffer = (char *) alloca (buflen);
331 #if _STACK_GROWS_DOWN
332           if (buffer + buflen == old_buffer)
333             buflen = 2 * buflen - 1024;
334 #elif _STACK_GROWS_UP
335           if (old_buffer + buflen - 1024 == buffer)
336             {
337               buffer = old_buffer;
338               buflen = 2 * buflen - 1024;
339             }
340 #endif
341         }
342     }
343
344   if (secure[grpdb])
345     seteuid (oldeuid);
346
347   cache_addgr (db, fd, req, key, grp, uid);
348
349   if (use_malloc)
350     free (buffer);
351 }