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