Add hidden_def.
[glibc.git] / nscd / pwdcache.c
blobf6ca001a87f232656050ded31ad1ca4fb18c953a
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.
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 <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>
35 #include "nscd.h"
36 #include "dbg_log.h"
38 /* This is the standard reply in case the service is disabled. */
39 static const pw_response_header disabled =
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
52 /* This is the struct describing how to write this record. */
53 const struct iovec pwd_iov_disabled =
55 .iov_base = (void *) &disabled,
56 .iov_len = sizeof (disabled)
60 /* This is the standard reply in case we haven't found the dataset. */
61 static const pw_response_header notfound =
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
74 /* This is the struct describing how to write this record. */
75 static const struct iovec iov_notfound =
77 .iov_base = (void *) &notfound,
78 .iov_len = sizeof (notfound)
82 struct passwddata
84 pw_response_header resp;
85 char strdata[0];
89 static void
90 cache_addpw (struct database *db, int fd, request_header *req, void *key,
91 struct passwd *pwd, uid_t owner)
93 ssize_t total;
94 ssize_t written;
95 time_t t = time (NULL);
97 if (pwd == NULL)
99 /* We have no data. This means we send the standard reply for this
100 case. */
101 void *copy;
103 total = sizeof (notfound);
105 written = TEMP_FAILURE_RETRY (writev (fd, &iov_notfound, 1));
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);
112 /* Compute the timeout time. */
113 t += db->negtimeout;
115 /* Now get the lock to safely insert the records. */
116 pthread_rwlock_rdlock (&db->lock);
118 cache_add (req->type, copy, req->key_len, &notfound,
119 sizeof (notfound), (void *) -1, 0, t, db, owner);
121 pthread_rwlock_unlock (&db->lock);
123 else
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;
136 /* We need this to insert the `byuid' entry. */
137 n = snprintf (buf, sizeof (buf), "%d", pwd->pw_uid) + 1;
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"));
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;
158 cp = data->strdata;
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);
167 /* Finally the stringified UID value. */
168 memcpy (cp, buf, n);
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));
175 /* Compute the timeout time. */
176 t += db->postimeout;
178 /* Now get the lock to safely insert the records. */
179 pthread_rwlock_rdlock (&db->lock);
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);
185 cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db, owner);
187 pthread_rwlock_unlock (&db->lock);
190 if (__builtin_expect (written != total, 0) && debug_level > 0)
192 char buf[256];
193 dbg_log (_("short write in %s: %s"), __FUNCTION__,
194 strerror_r (errno, buf, sizeof (buf)));
199 void
200 addpwbyname (struct database *db, int fd, request_header *req,
201 void *key, uid_t c_uid)
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;
214 if (__builtin_expect (debug_level > 0, 0))
215 dbg_log (_("Haven't found \"%s\" in password cache!"), (char *) key);
217 if (secure[pwddb])
219 oldeuid = geteuid ();
220 seteuid (c_uid);
223 while (__getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
224 && errno == ERANGE)
226 char *old_buffer = buffer;
227 errno = 0;
228 #define INCR 1024
230 if (__builtin_expect (buflen > 32768, 0))
232 buflen += INCR;
233 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
234 if (buffer == NULL)
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;
243 use_malloc = true;
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);
251 if (secure[pwddb])
252 seteuid (oldeuid);
254 cache_addpw (db, fd, req, key, pwd, c_uid);
256 if (use_malloc)
257 free (buffer);
261 void
262 addpwbyuid (struct database *db, int fd, request_header *req,
263 void *key, uid_t c_uid)
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;
278 if (*(char *) key == '\0' || *ep != '\0') /* invalid numeric uid */
280 if (debug_level > 0)
281 dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
283 errno = EINVAL;
284 return;
287 if (__builtin_expect (debug_level > 0, 0))
288 dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
290 if (secure[pwddb])
292 oldeuid = geteuid ();
293 seteuid (c_uid);
296 while (__getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
297 && errno == ERANGE)
299 char *old_buffer = buffer;
300 errno = 0;
302 if (__builtin_expect (buflen > 32768, 0))
304 buflen += 1024;
305 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
306 if (buffer == NULL)
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;
315 use_malloc = true;
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);
323 if (secure[pwddb])
324 seteuid (oldeuid);
326 cache_addpw (db, fd, req, key, pwd, c_uid);
328 if (use_malloc)
329 free (buffer);