Update.
[glibc.git] / nscd / pwdcache.c
blobcae33033e965860cce945987c4f97a1d5bc35601
1 /* Cache handling for passwd lookup.
2 Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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.
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.
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. */
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>
32 #include "nscd.h"
33 #include "dbg_log.h"
35 /* This is the standard reply in case the service is disabled. */
36 static const pw_response_header disabled =
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
49 /* This is the struct describing how to write this record. */
50 const struct iovec pwd_iov_disabled =
52 iov_base: (void *) &disabled,
53 iov_len: sizeof (disabled)
57 /* This is the standard reply in case we haven't found the dataset. */
58 static const pw_response_header notfound =
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
71 /* This is the struct describing how to write this record. */
72 static const struct iovec iov_notfound =
74 iov_base: (void *) &notfound,
75 iov_len: sizeof (notfound)
79 struct passwddata
81 pw_response_header resp;
82 char strdata[0];
86 static void
87 cache_addpw (struct database *db, int fd, request_header *req, void *key,
88 struct passwd *pwd, uid_t owner)
90 ssize_t total;
91 ssize_t written;
92 time_t t = time (NULL);
94 if (pwd == NULL)
96 /* We have no data. This means we send the standard reply for this
97 case. */
98 void *copy;
100 total = sizeof (notfound);
102 written = writev (fd, &iov_notfound, 1);
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);
109 /* Compute the timeout time. */
110 t += db->negtimeout;
112 /* Now get the lock to safely insert the records. */
113 pthread_rwlock_rdlock (&db->lock);
115 cache_add (req->type, copy, req->key_len, &notfound,
116 sizeof (notfound), (void *) -1, 0, t, db, owner);
118 pthread_rwlock_unlock (&db->lock);
120 else
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;
133 /* We need this to insert the `byuid' entry. */
134 n = snprintf (buf, sizeof (buf), "%d", pwd->pw_uid) + 1;
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"));
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;
154 cp = data->strdata;
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);
163 /* Finally the stringified UID value. */
164 memcpy (cp, buf, n);
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 = TEMP_FAILURE_RETRY (write (fd, &data->resp, total));
171 /* Compute the timeout time. */
172 t += db->postimeout;
174 /* Now get the lock to safely insert the records. */
175 pthread_rwlock_rdlock (&db->lock);
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);
181 cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db, owner);
183 pthread_rwlock_unlock (&db->lock);
186 if (__builtin_expect (written != total, 0) && debug_level > 0)
188 char buf[256];
189 dbg_log (_("short write in %s: %s"), __FUNCTION__,
190 strerror_r (errno, buf, sizeof (buf)));
195 void
196 addpwbyname (struct database *db, int fd, request_header *req,
197 void *key, uid_t c_uid)
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 = 0;
209 if (debug_level > 0)
210 dbg_log (_("Haven't found \"%s\" in password cache!"), (char *)key);
212 if (secure[pwddb])
214 oldeuid = geteuid ();
215 seteuid (c_uid);
218 while (__getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
219 && errno == ERANGE)
221 errno = 0;
222 buflen += 256;
223 buffer = alloca (buflen);
226 if (secure[pwddb])
227 seteuid (oldeuid);
229 cache_addpw (db, fd, req, key, pwd, c_uid);
233 void
234 addpwbyuid (struct database *db, int fd, request_header *req,
235 void *key, uid_t c_uid)
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 oldeuid = 0;
246 char *ep;
247 uid_t uid = strtoul ((char*) key, &ep, 10);
249 if (*(char*)key == '\0' || *ep != '\0') /* invalid numeric uid */
251 if (debug_level > 0)
252 dbg_log (_("Invalid numeric uid \"%s\"!"), (char *)key);
254 errno = EINVAL;
255 return;
258 if (debug_level > 0)
259 dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
261 if (secure[pwddb])
263 oldeuid = geteuid ();
264 seteuid (c_uid);
267 while (__getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
268 && errno == ERANGE)
270 errno = 0;
271 buflen += 256;
272 buffer = alloca (buflen);
275 if (secure[pwddb])
276 seteuid (oldeuid);
278 cache_addpw (db, fd, req, key, pwd, c_uid);