(nscd_gethst_r): Make sure resultbuf->h_addr_list addresses are
[kopensolaris-gnu/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd 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 <pwd.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.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 pw_response_header disabled =
37 {
38   version: NSCD_VERSION,
39   found: -1,
40   pw_name_len: 0,
41   pw_passwd_len: 0,
42   pw_uid: -1,
43   pw_gid: -1,
44   pw_gecos_len: 0,
45   pw_dir_len: 0,
46   pw_shell_len: 0
47 };
48
49 /* This is the struct describing how to write this record.  */
50 const struct iovec pwd_iov_disabled =
51 {
52   iov_base: (void *) &disabled,
53   iov_len: sizeof (disabled)
54 };
55
56
57 /* This is the standard reply in case we haven't found the dataset.  */
58 static const pw_response_header notfound =
59 {
60   version: NSCD_VERSION,
61   found: 0,
62   pw_name_len: 0,
63   pw_passwd_len: 0,
64   pw_uid: -1,
65   pw_gid: -1,
66   pw_gecos_len: 0,
67   pw_dir_len: 0,
68   pw_shell_len: 0
69 };
70
71 /* This is the struct describing how to write this record.  */
72 static const struct iovec iov_notfound =
73 {
74   iov_base: (void *) &notfound,
75   iov_len: sizeof (notfound)
76 };
77
78
79 struct passwddata
80 {
81   pw_response_header resp;
82   char strdata[0];
83 };
84
85
86 static void
87 cache_addpw (struct database *db, int fd, request_header *req, void *key,
88              struct passwd *pwd, uid_t owner)
89 {
90   ssize_t total;
91   ssize_t written;
92   time_t t = time (NULL);
93
94   if (pwd == NULL)
95     {
96       /* We have no data.  This means we send the standard reply for this
97          case.  */
98       void *copy;
99
100       total = sizeof (notfound);
101
102       written = writev (fd, &iov_notfound, 1);
103
104       copy = malloc (req->key_len);
105       if (copy == NULL)
106         error (EXIT_FAILURE, errno, _("while allocating key copy"));
107       memcpy (copy, key, req->key_len);
108
109       /* Compute the timeout time.  */
110       t += db->negtimeout;
111
112       /* Now get the lock to safely insert the records.  */
113       pthread_rwlock_rdlock (&db->lock);
114
115       cache_add (req->type, copy, req->key_len, &notfound,
116                  sizeof (notfound), (void *) -1, 0, t, db, owner);
117
118       pthread_rwlock_unlock (&db->lock);
119     }
120   else
121     {
122       /* Determine the I/O structure.  */
123       struct passwddata *data;
124       size_t pw_name_len = strlen (pwd->pw_name) + 1;
125       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
126       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
127       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
128       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
129       char *cp;
130       char buf[12];
131       ssize_t n;
132
133       /* We need this to insert the `byuid' entry.  */
134       n = snprintf (buf, sizeof (buf), "%d", pwd->pw_uid) + 1;
135
136       /* We allocate all data in one memory block: the iov vector,
137          the response header and the dataset itself.  */
138       total = (sizeof (struct passwddata) + pw_name_len + pw_passwd_len
139                + pw_gecos_len + pw_dir_len + pw_shell_len);
140       data = (struct passwddata *) malloc (total + n);
141       if (data == NULL)
142         /* There is no reason to go on.  */
143         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
144
145       data->resp.found = 1;
146       data->resp.pw_name_len = pw_name_len;
147       data->resp.pw_passwd_len = pw_passwd_len;
148       data->resp.pw_uid = pwd->pw_uid;
149       data->resp.pw_gid = pwd->pw_gid;
150       data->resp.pw_gecos_len = pw_gecos_len;
151       data->resp.pw_dir_len = pw_dir_len;
152       data->resp.pw_shell_len = pw_shell_len;
153
154       cp = data->strdata;
155
156       /* Copy the strings over into the buffer.  */
157       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
158       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
159       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
160       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
161       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
162
163       /* Finally the stringified UID value.  */
164       memcpy (cp, buf, n);
165
166       /* We write the dataset before inserting it to the database
167          since while inserting this thread might block and so would
168          unnecessarily let the receiver wait.  */
169       written = write (fd, &data->resp, total);
170
171       /* Compute the timeout time.  */
172       t += db->postimeout;
173
174       /* Now get the lock to safely insert the records.  */
175       pthread_rwlock_rdlock (&db->lock);
176
177       /* We have to add the value for both, byname and byuid.  */
178       cache_add (GETPWBYNAME, data->strdata, pw_name_len, data,
179                  total, data, 0, t, db, owner);
180
181       cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db, owner);
182
183       pthread_rwlock_unlock (&db->lock);
184     }
185
186   if (written != total)
187     {
188       char buf[256];
189       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
190                strerror_r (errno, buf, sizeof (buf)));
191     }
192 }
193
194
195 void
196 addpwbyname (struct database *db, int fd, request_header *req,
197              void *key, uid_t c_uid)
198 {
199   /* Search for the entry matching the key.  Please note that we don't
200      look again in the table whether the dataset is now available.  We
201      simply insert it.  It does not matter if it is in there twice.  The
202      pruning function only will look at the timestamp.  */
203   int buflen = 256;
204   char *buffer = alloca (buflen);
205   struct passwd resultbuf;
206   struct passwd *pwd;
207   uid_t oldeuid;
208
209   if (debug_level > 0)
210     dbg_log (_("Haven't found \"%s\" in password cache!"), key);
211
212   if (secure[pwddb])
213     {
214       oldeuid = geteuid ();
215       seteuid (c_uid);
216     }
217
218   while (getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
219          && errno == ERANGE)
220     {
221       errno = 0;
222       buflen += 256;
223       buffer = alloca (buflen);
224     }
225
226   if (secure[pwddb])
227     seteuid (c_uid);
228
229   cache_addpw (db, fd, req, key, pwd, c_uid);
230 }
231
232
233 void
234 addpwbyuid (struct database *db, int fd, request_header *req,
235             void *key, uid_t c_uid)
236 {
237   /* Search for the entry matching the key.  Please note that we don't
238      look again in the table whether the dataset is now available.  We
239      simply insert it.  It does not matter if it is in there twice.  The
240      pruning function only will look at the timestamp.  */
241   int buflen = 256;
242   char *buffer = alloca (buflen);
243   struct passwd resultbuf;
244   struct passwd *pwd;
245   uid_t uid = atol (key);
246   uid_t oldeuid = 0;
247
248   if (debug_level > 0)
249     dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
250
251   if (secure[pwddb])
252     {
253       oldeuid = geteuid ();
254       seteuid (c_uid);
255     }
256
257   while (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
258          && errno == ERANGE)
259     {
260       errno = 0;
261       buflen += 256;
262       buffer = alloca (buflen);
263     }
264
265   if (secure[pwddb])
266     seteuid (oldeuid);
267
268   cache_addpw (db, fd, req, key, pwd, c_uid);
269 }