Update to LGPL v2.1.
[jlayton/glibc.git] / nscd / grpcache.c
1 /* Cache handling for group lookup.
2    Copyright (C) 1998, 1999, 2000 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 <stddef.h>
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <libintl.h>
31
32 #include "nscd.h"
33 #include "dbg_log.h"
34
35 /* This is the standard reply in case the service is disabled.  */
36 static const gr_response_header disabled =
37 {
38   version: NSCD_VERSION,
39   found: -1,
40   gr_name_len: 0,
41   gr_passwd_len: 0,
42   gr_gid: -1,
43   gr_mem_cnt: 0,
44 };
45
46 /* This is the struct describing how to write this record.  */
47 const struct iovec grp_iov_disabled =
48 {
49   iov_base: (void *) &disabled,
50   iov_len: sizeof (disabled)
51 };
52
53
54 /* This is the standard reply in case we haven't found the dataset.  */
55 static const gr_response_header notfound =
56 {
57   version: NSCD_VERSION,
58   found: 0,
59   gr_name_len: 0,
60   gr_passwd_len: 0,
61   gr_gid: -1,
62   gr_mem_cnt: 0,
63 };
64
65 /* This is the struct describing how to write this record.  */
66 static const struct iovec iov_notfound =
67 {
68   iov_base: (void *) &notfound,
69   iov_len: sizeof (notfound)
70 };
71
72
73 struct groupdata
74 {
75   gr_response_header resp;
76   char strdata[0];
77 };
78
79
80 static void
81 cache_addgr (struct database *db, int fd, request_header *req, void *key,
82              struct group *grp, uid_t owner)
83 {
84   ssize_t total;
85   ssize_t written;
86   time_t t = time (NULL);
87
88   if (grp == NULL)
89     {
90       /* We have no data.  This means we send the standard reply for this
91          case.  */
92       void *copy;
93
94       total = sizeof (notfound);
95
96       written = writev (fd, &iov_notfound, 1);
97
98       copy = malloc (req->key_len);
99       if (copy == NULL)
100         error (EXIT_FAILURE, errno, _("while allocating key copy"));
101       memcpy (copy, key, req->key_len);
102
103       /* Compute the timeout time.  */
104       t += db->negtimeout;
105
106       /* Now get the lock to safely insert the records.  */
107       pthread_rwlock_rdlock (&db->lock);
108
109       cache_add (req->type, copy, req->key_len, &notfound,
110                  sizeof (notfound), (void *) -1, 0, t, db, owner);
111
112       pthread_rwlock_unlock (&db->lock);
113     }
114   else
115     {
116       /* Determine the I/O structure.  */
117       struct groupdata *data;
118       size_t gr_name_len = strlen (grp->gr_name) + 1;
119       size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
120       size_t gr_mem_cnt = 0;
121       uint32_t *gr_mem_len;
122       size_t gr_mem_len_total = 0;
123       char *gr_name;
124       char *cp;
125       char buf[12];
126       ssize_t n;
127       size_t cnt;
128
129       /* We need this to insert the `bygid' entry.  */
130       n = snprintf (buf, sizeof (buf), "%d", grp->gr_gid) + 1;
131
132       /* Determine the length of all members.  */
133       while (grp->gr_mem[gr_mem_cnt])
134         ++gr_mem_cnt;
135       gr_mem_len = (uint32_t *) alloca (gr_mem_cnt * sizeof (uint32_t));
136       for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
137         {
138           gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
139           gr_mem_len_total += gr_mem_len[gr_mem_cnt];
140         }
141
142       /* We allocate all data in one memory block: the iov vector,
143          the response header and the dataset itself.  */
144       total = (sizeof (struct groupdata)
145                + gr_mem_cnt * sizeof (uint32_t)
146                + gr_name_len + gr_passwd_len + gr_mem_len_total);
147       data = (struct groupdata *) malloc (total + n);
148       if (data == NULL)
149         /* There is no reason to go on.  */
150         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
151
152       data->resp.found = 1;
153       data->resp.gr_name_len = gr_name_len;
154       data->resp.gr_passwd_len = gr_passwd_len;
155       data->resp.gr_gid = grp->gr_gid;
156       data->resp.gr_mem_cnt = gr_mem_cnt;
157
158       cp = data->strdata;
159
160       /* This is the member string length array.  */
161       cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
162       gr_name = cp;
163       cp = mempcpy (cp, grp->gr_name, gr_name_len);
164       cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
165
166       for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
167         cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
168
169       /* Finally the stringified GID value.  */
170       memcpy (cp, buf, n);
171
172       /* Write the result.  */
173       written = 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       cache_add (GETGRBYGID, cp, n, data, total, data, 1, t, db, owner);
186
187       pthread_rwlock_unlock (&db->lock);
188     }
189
190   if (written != total)
191     {
192       char buf[256];
193       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
194                strerror_r (errno, buf, sizeof (buf)));
195     }
196 }
197
198
199 void
200 addgrbyname (struct database *db, int fd, request_header *req,
201              void *key, uid_t uid)
202 {
203   /* Search for the entry matching the key.  Please note that we don't
204      look again in the table whether the dataset is now available.  We
205      simply insert it.  It does not matter if it is in there twice.  The
206      pruning function only will look at the timestamp.  */
207   int buflen = 256;
208   char *buffer = alloca (buflen);
209   struct group resultbuf;
210   struct group *grp;
211   uid_t oldeuid = 0;
212
213   if (debug_level > 0)
214     dbg_log (_("Haven't found \"%s\" in group cache!"), (char *)key);
215
216   if (secure[grpdb])
217     {
218       oldeuid = geteuid ();
219       seteuid (uid);
220     }
221
222   while (__getgrnam_r (key, &resultbuf, buffer, buflen, &grp) != 0
223          && errno == ERANGE)
224     {
225       errno = 0;
226       buflen += 256;
227       buffer = alloca (buflen);
228     }
229
230   if (secure[grpdb])
231     seteuid (oldeuid);
232
233   cache_addgr (db, fd, req, key, grp, uid);
234 }
235
236
237 void
238 addgrbygid (struct database *db, int fd, request_header *req,
239             void *key, uid_t uid)
240 {
241   /* Search for the entry matching the key.  Please note that we don't
242      look again in the table whether the dataset is now available.  We
243      simply insert it.  It does not matter if it is in there twice.  The
244      pruning function only will look at the timestamp.  */
245   int buflen = 256;
246   char *buffer = alloca (buflen);
247   struct group resultbuf;
248   struct group *grp;
249   gid_t gid = atol (key);
250   uid_t oldeuid = 0;
251
252   if (debug_level > 0)
253     dbg_log (_("Haven't found \"%d\" in group cache!"), gid);
254
255   if (secure[grpdb])
256     {
257       oldeuid = geteuid ();
258       seteuid (uid);
259     }
260
261   while (__getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0
262          && errno == ERANGE)
263     {
264       errno = 0;
265       buflen += 256;
266       buffer = alloca (buflen);
267     }
268
269   if (secure[grpdb])
270     seteuid (oldeuid);
271
272   cache_addgr (db, fd, req, key, grp, uid);
273 }