1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <alloca.h>
21: #include <assert.h>
22: #include <errno.h>
23: #include <error.h>
24: #include <grp.h>
25: #include <libintl.h>
26: #include <stdbool.h>
27: #include <stddef.h>
28: #include <stdio.h>
29: #include <stdint.h>
30: #include <stdlib.h>
31: #include <string.h>
32: #include <unistd.h>
33: #include <sys/mman.h>
34: #include <sys/socket.h>
35: #include <stackinfo.h>
36:
37: #include "nscd.h"
38: #include "dbg_log.h"
39: #ifdef HAVE_SENDFILE
40: # include <kernel-features.h>
41: #endif
42:
43:
44: static const gr_response_header disabled =
45: {
46: .version = NSCD_VERSION,
47: .found = -1,
48: .gr_name_len = 0,
49: .gr_passwd_len = 0,
50: .gr_gid = -1,
51: .gr_mem_cnt = 0,
52: };
53:
54:
55: const struct iovec grp_iov_disabled =
56: {
57: .iov_base = (void *) &disabled,
58: .iov_len = sizeof (disabled)
59: };
60:
61:
62:
63: static const gr_response_header notfound =
64: {
65: .version = NSCD_VERSION,
66: .found = 0,
67: .gr_name_len = 0,
68: .gr_passwd_len = 0,
69: .gr_gid = -1,
70: .gr_mem_cnt = 0,
71: };
72:
73:
74: static void
75: cache_addgr (struct database_dyn *db, int fd, request_header *req,
76: const void *key, struct group *grp, uid_t owner,
77: struct hashentry *he, struct datahead *dh, int errval)
78: {
79: ssize_t total;
80: ssize_t written;
81: time_t t = time (NULL);
82:
83:
84:
85: struct dataset
86: {
87: struct datahead head;
88: gr_response_header resp;
89: char strdata[0];
90: } *dataset;
91:
92: assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
93:
94: if (grp == NULL)
95: {
96: if (he != NULL && errval == EAGAIN)
97: {
98:
99:
100:
101: if (reload_count != UINT_MAX)
102:
103: dh->nreloads = reload_count - 1;
104:
105: written = total = 0;
106: }
107: else
108: {
109:
110:
111: total = sizeof (notfound);
112:
113: written = TEMP_FAILURE_RETRY (send (fd, ¬found, total,
114: MSG_NOSIGNAL));
115:
116: dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
117:
118: if (dataset != NULL)
119: {
120: dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
121: dataset->head.recsize = total;
122: dataset->head.notfound = true;
123: dataset->head.nreloads = 0;
124: dataset->head.usable = true;
125:
126:
127: dataset->head.timeout = t + db->negtimeout;
128:
129:
130: memcpy (&dataset->resp, ¬found, total);
131:
132:
133: memcpy (dataset->strdata, key, req->key_len);
134:
135:
136: if (db->persistent)
137: {
138:
139: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
140: msync ((void *) pval,
141: ((uintptr_t) dataset & pagesize_m1)
142: + sizeof (struct dataset) + req->key_len, MS_ASYNC);
143: }
144:
145:
146: pthread_rwlock_rdlock (&db->lock);
147:
148: if (cache_add (req->type, &dataset->strdata, req->key_len,
149: &dataset->head, true, db, owner) < 0)
150:
151: dataset->head.usable = false;
152:
153: pthread_rwlock_unlock (&db->lock);
154:
155:
156: if (dh != NULL)
157: dh->usable = false;
158: }
159: else
160: ++db->head->addfailed;
161: }
162: }
163: else
164: {
165:
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;
169: uint32_t *gr_mem_len;
170: size_t gr_mem_len_total = 0;
171: char *gr_name;
172: char *cp;
173: const size_t key_len = strlen (key);
174: const size_t buf_len = 3 * sizeof (grp->gr_gid) + key_len + 1;
175: char *buf = alloca (buf_len);
176: ssize_t n;
177: size_t cnt;
178:
179:
180: int key_offset;
181: n = snprintf (buf, buf_len, "%d%c%n%s", grp->gr_gid, '\0',
182: &key_offset, (char *) key) + 1;
183:
184:
185: while (grp->gr_mem[gr_mem_cnt])
186: ++gr_mem_cnt;
187: gr_mem_len = (uint32_t *) alloca (gr_mem_cnt * sizeof (uint32_t));
188: for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
189: {
190: gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
191: gr_mem_len_total += gr_mem_len[gr_mem_cnt];
192: }
193:
194: written = total = (sizeof (struct dataset)
195: + gr_mem_cnt * sizeof (uint32_t)
196: + gr_name_len + gr_passwd_len + gr_mem_len_total);
197:
198:
199:
200:
201:
202: bool alloca_used = false;
203: dataset = NULL;
204:
205: if (he == NULL)
206: {
207: dataset = (struct dataset *) mempool_alloc (db, total + n);
208: if (dataset == NULL)
209: ++db->head->addfailed;
210: }
211:
212: if (dataset == NULL)
213: {
214:
215:
216:
217: dataset = (struct dataset *) alloca (total + n);
218:
219:
220: alloca_used = true;
221: }
222:
223: dataset->head.allocsize = total + n;
224: dataset->head.recsize = total - offsetof (struct dataset, resp);
225: dataset->head.notfound = false;
226: dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
227: dataset->head.usable = true;
228:
229:
230: dataset->head.timeout = t + db->postimeout;
231:
232: dataset->resp.version = NSCD_VERSION;
233: dataset->resp.found = 1;
234: dataset->resp.gr_name_len = gr_name_len;
235: dataset->resp.gr_passwd_len = gr_passwd_len;
236: dataset->resp.gr_gid = grp->gr_gid;
237: dataset->resp.gr_mem_cnt = gr_mem_cnt;
238:
239: cp = dataset->strdata;
240:
241:
242: cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
243: gr_name = cp;
244: cp = mempcpy (cp, grp->gr_name, gr_name_len);
245: cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
246:
247: for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
248: cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
249:
250:
251: memcpy (cp, buf, n);
252: char *key_copy = cp + key_offset;
253: assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
254:
255:
256:
257: if (he != NULL)
258: {
259: assert (fd == -1);
260:
261: if (total + n == dh->allocsize
262: && total - offsetof (struct dataset, resp) == dh->recsize
263: && memcmp (&dataset->resp, dh->data,
264: dh->allocsize - offsetof (struct dataset, resp)) == 0)
265: {
266:
267:
268:
269: dh->timeout = dataset->head.timeout;
270: ++dh->nreloads;
271: }
272: else
273: {
274:
275:
276: struct dataset *newp
277: = (struct dataset *) mempool_alloc (db, total + n);
278: if (newp != NULL)
279: {
280:
281: gr_name = (char *) newp + (gr_name - (char *) dataset);
282: cp = (char *) newp + (cp - (char *) dataset);
283: key_copy = (char *) newp + (key_copy - (char *) dataset);
284:
285: dataset = memcpy (newp, dataset, total + n);
286: alloca_used = false;
287: }
288:
289:
290: dh->usable = false;
291: }
292: }
293: else
294: {
295:
296:
297:
298: assert (fd != -1);
299:
300: #ifdef HAVE_SENDFILE
301: if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
302: {
303: assert (db->wr_fd != -1);
304: assert ((char *) &dataset->resp > (char *) db->data);
305: assert ((char *) &dataset->resp - (char *) db->head
306: + total
307: <= (sizeof (struct database_pers_head)
308: + db->head->module * sizeof (ref_t)
309: + db->head->data_size));
310: written = sendfileall (fd, db->wr_fd,
311: (char *) &dataset->resp
312: - (char *) db->head, total);
313: # ifndef __ASSUME_SENDFILE
314: if (written == -1 && errno == ENOSYS)
315: goto use_write;
316: # endif
317: }
318: else
319: # ifndef __ASSUME_SENDFILE
320: use_write:
321: # endif
322: #endif
323: written = writeall (fd, &dataset->resp, total);
324: }
325:
326:
327:
328: if (! alloca_used)
329: {
330:
331: if (db->persistent)
332: {
333:
334: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
335: msync ((void *) pval,
336: ((uintptr_t) dataset & pagesize_m1) + total + n,
337: MS_ASYNC);
338: }
339:
340:
341: pthread_rwlock_rdlock (&db->lock);
342:
343:
344:
345:
346:
347: bool first = true;
348:
349:
350: if (req->type == GETGRBYGID)
351: {
352: if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
353: db, owner) < 0)
354: {
355:
356:
357: dataset->head.usable = false;
358: goto out;
359: }
360:
361: first = false;
362: }
363:
364: else if (strcmp (key_copy, gr_name) != 0)
365: {
366: if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
367: &dataset->head, true, db, owner) < 0)
368: {
369:
370:
371: dataset->head.usable = false;
372: goto out;
373: }
374:
375: first = false;
376: }
377:
378:
379: if ((req->type == GETGRBYNAME || db->propagate)
380: && __builtin_expect (cache_add (GETGRBYNAME, gr_name,
381: gr_name_len,
382: &dataset->head, first, db, owner)
383: == 0, 1))
384: {
385: if (req->type == GETGRBYNAME && db->propagate)
386: (void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
387: req->type != GETGRBYNAME, db, owner);
388: }
389: else if (first)
390:
391:
392: dataset->head.usable = false;
393:
394: out:
395: pthread_rwlock_unlock (&db->lock);
396: }
397: }
398:
399: if (__builtin_expect (written != total, 0) && debug_level > 0)
400: {
401: char buf[256];
402: dbg_log (_("short write in %s: %s"), __FUNCTION__,
403: strerror_r (errno, buf, sizeof (buf)));
404: }
405: }
406:
407:
408: union keytype
409: {
410: void *v;
411: gid_t g;
412: };
413:
414:
415: static int
416: lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
417: size_t buflen, struct group **grp)
418: {
419: if (type == GETGRBYNAME)
420: return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
421: else
422: return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
423: }
424:
425:
426: static void
427: addgrbyX (struct database_dyn *db, int fd, request_header *req,
428: union keytype key, const char *keystr, uid_t uid,
429: struct hashentry *he, struct datahead *dh)
430: {
431:
432:
433:
434:
435: size_t buflen = 1024;
436: char *buffer = (char *) alloca (buflen);
437: struct group resultbuf;
438: struct group *grp;
439: bool use_malloc = false;
440: int errval = 0;
441:
442: if (__builtin_expect (debug_level > 0, 0))
443: {
444: if (he == NULL)
445: dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
446: else
447: dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
448: }
449:
450: while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
451: && (errval = errno) == ERANGE)
452: {
453: errno = 0;
454:
455: if (__builtin_expect (buflen > 32768, 0))
456: {
457: char *old_buffer = buffer;
458: buflen *= 2;
459: buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
460: if (buffer == NULL)
461: {
462:
463:
464:
465: grp = NULL;
466: buffer = old_buffer;
467:
468:
469:
470:
471: errval = EAGAIN;
472: break;
473: }
474: use_malloc = true;
475: }
476: else
477:
478:
479: buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
480: }
481:
482: cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
483:
484: if (use_malloc)
485: free (buffer);
486: }
487:
488:
489: void
490: addgrbyname (struct database_dyn *db, int fd, request_header *req,
491: void *key, uid_t uid)
492: {
493: union keytype u = { .v = key };
494:
495: addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
496: }
497:
498:
499: void
500: readdgrbyname (struct database_dyn *db, struct hashentry *he,
501: struct datahead *dh)
502: {
503: request_header req =
504: {
505: .type = GETGRBYNAME,
506: .key_len = he->len
507: };
508: union keytype u = { .v = db->data + he->key };
509:
510: addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
511: }
512:
513:
514: void
515: addgrbygid (struct database_dyn *db, int fd, request_header *req,
516: void *key, uid_t uid)
517: {
518: char *ep;
519: gid_t gid = strtoul ((char *) key, &ep, 10);
520:
521: if (*(char *) key == '\0' || *ep != '\0')
522: {
523: if (debug_level > 0)
524: dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
525:
526: errno = EINVAL;
527: return;
528: }
529:
530: union keytype u = { .g = gid };
531:
532: addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
533: }
534:
535:
536: void
537: readdgrbygid (struct database_dyn *db, struct hashentry *he,
538: struct datahead *dh)
539: {
540: char *ep;
541: gid_t gid = strtoul (db->data + he->key, &ep, 10);
542:
543:
544: assert (*(db->data + he->key) != '\0' && *ep == '\0');
545:
546: request_header req =
547: {
548: .type = GETGRBYGID,
549: .key_len = he->len
550: };
551: union keytype u = { .g = gid };
552:
553: addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
554: }