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 <atomic.h>
23: #include <error.h>
24: #include <errno.h>
25: #include <fcntl.h>
26: #include <grp.h>
27: #include <libintl.h>
28: #include <pthread.h>
29: #include <pwd.h>
30: #include <resolv.h>
31: #include <stdio.h>
32: #include <stdlib.h>
33: #include <unistd.h>
34: #include <arpa/inet.h>
35: #ifdef HAVE_EPOLL
36: # include <sys/epoll.h>
37: #endif
38: #include <sys/mman.h>
39: #include <sys/param.h>
40: #include <sys/poll.h>
41: #ifdef HAVE_SENDFILE
42: # include <sys/sendfile.h>
43: #endif
44: #include <sys/socket.h>
45: #include <sys/stat.h>
46: #include <sys/un.h>
47:
48: #include "nscd.h"
49: #include "dbg_log.h"
50: #include "selinux.h"
51: #ifdef HAVE_SENDFILE
52: # include <kernel-features.h>
53: #endif
54:
55:
56:
57: extern void *xmalloc (size_t n);
58: extern void *xcalloc (size_t n, size_t s);
59: extern void *xrealloc (void *o, size_t n);
60:
61:
62: const char *server_user;
63: static uid_t server_uid;
64: static gid_t server_gid;
65: const char *stat_user;
66: uid_t stat_uid;
67: static gid_t *server_groups;
68: #ifndef NGROUPS
69: # define NGROUPS 32
70: #endif
71: static int server_ngroups;
72:
73: static pthread_attr_t attr;
74:
75: static void begin_drop_privileges (void);
76: static void finish_drop_privileges (void);
77:
78:
79: const char *const serv2str[LASTREQ] =
80: {
81: [GETPWBYNAME] = "GETPWBYNAME",
82: [GETPWBYUID] = "GETPWBYUID",
83: [GETGRBYNAME] = "GETGRBYNAME",
84: [GETGRBYGID] = "GETGRBYGID",
85: [GETHOSTBYNAME] = "GETHOSTBYNAME",
86: [GETHOSTBYNAMEv6] = "GETHOSTBYNAMEv6",
87: [GETHOSTBYADDR] = "GETHOSTBYADDR",
88: [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
89: [SHUTDOWN] = "SHUTDOWN",
90: [GETSTAT] = "GETSTAT",
91: [INVALIDATE] = "INVALIDATE",
92: [GETFDPW] = "GETFDPW",
93: [GETFDGR] = "GETFDGR",
94: [GETFDHST] = "GETFDHST",
95: [GETAI] = "GETAI",
96: [INITGROUPS] = "INITGROUPS",
97: [GETSERVBYNAME] = "GETSERVBYNAME",
98: [GETSERVBYPORT] = "GETSERVBYPORT",
99: [GETFDSERV] = "GETFDSERV"
100: };
101:
102:
103: struct database_dyn dbs[lastdb] =
104: {
105: [pwddb] = {
106: .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
107: .prunelock = PTHREAD_MUTEX_INITIALIZER,
108: .enabled = 0,
109: .check_file = 1,
110: .persistent = 0,
111: .propagate = 1,
112: .shared = 0,
113: .max_db_size = DEFAULT_MAX_DB_SIZE,
114: .reset_res = 0,
115: .filename = "/etc/passwd",
116: .db_filename = _PATH_NSCD_PASSWD_DB,
117: .disabled_iov = &pwd_iov_disabled,
118: .postimeout = 3600,
119: .negtimeout = 20,
120: .wr_fd = -1,
121: .ro_fd = -1,
122: .mmap_used = false
123: },
124: [grpdb] = {
125: .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
126: .prunelock = PTHREAD_MUTEX_INITIALIZER,
127: .enabled = 0,
128: .check_file = 1,
129: .persistent = 0,
130: .propagate = 1,
131: .shared = 0,
132: .max_db_size = DEFAULT_MAX_DB_SIZE,
133: .reset_res = 0,
134: .filename = "/etc/group",
135: .db_filename = _PATH_NSCD_GROUP_DB,
136: .disabled_iov = &grp_iov_disabled,
137: .postimeout = 3600,
138: .negtimeout = 60,
139: .wr_fd = -1,
140: .ro_fd = -1,
141: .mmap_used = false
142: },
143: [hstdb] = {
144: .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
145: .prunelock = PTHREAD_MUTEX_INITIALIZER,
146: .enabled = 0,
147: .check_file = 1,
148: .persistent = 0,
149: .propagate = 0,
150: .shared = 0,
151: .max_db_size = DEFAULT_MAX_DB_SIZE,
152: .reset_res = 1,
153: .filename = "/etc/hosts",
154: .db_filename = _PATH_NSCD_HOSTS_DB,
155: .disabled_iov = &hst_iov_disabled,
156: .postimeout = 3600,
157: .negtimeout = 20,
158: .wr_fd = -1,
159: .ro_fd = -1,
160: .mmap_used = false
161: },
162: [servdb] = {
163: .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
164: .prunelock = PTHREAD_MUTEX_INITIALIZER,
165: .enabled = 0,
166: .check_file = 1,
167: .persistent = 0,
168: .propagate = 0,
169: .shared = 0,
170: .max_db_size = DEFAULT_MAX_DB_SIZE,
171: .reset_res = 0,
172: .filename = "/etc/services",
173: .db_filename = _PATH_NSCD_SERVICES_DB,
174: .disabled_iov = &serv_iov_disabled,
175: .postimeout = 28800,
176: .negtimeout = 20,
177: .wr_fd = -1,
178: .ro_fd = -1,
179: .mmap_used = false
180: }
181: };
182:
183:
184:
185: static struct
186: {
187: bool data_request;
188: struct database_dyn *db;
189: } const reqinfo[LASTREQ] =
190: {
191: [GETPWBYNAME] = { true, &dbs[pwddb] },
192: [GETPWBYUID] = { true, &dbs[pwddb] },
193: [GETGRBYNAME] = { true, &dbs[grpdb] },
194: [GETGRBYGID] = { true, &dbs[grpdb] },
195: [GETHOSTBYNAME] = { true, &dbs[hstdb] },
196: [GETHOSTBYNAMEv6] = { true, &dbs[hstdb] },
197: [GETHOSTBYADDR] = { true, &dbs[hstdb] },
198: [GETHOSTBYADDRv6] = { true, &dbs[hstdb] },
199: [SHUTDOWN] = { false, NULL },
200: [GETSTAT] = { false, NULL },
201: [SHUTDOWN] = { false, NULL },
202: [GETFDPW] = { false, &dbs[pwddb] },
203: [GETFDGR] = { false, &dbs[grpdb] },
204: [GETFDHST] = { false, &dbs[hstdb] },
205: [GETAI] = { true, &dbs[hstdb] },
206: [INITGROUPS] = { true, &dbs[grpdb] },
207: [GETSERVBYNAME] = { true, &dbs[servdb] },
208: [GETSERVBYPORT] = { true, &dbs[servdb] },
209: [GETFDSERV] = { false, &dbs[servdb] }
210: };
211:
212:
213:
214: #define CACHE_PRUNE_INTERVAL 15
215:
216:
217:
218: int nthreads = -1;
219:
220: int max_nthreads = 32;
221:
222:
223: static int sock;
224:
225:
226: unsigned long int client_queued;
227:
228:
229: ssize_t
230: writeall (int fd, const void *buf, size_t len)
231: {
232: size_t n = len;
233: ssize_t ret;
234: do
235: {
236: ret = TEMP_FAILURE_RETRY (send (fd, buf, n, MSG_NOSIGNAL));
237: if (ret <= 0)
238: break;
239: buf = (const char *) buf + ret;
240: n -= ret;
241: }
242: while (n > 0);
243: return ret < 0 ? ret : len - n;
244: }
245:
246:
247: #ifdef HAVE_SENDFILE
248: ssize_t
249: sendfileall (int tofd, int fromfd, off_t off, size_t len)
250: {
251: ssize_t n = len;
252: ssize_t ret;
253:
254: do
255: {
256: ret = TEMP_FAILURE_RETRY (sendfile (tofd, fromfd, &off, n));
257: if (ret <= 0)
258: break;
259: n -= ret;
260: }
261: while (n > 0);
262: return ret < 0 ? ret : len - n;
263: }
264: #endif
265:
266:
267: enum usekey
268: {
269: use_not = 0,
270:
271: use_first = 16,
272: use_begin = 32,
273: use_end = 64,
274:
275: use_he = 1,
276: use_he_begin = use_he | use_begin,
277: use_he_end = use_he | use_end,
278: #if SEPARATE_KEY
279: use_key = 2,
280: use_key_begin = use_key | use_begin,
281: use_key_end = use_key | use_end,
282: use_key_first = use_key_begin | use_first,
283: #endif
284: use_data = 3,
285: use_data_begin = use_data | use_begin,
286: use_data_end = use_data | use_end,
287: use_data_first = use_data_begin | use_first
288: };
289:
290:
291: static int
292: check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap,
293: enum usekey use, ref_t start, size_t len)
294: {
295: assert (len >= 2);
296:
297: if (start > first_free || start + len > first_free
298: || (start & BLOCK_ALIGN_M1))
299: return 0;
300:
301: if (usemap[start] == use_not)
302: {
303:
304: usemap[start] = use | use_begin;
305: use &= ~use_first;
306:
307: while (--len > 0)
308: if (usemap[++start] != use_not)
309: return 0;
310: else
311: usemap[start] = use;
312:
313:
314: usemap[start] = use | use_end;
315: }
316: else if ((usemap[start] & ~use_first) == ((use | use_begin) & ~use_first))
317: {
318:
319: if (use == use_he)
320: return 0;
321:
322: usemap[start] |= (use & use_first);
323: use &= ~use_first;
324:
325: while (--len > 1)
326: if (usemap[++start] != use)
327: return 0;
328:
329: if (usemap[++start] != (use | use_end))
330: return 0;
331: }
332: else
333:
334: return 0;
335:
336: return 1;
337: }
338:
339:
340:
341: static int
342: verify_persistent_db (void *mem, struct database_pers_head *readhead, int dbnr)
343: {
344: assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb);
345:
346: time_t now = time (NULL);
347:
348: struct database_pers_head *head = mem;
349: struct database_pers_head head_copy = *head;
350:
351:
352: if (readhead != NULL && memcmp (head, readhead, sizeof (*head)) != 0)
353: return 0;
354:
355:
356: if (head->version != DB_VERSION
357: || head->header_size != sizeof (*head)
358:
359:
360: || head->timestamp > now + 60 * 60 + 60
361: || (head->gc_cycle & 1)
362: || (size_t) head->module > INT32_MAX / sizeof (ref_t)
363: || (size_t) head->data_size > INT32_MAX - head->module * sizeof (ref_t)
364: || head->first_free < 0
365: || head->first_free > head->data_size
366: || (head->first_free & BLOCK_ALIGN_M1) != 0
367: || head->maxnentries < 0
368: || head->maxnsearched < 0)
369: return 0;
370:
371: uint8_t *usemap = calloc (head->first_free, 1);
372: if (usemap == NULL)
373: return 0;
374:
375: const char *data = (char *) &head->array[roundup (head->module,
376: ALIGN / sizeof (ref_t))];
377:
378: nscd_ssize_t he_cnt = 0;
379: for (nscd_ssize_t cnt = 0; cnt < head->module; ++cnt)
380: {
381: ref_t trail = head->array[cnt];
382: ref_t work = trail;
383: int tick = 0;
384:
385: while (work != ENDREF)
386: {
387: if (! check_use (data, head->first_free, usemap, use_he, work,
388: sizeof (struct hashentry)))
389: goto fail;
390:
391:
392: struct hashentry *here = (struct hashentry *) (data + work);
393:
394: ++he_cnt;
395:
396:
397: if (here->type >= LASTREQ
398: || reqinfo[here->type].db != &dbs[dbnr])
399: goto fail;
400:
401:
402: if (here->first != false && here->first != true)
403: goto fail;
404:
405: if (here->len < 0)
406: goto fail;
407:
408:
409: if (here->packet < 0
410: || here->packet > head->first_free
411: || here->packet + sizeof (struct datahead) > head->first_free)
412: goto fail;
413:
414: struct datahead *dh = (struct datahead *) (data + here->packet);
415:
416: if (! check_use (data, head->first_free, usemap,
417: use_data | (here->first ? use_first : 0),
418: here->packet, dh->allocsize))
419: goto fail;
420:
421: if (dh->allocsize < sizeof (struct datahead)
422: || dh->recsize > dh->allocsize
423: || (dh->notfound != false && dh->notfound != true)
424: || (dh->usable != false && dh->usable != true))
425: goto fail;
426:
427: if (here->key < here->packet + sizeof (struct datahead)
428: || here->key > here->packet + dh->allocsize
429: || here->key + here->len > here->packet + dh->allocsize)
430: {
431: #if SEPARATE_KEY
432:
433:
434: if (! check_use (data, head->first_free, usemap,
435: use_key | (here->first ? use_first : 0),
436: here->key, here->len))
437: #endif
438: goto fail;
439: }
440:
441: work = here->next;
442:
443: if (work == trail)
444:
445: goto fail;
446: if (tick)
447: trail = ((struct hashentry *) (data + trail))->next;
448: tick = 1 - tick;
449: }
450: }
451:
452: if (he_cnt != head->nentries)
453: goto fail;
454:
455:
456:
457: for (ref_t idx = 0; idx < head->first_free; ++idx)
458: {
459: #if SEPARATE_KEY
460: if (usemap[idx] == use_key_begin)
461: goto fail;
462: #endif
463: if (usemap[idx] == use_data_begin)
464: goto fail;
465: }
466:
467:
468: if (memcmp (mem, &head_copy, sizeof (*head)) != 0)
469: goto fail;
470:
471: free (usemap);
472: return 1;
473:
474: fail:
475: free (usemap);
476: return 0;
477: }
478:
479:
480: #ifdef O_CLOEXEC
481: # define EXTRA_O_FLAGS O_CLOEXEC
482: #else
483: # define EXTRA_O_FLAGS 0
484: #endif
485:
486:
487:
488: void
489: nscd_init (void)
490: {
491:
492:
493: if (server_user != NULL)
494: begin_drop_privileges ();
495:
496: if (nthreads == -1)
497:
498: nthreads = 2 * lastdb;
499:
500: for (size_t cnt = 0; cnt < lastdb; ++cnt)
501: if (dbs[cnt].enabled)
502: {
503: pthread_rwlock_init (&dbs[cnt].lock, NULL);
504: pthread_mutex_init (&dbs[cnt].memlock, NULL);
505:
506: if (dbs[cnt].persistent)
507: {
508:
509: int fd = open (dbs[cnt].db_filename, O_RDWR | EXTRA_O_FLAGS);
510: if (fd != -1)
511: {
512: struct stat64 st;
513: void *mem;
514: size_t total;
515: struct database_pers_head head;
516: ssize_t n = TEMP_FAILURE_RETRY (read (fd, &head,
517: sizeof (head)));
518: if (n != sizeof (head) || fstat64 (fd, &st) != 0)
519: {
520: fail_db:
521: dbg_log (_("invalid persistent database file \"%s\": %s"),
522: dbs[cnt].db_filename, strerror (errno));
523: unlink (dbs[cnt].db_filename);
524: }
525: else if (head.module == 0 && head.data_size == 0)
526: {
527:
528:
529: unlink (dbs[cnt].db_filename);
530: }
531: else if (head.header_size != (int) sizeof (head))
532: {
533: dbg_log (_("invalid persistent database file \"%s\": %s"),
534: dbs[cnt].db_filename,
535: _("header size does not match"));
536: unlink (dbs[cnt].db_filename);
537: }
538: else if ((total = (sizeof (head)
539: + roundup (head.module * sizeof (ref_t),
540: ALIGN)
541: + head.data_size))
542: > st.st_size
543: || total < sizeof (head))
544: {
545: dbg_log (_("invalid persistent database file \"%s\": %s"),
546: dbs[cnt].db_filename,
547: _("file size does not match"));
548: unlink (dbs[cnt].db_filename);
549: }
550:
551:
552:
553:
554:
555:
556: else if ((mem = mmap (NULL, dbs[cnt].max_db_size,
557: PROT_READ | PROT_WRITE,
558: MAP_SHARED,