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