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