]> sourceware.org Git - glibc.git/blame - nscd/pwdcache.c
Fix missing libc-internal.h include.
[glibc.git] / nscd / pwdcache.c
CommitLineData
67479a70 1/* Cache handling for passwd lookup.
568035b7 2 Copyright (C) 1998-2013 Free Software Foundation, Inc.
d67281a7 3 This file is part of the GNU C Library.
67479a70 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
d67281a7 5
43bc8ac6 6 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
d67281a7 10
43bc8ac6 11 This program is distributed in the hope that it will be useful,
d67281a7 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
d67281a7 15
43bc8ac6 16 You should have received a copy of the GNU General Public License
59ba27a6 17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
d67281a7 18
9caf4f1c 19#include <alloca.h>
a95a08b4 20#include <assert.h>
d67281a7 21#include <errno.h>
67479a70 22#include <error.h>
a95a08b4 23#include <libintl.h>
d67281a7 24#include <pwd.h>
c7a9b6e2 25#include <stdbool.h>
67479a70
UD
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
d67281a7 29#include <string.h>
67479a70 30#include <time.h>
ba9234d9 31#include <unistd.h>
a95a08b4 32#include <sys/mman.h>
0b20008e 33#include <sys/socket.h>
c7a9b6e2 34#include <stackinfo.h>
d67281a7 35
d67281a7 36#include "nscd.h"
67479a70 37#include "dbg_log.h"
eac10791
UD
38#ifdef HAVE_SENDFILE
39# include <kernel-features.h>
40#endif
d67281a7 41
67479a70
UD
42/* This is the standard reply in case the service is disabled. */
43static const pw_response_header disabled =
44{
c2e13112
RM
45 .version = NSCD_VERSION,
46 .found = -1,
47 .pw_name_len = 0,
48 .pw_passwd_len = 0,
49 .pw_uid = -1,
50 .pw_gid = -1,
51 .pw_gecos_len = 0,
52 .pw_dir_len = 0,
53 .pw_shell_len = 0
d67281a7 54};
d67281a7 55
67479a70
UD
56/* This is the struct describing how to write this record. */
57const struct iovec pwd_iov_disabled =
d67281a7 58{
c2e13112
RM
59 .iov_base = (void *) &disabled,
60 .iov_len = sizeof (disabled)
d67281a7 61};
d67281a7 62
d67281a7 63
67479a70
UD
64/* This is the standard reply in case we haven't found the dataset. */
65static const pw_response_header notfound =
d67281a7 66{
c2e13112
RM
67 .version = NSCD_VERSION,
68 .found = 0,
69 .pw_name_len = 0,
70 .pw_passwd_len = 0,
71 .pw_uid = -1,
72 .pw_gid = -1,
73 .pw_gecos_len = 0,
74 .pw_dir_len = 0,
75 .pw_shell_len = 0
67479a70 76};
d67281a7 77
d67281a7 78
a4c7ea7b 79static time_t
a95a08b4
UD
80cache_addpw (struct database_dyn *db, int fd, request_header *req,
81 const void *key, struct passwd *pwd, uid_t owner,
20e498bd 82 struct hashentry *const he, struct datahead *dh, int errval)
d67281a7 83{
306dfba9 84 bool all_written = true;
67479a70 85 ssize_t total;
67479a70 86 time_t t = time (NULL);
d67281a7 87
a95a08b4
UD
88 /* We allocate all data in one memory block: the iov vector,
89 the response header and the dataset itself. */
90 struct dataset
91 {
92 struct datahead head;
93 pw_response_header resp;
94 char strdata[0];
95 } *dataset;
96
97 assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
98
a4c7ea7b 99 time_t timeout = MAX_TIMEOUT_VALUE;
67479a70 100 if (pwd == NULL)
d67281a7 101 {
a95a08b4
UD
102 if (he != NULL && errval == EAGAIN)
103 {
104 /* If we have an old record available but cannot find one
105 now because the service is not available we keep the old
106 record and make sure it does not get removed. */
107 if (reload_count != UINT_MAX && dh->nreloads == reload_count)
108 /* Do not reset the value if we never not reload the record. */
109 dh->nreloads = reload_count - 1;
110
a4c7ea7b
UD
111 /* Reload with the same time-to-live value. */
112 timeout = dh->timeout = t + db->postimeout;
113
306dfba9 114 total = 0;
a95a08b4
UD
115 }
116 else
117 {
118 /* We have no data. This means we send the standard reply for this
119 case. */
306dfba9 120 total = sizeof (notfound);
d67281a7 121
306dfba9
AS
122 if (fd != -1
123 && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
124 MSG_NOSIGNAL)) != total)
125 all_written = false;
d67281a7 126
3e1aa84e
UD
127 /* If we have a transient error or cannot permanently store
128 the result, so be it. */
129 if (errno == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
445b4a53
TK
130 {
131 /* Mark the old entry as obsolete. */
132 if (dh != NULL)
133 dh->usable = false;
134 }
99231d9a
UD
135 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
136 + req->key_len), 1)) != NULL)
a95a08b4
UD
137 {
138 dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
139 dataset->head.recsize = total;
140 dataset->head.notfound = true;
141 dataset->head.nreloads = 0;
142 dataset->head.usable = true;
d67281a7 143
a95a08b4 144 /* Compute the timeout time. */
a4c7ea7b 145 timeout = dataset->head.timeout = t + db->negtimeout;
d67281a7 146
a95a08b4
UD
147 /* This is the reply. */
148 memcpy (&dataset->resp, &notfound, total);
d67281a7 149
a95a08b4
UD
150 /* Copy the key data. */
151 char *key_copy = memcpy (dataset->strdata, key, req->key_len);
d67281a7 152
cf244b74
UD
153 /* If necessary, we also propagate the data to disk. */
154 if (db->persistent)
155 {
156 // XXX async OK?
157 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
158 msync ((void *) pval,
159 ((uintptr_t) dataset & pagesize_m1)
160 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
161 }
162
7e71e55f 163 (void) cache_add (req->type, key_copy, req->key_len,
528741cb 164 &dataset->head, true, db, owner, he == NULL);
a95a08b4 165
00ebd7ed
UD
166 pthread_rwlock_unlock (&db->lock);
167
a95a08b4
UD
168 /* Mark the old entry as obsolete. */
169 if (dh != NULL)
170 dh->usable = false;
171 }
99bb9f42 172 }
d67281a7
UD
173 }
174 else
175 {
67479a70 176 /* Determine the I/O structure. */
67479a70
UD
177 size_t pw_name_len = strlen (pwd->pw_name) + 1;
178 size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
179 size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
180 size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
181 size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
182 char *cp;
a95a08b4
UD
183 const size_t key_len = strlen (key);
184 const size_t buf_len = 3 * sizeof (pwd->pw_uid) + key_len + 1;
185 char *buf = alloca (buf_len);
67479a70
UD
186 ssize_t n;
187
188 /* We need this to insert the `byuid' entry. */
a95a08b4
UD
189 int key_offset;
190 n = snprintf (buf, buf_len, "%d%c%n%s", pwd->pw_uid, '\0',
191 &key_offset, (char *) key) + 1;
192
306dfba9
AS
193 total = (offsetof (struct dataset, strdata)
194 + pw_name_len + pw_passwd_len
195 + pw_gecos_len + pw_dir_len + pw_shell_len);
a95a08b4
UD
196
197 /* If we refill the cache, first assume the reconrd did not
198 change. Allocate memory on the cache since it is likely
199 discarded anyway. If it turns out to be necessary to have a
200 new record we can still allocate real memory. */
201 bool alloca_used = false;
202 dataset = NULL;
203
204 if (he == NULL)
20e498bd 205 dataset = (struct dataset *) mempool_alloc (db, total + n, 1);
a95a08b4
UD
206
207 if (dataset == NULL)
208 {
209 /* We cannot permanently add the result in the moment. But
210 we can provide the result as is. Store the data in some
211 temporary memory. */
212 dataset = (struct dataset *) alloca (total + n);
213
214 /* We cannot add this record to the permanent database. */
215 alloca_used = true;
216 }
217
218 dataset->head.allocsize = total + n;
219 dataset->head.recsize = total - offsetof (struct dataset, resp);
220 dataset->head.notfound = false;
221 dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
222 dataset->head.usable = true;
223
224 /* Compute the timeout time. */
a4c7ea7b 225 timeout = dataset->head.timeout = t + db->postimeout;
a95a08b4
UD
226
227 dataset->resp.version = NSCD_VERSION;
228 dataset->resp.found = 1;
229 dataset->resp.pw_name_len = pw_name_len;
230 dataset->resp.pw_passwd_len = pw_passwd_len;
231 dataset->resp.pw_uid = pwd->pw_uid;
232 dataset->resp.pw_gid = pwd->pw_gid;
233 dataset->resp.pw_gecos_len = pw_gecos_len;
234 dataset->resp.pw_dir_len = pw_dir_len;
235 dataset->resp.pw_shell_len = pw_shell_len;
236
237 cp = dataset->strdata;
67479a70
UD
238
239 /* Copy the strings over into the buffer. */
240 cp = mempcpy (cp, pwd->pw_name, pw_name_len);
241 cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
242 cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
243 cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
244 cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
245
a95a08b4 246 /* Finally the stringified UID value. */
67479a70 247 memcpy (cp, buf, n);
a95a08b4
UD
248 char *key_copy = cp + key_offset;
249 assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
67479a70 250
5a337776
UD
251 assert (cp == dataset->strdata + total - offsetof (struct dataset,
252 strdata));
253
a95a08b4
UD
254 /* Now we can determine whether on refill we have to create a new
255 record or not. */
256 if (he != NULL)
257 {
258 assert (fd == -1);
801ddb6a 259
20e498bd 260 if (dataset->head.allocsize == dh->allocsize
5a337776 261 && dataset->head.recsize == dh->recsize
a95a08b4
UD
262 && memcmp (&dataset->resp, dh->data,
263 dh->allocsize - offsetof (struct dataset, resp)) == 0)
264 {
cf244b74 265 /* The data has not changed. We will just bump the
a95a08b4
UD
266 timeout value. Note that the new record has been
267 allocated on the stack and need not be freed. */
268 dh->timeout = dataset->head.timeout;
269 ++dh->nreloads;
270 }
271 else
272 {
273 /* We have to create a new record. Just allocate
274 appropriate memory and copy it. */
275 struct dataset *newp
20e498bd 276 = (struct dataset *) mempool_alloc (db, total + n, 1);
a95a08b4
UD
277 if (newp != NULL)
278 {
279 /* Adjust pointer into the memory block. */
280 cp = (char *) newp + (cp - (char *) dataset);
61705e06 281 key_copy = (char *) newp + (key_copy - (char *) dataset);
a95a08b4
UD
282
283 dataset = memcpy (newp, dataset, total + n);
284 alloca_used = false;
285 }
286
287 /* Mark the old record as obsolete. */
288 dh->usable = false;
289 }
290 }
291 else
292 {
293 /* We write the dataset before inserting it to the database
294 since while inserting this thread might block and so would
295 unnecessarily let the receiver wait. */
296 assert (fd != -1);
67479a70 297
eac10791 298#ifdef HAVE_SENDFILE
74158740 299 if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
eac10791
UD
300 {
301 assert (db->wr_fd != -1);
302 assert ((char *) &dataset->resp > (char *) db->data);
ea547a1a 303 assert ((char *) dataset - (char *) db->head
eac10791
UD
304 + total
305 <= (sizeof (struct database_pers_head)
a4c7ea7b
UD
306 + db->head->module * sizeof (ref_t)
307 + db->head->data_size));
306dfba9
AS
308 ssize_t written = sendfileall (fd, db->wr_fd,
309 (char *) &dataset->resp
310 - (char *) db->head,
311 dataset->head.recsize);
312 if (written != dataset->head.recsize)
313 {
eac10791 314# ifndef __ASSUME_SENDFILE
306dfba9
AS
315 if (written == -1 && errno == ENOSYS)
316 goto use_write;
eac10791 317# endif
306dfba9
AS
318 all_written = false;
319 }
eac10791
UD
320 }
321 else
322# ifndef __ASSUME_SENDFILE
323 use_write:
324# endif
325#endif
306dfba9
AS
326 if (writeall (fd, &dataset->resp, dataset->head.recsize)
327 != dataset->head.recsize)
328 all_written = false;
a95a08b4 329 }
67479a70 330
67479a70 331
a95a08b4
UD
332 /* Add the record to the database. But only if it has not been
333 stored on the stack. */
334 if (! alloca_used)
335 {
336 /* If necessary, we also propagate the data to disk. */
337 if (db->persistent)
3418007e
UD
338 {
339 // XXX async OK?
340 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
341 msync ((void *) pval,
342 ((uintptr_t) dataset & pagesize_m1) + total + n,
343 MS_ASYNC);
344 }
67479a70 345
a95a08b4
UD
346 /* NB: in the following code we always must add the entry
347 marked with FIRST first. Otherwise we end up with
348 dangling "pointers" in case a latter hash entry cannot be
349 added. */
797ed6f7 350 bool first = true;
67479a70 351
a95a08b4 352 /* If the request was by UID, add that entry first. */
797ed6f7 353 if (req->type == GETPWBYUID)
a95a08b4 354 {
03e157d8 355 if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
528741cb 356 db, owner, he == NULL) < 0)
7e71e55f 357 goto out;
797ed6f7
UD
358
359 first = false;
a95a08b4
UD
360 }
361 /* If the key is different from the name add a separate entry. */
362 else if (strcmp (key_copy, dataset->strdata) != 0)
363 {
364 if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
528741cb 365 &dataset->head, true, db, owner, he == NULL) < 0)
7e71e55f 366 goto out;
a95a08b4
UD
367
368 first = false;
369 }
370
371 /* We have to add the value for both, byname and byuid. */
797ed6f7
UD
372 if ((req->type == GETPWBYNAME || db->propagate)
373 && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
374 pw_name_len, &dataset->head,
528741cb
UD
375 first, db, owner, he == NULL)
376 == 0, 1))
a95a08b4 377 {
797ed6f7 378 if (req->type == GETPWBYNAME && db->propagate)
03e157d8 379 (void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
528741cb 380 false, db, owner, false);
a95a08b4 381 }
00ebd7ed
UD
382
383 out:
384 pthread_rwlock_unlock (&db->lock);
a95a08b4 385 }
d67281a7 386 }
14e9dd67 387
306dfba9 388 if (__builtin_expect (!all_written, 0) && debug_level > 0)
d67281a7 389 {
67479a70
UD
390 char buf[256];
391 dbg_log (_("short write in %s: %s"), __FUNCTION__,
392 strerror_r (errno, buf, sizeof (buf)));
d67281a7 393 }
a4c7ea7b
UD
394
395 return timeout;
d67281a7
UD
396}
397
d67281a7 398
a95a08b4
UD
399union keytype
400{
401 void *v;
402 uid_t u;
403};
404
405
406static int
407lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer,
408 size_t buflen, struct passwd **pwd)
409{
410 if (type == GETPWBYNAME)
411 return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd);
412 else
413 return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd);
414}
415
416
a4c7ea7b 417static time_t
a95a08b4
UD
418addpwbyX (struct database_dyn *db, int fd, request_header *req,
419 union keytype key, const char *keystr, uid_t c_uid,
420 struct hashentry *he, struct datahead *dh)
67479a70
UD
421{
422 /* Search for the entry matching the key. Please note that we don't
423 look again in the table whether the dataset is now available. We
424 simply insert it. It does not matter if it is in there twice. The
425 pruning function only will look at the timestamp. */
a95a08b4 426 size_t buflen = 1024;
c7a9b6e2 427 char *buffer = (char *) alloca (buflen);
67479a70
UD
428 struct passwd resultbuf;
429 struct passwd *pwd;
c7a9b6e2 430 bool use_malloc = false;
a95a08b4 431 int errval = 0;
d67281a7 432
c7a9b6e2 433 if (__builtin_expect (debug_level > 0, 0))
a95a08b4
UD
434 {
435 if (he == NULL)
436 dbg_log (_("Haven't found \"%s\" in password cache!"), keystr);
437 else
438 dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
439 }
d67281a7 440
a95a08b4
UD
441 while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0
442 && (errval = errno) == ERANGE)
d67281a7 443 {
67479a70 444 errno = 0;
c7a9b6e2
UD
445
446 if (__builtin_expect (buflen > 32768, 0))
447 {
b21fa963 448 char *old_buffer = buffer;
4379b403 449 buflen *= 2;
c7a9b6e2
UD
450 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
451 if (buffer == NULL)
452 {
453 /* We ran out of memory. We cannot do anything but
454 sending a negative response. In reality this should
455 never happen. */
456 pwd = NULL;
457 buffer = old_buffer;
a95a08b4
UD
458
459 /* We set the error to indicate this is (possibly) a
460 temporary error and that it does not mean the entry
461 is not available at all. */
462 errval = EAGAIN;
c7a9b6e2
UD
463 break;
464 }
465 use_malloc = true;
466 }
467 else
9caf4f1c
UD
468 /* Allocate a new buffer on the stack. If possible combine it
469 with the previously allocated buffer. */
4379b403 470 buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
d67281a7 471 }
14e9dd67 472
a95a08b4 473 /* Add the entry to the cache. */
a4c7ea7b
UD
474 time_t timeout = cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh,
475 errval);
c7a9b6e2
UD
476
477 if (use_malloc)
478 free (buffer);
a4c7ea7b
UD
479
480 return timeout;
d67281a7
UD
481}
482
d67281a7 483
67479a70 484void
a95a08b4
UD
485addpwbyname (struct database_dyn *db, int fd, request_header *req,
486 void *key, uid_t c_uid)
487{
488 union keytype u = { .v = key };
489
490 addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
491}
492
493
a4c7ea7b 494time_t
a95a08b4
UD
495readdpwbyname (struct database_dyn *db, struct hashentry *he,
496 struct datahead *dh)
497{
498 request_header req =
499 {
500 .type = GETPWBYNAME,
501 .key_len = he->len
502 };
503 union keytype u = { .v = db->data + he->key };
504
a4c7ea7b 505 return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
a95a08b4
UD
506}
507
508
509void
510addpwbyuid (struct database_dyn *db, int fd, request_header *req,
a1c542bf 511 void *key, uid_t c_uid)
67479a70 512{
8e9b2075 513 char *ep;
c7a9b6e2 514 uid_t uid = strtoul ((char *) key, &ep, 10);
c7a9b6e2
UD
515
516 if (*(char *) key == '\0' || *ep != '\0') /* invalid numeric uid */
8e9b2075 517 {
c7a9b6e2 518 if (debug_level > 0)
a4c7ea7b 519 dbg_log (_("Invalid numeric uid \"%s\"!"), (char *) key);
8e9b2075
UD
520
521 errno = EINVAL;
522 return;
523 }
d67281a7 524
a95a08b4 525 union keytype u = { .u = uid };
d67281a7 526
a95a08b4
UD
527 addpwbyX (db, fd, req, u, key, c_uid, NULL, NULL);
528}
a1c542bf 529
c7a9b6e2 530
a4c7ea7b 531time_t
a95a08b4
UD
532readdpwbyuid (struct database_dyn *db, struct hashentry *he,
533 struct datahead *dh)
534{
535 char *ep;
536 uid_t uid = strtoul (db->data + he->key, &ep, 10);
d67281a7 537
a95a08b4
UD
538 /* Since the key has been added before it must be OK. */
539 assert (*(db->data + he->key) != '\0' && *ep == '\0');
a1c542bf 540
a95a08b4
UD
541 request_header req =
542 {
543 .type = GETPWBYUID,
544 .key_len = he->len
545 };
546 union keytype u = { .u = uid };
c7a9b6e2 547
a4c7ea7b 548 return addpwbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
d67281a7 549}
This page took 0.42664 seconds and 5 git commands to generate.