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