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