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