1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <assert.h>
21: #include <errno.h>
22: #include <grp.h>
23: #include <libintl.h>
24: #include <string.h>
25: #include <time.h>
26: #include <unistd.h>
27: #include <sys/mman.h>
28:
29: #include "dbg_log.h"
30: #include "nscd.h"
31: #ifdef HAVE_SENDFILE
32: # include <kernel-features.h>
33: #endif
34:
35: #include "../nss/nsswitch.h"
36:
37:
38:
39: typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
40: long int *, long int *,
41: gid_t **, long int, int *);
42:
43:
44: static const initgr_response_header notfound =
45: {
46: .version = NSCD_VERSION,
47: .found = 0,
48: .ngrps = 0
49: };
50:
51:
52: #include "../grp/compat-initgroups.c"
53:
54:
55: static void
56: addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
57: void *key, uid_t uid, struct hashentry *he,
58: struct datahead *dh)
59: {
60:
61:
62:
63:
64:
65:
66:
67:
68: struct dataset
69: {
70: struct datahead head;
71: initgr_response_header resp;
72: char strdata[0];
73: } *dataset = NULL;
74:
75: if (__builtin_expect (debug_level > 0, 0))
76: {
77: if (he == NULL)
78: dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key);
79: else
80: dbg_log (_("Reloading \"%s\" in group cache!"), (char *) key);
81: }
82:
83: static service_user *group_database;
84: service_user *nip = NULL;
85: int no_more;
86:
87: if (group_database != NULL)
88: {
89: nip = group_database;
90: no_more = 0;
91: }
92: else
93: no_more = __nss_database_lookup ("group", NULL,
94: "compat [NOTFOUND=return] files", &nip);
95:
96:
97:
98:
99: long int limit = __sysconf (_SC_NGROUPS_MAX);
100:
101: long int size;
102: if (limit > 0)
103:
104: size = MIN (limit, 64);
105: else
106:
107: size = 16;
108:
109: long int start = 0;
110: bool all_tryagain = true;
111: bool any_success = false;
112:
113:
114:
115:
116: gid_t *groups = (gid_t *) malloc (size * sizeof (gid_t));
117: if (__builtin_expect (groups == NULL, 0))
118:
119: goto out;
120:
121:
122: while (! no_more)
123: {
124: long int prev_start = start;
125: enum nss_status status;
126: initgroups_dyn_function fct;
127: fct = __nss_lookup_function (nip, "initgroups_dyn");
128:
129: if (fct == NULL)
130: {
131: status = compat_call (nip, key, -1, &start, &size, &groups,
132: limit, &errno);
133:
134: if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE)
135: break;
136: }
137: else
138: status = DL_CALL_FCT (fct, (key, -1, &start, &size, &groups,
139: limit, &errno));
140:
141:
142: long int cnt = prev_start;
143: while (cnt < start)
144: {
145: long int inner;
146: for (inner = 0; inner < prev_start; ++inner)
147: if (groups[inner] == groups[cnt])
148: break;
149:
150: if (inner < prev_start)
151: groups[cnt] = groups[--start];
152: else
153: ++cnt;
154: }
155:
156: if (status != NSS_STATUS_TRYAGAIN)
157: all_tryagain = false;
158:
159:
160: if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
161: __libc_fatal ("illegal status in internal_getgrouplist");
162:
163: any_success |= status == NSS_STATUS_SUCCESS;
164:
165: if (status != NSS_STATUS_SUCCESS
166: && nss_next_action (nip, status) == NSS_ACTION_RETURN)
167: break;
168:
169: if (nip->next == NULL)
170: no_more = -1;
171: else
172: nip = nip->next;
173: }
174:
175: ssize_t total;
176: ssize_t written;
177: out:
178: if (!any_success)
179: {
180:
181: written = total = sizeof (notfound);
182:
183: if (he != NULL && all_tryagain)
184: {
185:
186:
187:
188: if (reload_count != UINT_MAX && dh->nreloads == reload_count)
189:
190: dh->nreloads = reload_count - 1;
191: }
192: else
193: {
194:
195:
196: if (fd != -1)
197: written = TEMP_FAILURE_RETRY (send (fd, ¬found, total,
198: MSG_NOSIGNAL));
199:
200: dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
201:
202: if (dataset != NULL)
203: {
204: dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
205: dataset->head.recsize = total;
206: dataset->head.notfound = true;
207: dataset->head.nreloads = 0;
208: dataset->head.usable = true;
209:
210:
211: dataset->head.timeout = time (NULL) + db->negtimeout;
212:
213:
214: memcpy (&dataset->resp, ¬found, total);
215:
216:
217: char *key_copy = memcpy (dataset->strdata, key, req->key_len);
218:
219:
220: if (db->persistent)
221: {
222:
223: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
224: msync ((void *) pval,
225: ((uintptr_t) dataset & pagesize_m1)
226: + sizeof (struct dataset) + req->key_len, MS_ASYNC);
227: }
228:
229:
230: pthread_rwlock_rdlock (&db->lock);
231:
232: if (cache_add (req->type, key_copy, req->key_len,
233: &dataset->head, true, db, uid) < 0)
234:
235: dataset->head.usable = false;
236:
237: pthread_rwlock_unlock (&db->lock);
238:
239:
240: if (dh != NULL)
241: dh->usable = false;
242: }
243: else
244: ++db->head->addfailed;
245: }
246: }
247: else
248: {
249:
250: written = total = sizeof (struct dataset) + start * sizeof (int32_t);
251:
252:
253:
254:
255:
256: bool alloca_used = false;
257: dataset = NULL;
258:
259: if (he == NULL)
260: {
261: dataset = (struct dataset *) mempool_alloc (db,
262: total + req->key_len);
263: if (dataset == NULL)
264: ++db->head->addfailed;
265: }
266:
267: if (dataset == NULL)
268: {
269:
270:
271:
272: dataset = (struct dataset *) alloca (total + req->key_len);
273:
274:
275: alloca_used = true;
276: }
277:
278: dataset->head.allocsize = total + req->key_len;
279: dataset->head.recsize = total - offsetof (struct dataset, resp);
280: dataset->head.notfound = false;
281: dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
282: dataset->head.usable = true;
283:
284:
285: dataset->head.timeout = time (NULL) + db->postimeout;
286:
287: dataset->resp.version = NSCD_VERSION;
288: dataset->resp.found = 1;
289: dataset->resp.ngrps = start;
290:
291: char *cp = dataset->strdata;
292:
293:
294:
295: if (sizeof (gid_t) == sizeof (int32_t))
296: cp = mempcpy (cp, groups, start * sizeof (gid_t));
297: else
298: {
299: gid_t *gcp = (gid_t *) cp;
300:
301: for (int i = 0; i < start; ++i)
302: *gcp++ = groups[i];
303:
304: cp = (char *) gcp;
305: }
306:
307:
308: memcpy (cp, key, req->key_len);
309:
310:
311:
312: if (he != NULL)
313: {
314: assert (fd == -1);
315:
316: if (total + req->key_len == dh->allocsize
317: && total - offsetof (struct dataset, resp) == dh->recsize
318: && memcmp (&dataset->resp, dh->data,
319: dh->allocsize - offsetof (struct dataset, resp)) == 0)
320: {
321:
322:
323:
324: dh->timeout = dataset->head.timeout;
325: ++dh->nreloads;
326: }
327: else
328: {
329:
330:
331: struct dataset *newp
332: = (struct dataset *) mempool_alloc (db, total + req->key_len);
333: if (newp != NULL)
334: {
335:
336: cp = (char *) newp + (cp - (char *) dataset);
337:
338: dataset = memcpy (newp, dataset, total + req->key_len);
339: alloca_used = false;
340: }
341:
342:
343: dh->usable = false;
344: }
345: }
346: else
347: {
348:
349:
350:
351: assert (fd != -1);
352:
353: #ifdef HAVE_SENDFILE
354: if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
355: {
356: assert (db->wr_fd != -1);
357: assert ((char *) &dataset->resp > (char *) db->data);
358: assert ((char *) &dataset->resp - (char *) db->head
359: + total
360: <= (sizeof (struct database_pers_head)
361: + db->head->module * sizeof (ref_t)
362: + db->head->data_size));
363: written = sendfileall (fd, db->wr_fd,
364: (char *) &dataset->resp
365: - (char *) db->head, total);
366: # ifndef __ASSUME_SENDFILE
367: if (written == -1 && errno == ENOSYS)
368: goto use_write;
369: # endif
370: }
371: else
372: # ifndef __ASSUME_SENDFILE
373: use_write:
374: # endif
375: #endif
376: written = writeall (fd, &dataset->resp, total);
377: }
378:
379:
380:
381:
382: if (! alloca_used)
383: {
384:
385: if (db->persistent)
386: {
387:
388: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
389: msync ((void *) pval,
390: ((uintptr_t) dataset & pagesize_m1) + total +
391: req->key_len, MS_ASYNC);
392: }
393:
394:
395: pthread_rwlock_rdlock (&db->lock);
396:
397: if (cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
398: db, uid) < 0)
399:
400:
401: dataset->head.usable = false;
402:
403: pthread_rwlock_unlock (&db->lock);
404: }
405: }
406:
407: free (groups);
408:
409: if (__builtin_expect (written != total, 0) && debug_level > 0)
410: {
411: char buf[256];
412: dbg_log (_("short write in %s: %s"), __FUNCTION__,
413: strerror_r (errno, buf, sizeof (buf)));
414: }
415: }
416:
417:
418: void
419: addinitgroups (struct database_dyn *db, int fd, request_header *req, void *key,
420: uid_t uid)
421: {
422: addinitgroupsX (db, fd, req, key, uid, NULL, NULL);
423: }
424:
425:
426: void
427: readdinitgroups (struct database_dyn *db, struct hashentry *he,
428: struct datahead *dh)
429: {
430: request_header req =
431: {
432: .type = INITGROUPS,
433: .key_len = he->len
434: };
435:
436: addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
437: }