f6ca001a87f232656050ded31ad1ca4fb18c953a
[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.version = NSCD_VERSION;
149       data->resp.found = 1;
150       data->resp.pw_name_len = pw_name_len;
151       data->resp.pw_passwd_len = pw_passwd_len;
152       data->resp.pw_uid = pwd->pw_uid;
153       data->resp.pw_gid = pwd->pw_gid;
154       data->resp.pw_gecos_len = pw_gecos_len;
155       data->resp.pw_dir_len = pw_dir_len;
156       data->resp.pw_shell_len = pw_shell_len;
157
158       cp = data->strdata;
159
160       /* Copy the strings over into the buffer.  */
161       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
162       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
163       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
164       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
165       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
166
167       /* Finally the stringified UID value.  */
168       memcpy (cp, buf, n);
169
170       /* We write the dataset before inserting it to the database
171          since while inserting this thread might block and so would
172          unnecessarily let the receiver wait.  */
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 (GETPWBYNAME, data->strdata, pw_name_len, data,
183                  total, data, 0, t, db, owner);
184
185       cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db, owner);
186
187       pthread_rwlock_unlock (&db->lock);
188     }
189
190   if (__builtin_expect (written != total, 0) && debug_level > 0)
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 addpwbyname (struct database *db, int fd, request_header *req,
201              void *key, uid_t c_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 = 1024;
208   char *buffer = (char *) alloca (buflen);
209   struct passwd resultbuf;
210   struct passwd *pwd;
211   uid_t oldeuid = 0;
212   bool use_malloc = false;
213
214   if (__builtin_expect (debug_level > 0, 0))
215     dbg_log (_("Haven't found \"%s\" in password cache!"), (char *) key);
216
217   if (secure[pwddb])
218     {
219       oldeuid = geteuid ();
220       seteuid (c_uid);
221     }
222
223   while (__getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
224          && errno == ERANGE)
225     {
226       char *old_buffer = buffer;
227       errno = 0;
228 #define INCR 1024
229
230       if (__builtin_expect (buflen > 32768, 0))
231         {
232           buflen += INCR;
233           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
234           if (buffer == NULL)
235             {
236               /* We ran out of memory.  We cannot do anything but
237                  sending a negative response.  In reality this should
238                  never happen.  */
239               pwd = NULL;
240               buffer = old_buffer;
241               break;
242             }
243           use_malloc = true;
244         }
245       else
246         /* Allocate a new buffer on the stack.  If possible combine it
247            with the previously allocated buffer.  */
248         buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
249     }
250
251   if (secure[pwddb])
252     seteuid (oldeuid);
253
254   cache_addpw (db, fd, req, key, pwd, c_uid);
255
256   if (use_malloc)
257     free (buffer);
258 }
259
260
261 void
262 addpwbyuid (struct database *db, int fd, request_header *req,
263             void *key, uid_t c_uid)
264 {
265   /* Search for the entry matching the key.  Please note that we don't
266      look again in the table whether the dataset is now available.  We
267      simply insert it.  It does not matter if it is in there twice.  The
268      pruning function only will look at the timestamp.  */
269   int buflen = 256;
270   char *buffer = (char *) alloca (buflen);
271   struct passwd resultbuf;
272   struct passwd *pwd;
273   uid_t oldeuid = 0;
274   char *ep;
275   uid_t uid = strtoul ((char *) key, &ep, 10);
276   bool use_malloc = false;
277
278   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
279     {
280       if (debug_level > 0)
281         dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
282
283       errno = EINVAL;
284       return;
285     }
286
287   if (__builtin_expect (debug_level > 0, 0))
288     dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
289
290   if (secure[pwddb])
291     {
292       oldeuid = geteuid ();
293       seteuid (c_uid);
294     }
295
296   while (__getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
297          && errno == ERANGE)
298     {
299       char *old_buffer = buffer;
300       errno = 0;
301
302       if (__builtin_expect (buflen > 32768, 0))
303         {
304           buflen += 1024;
305           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
306           if (buffer == NULL)
307             {
308               /* We ran out of memory.  We cannot do anything but
309                  sending a negative response.  In reality this should
310                  never happen.  */
311               pwd = NULL;
312               buffer = old_buffer;
313               break;
314             }
315           use_malloc = true;
316         }
317       else
318         /* Allocate a new buffer on the stack.  If possible combine it
319            with the previously allocated buffer.  */
320         buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
321     }
322
323   if (secure[pwddb])
324     seteuid (oldeuid);
325
326   cache_addpw (db, fd, req, key, pwd, c_uid);
327
328   if (use_malloc)
329     free (buffer);
330 }