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