1 /* Inner loops of cache daemon.
2 Copyright (C) 1998-2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
34 #include <arpa/inet.h>
35 #include <sys/param.h>
37 #include <sys/socket.h>
44 /* Wrapper functions with error checking for standard functions. */
45 extern void *xmalloc (size_t n
);
46 extern void *xcalloc (size_t n
, size_t s
);
47 extern void *xrealloc (void *o
, size_t n
);
49 /* Support to run nscd as an unprivileged user */
50 const char *server_user
;
51 static uid_t server_uid
;
52 static gid_t server_gid
;
53 const char *stat_user
;
55 static gid_t
*server_groups
;
59 static int server_ngroups
= NGROUPS
;
61 static void begin_drop_privileges (void);
62 static void finish_drop_privileges (void);
65 /* Mapping of request type to database. */
66 static const dbtype serv2db
[LASTDBREQ
+ 1] =
68 [GETPWBYNAME
] = pwddb
,
70 [GETGRBYNAME
] = grpdb
,
72 [GETHOSTBYNAME
] = hstdb
,
73 [GETHOSTBYNAMEv6
] = hstdb
,
74 [GETHOSTBYADDR
] = hstdb
,
75 [GETHOSTBYADDRv6
] = hstdb
,
78 /* Map request type to a string. */
79 const char *serv2str
[LASTREQ
] =
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"
94 /* The control data structures for the services. */
95 struct database dbs
[lastdb
] =
98 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
101 .filename
= "/etc/passwd",
103 .disabled_iov
= &pwd_iov_disabled
,
108 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
111 .filename
= "/etc/group",
113 .disabled_iov
= &grp_iov_disabled
,
118 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
121 .filename
= "/etc/hosts",
123 .disabled_iov
= &hst_iov_disabled
,
129 /* Number of seconds between two cache pruning runs. */
130 #define CACHE_PRUNE_INTERVAL 15
132 /* Number of threads to use. */
135 /* Socket for incoming connections. */
138 /* Number of times clients had to wait. */
139 unsigned long int client_queued
;
142 /* Initialize database information structures. */
146 struct sockaddr_un sock_addr
;
149 /* Secure mode and unprivileged mode are incompatible */
150 if (server_user
!= NULL
&& secure_in_use
)
152 dbg_log (_("Cannot run nscd in secure mode as unprivileged user"));
156 /* Look up unprivileged uid/gid/groups before we start listening on the
158 if (server_user
!= NULL
)
159 begin_drop_privileges ();
162 /* No configuration for this value, assume a default. */
163 nthreads
= 2 * lastdb
;
165 for (cnt
= 0; cnt
< lastdb
; ++cnt
)
166 if (dbs
[cnt
].enabled
)
168 pthread_rwlock_init (&dbs
[cnt
].lock
, NULL
);
170 dbs
[cnt
].array
= (struct hashentry
**)
171 calloc (dbs
[cnt
].module
, sizeof (struct hashentry
*));
172 if (dbs
[cnt
].array
== NULL
)
174 dbg_log (_("while allocating cache: %s"), strerror (errno
));
178 if (dbs
[cnt
].check_file
)
180 /* We need the modification date of the file. */
183 if (stat (dbs
[cnt
].filename
, &st
) < 0)
185 /* We cannot stat() the file, disable file checking. */
186 dbg_log (_("cannot stat() file `%s': %s"),
187 dbs
[cnt
].filename
, strerror (errno
));
188 dbs
[cnt
].check_file
= 0;
191 dbs
[cnt
].file_mtime
= st
.st_mtime
;
195 /* Create the socket. */
196 sock
= socket (AF_UNIX
, SOCK_STREAM
, 0);
199 dbg_log (_("cannot open socket: %s"), strerror (errno
));
202 /* Bind a name to the socket. */
203 sock_addr
.sun_family
= AF_UNIX
;
204 strcpy (sock_addr
.sun_path
, _PATH_NSCDSOCKET
);
205 if (bind (sock
, (struct sockaddr
*) &sock_addr
, sizeof (sock_addr
)) < 0)
207 dbg_log ("%s: %s", _PATH_NSCDSOCKET
, strerror (errno
));
211 /* We don't wait for data otherwise races between threads can get
212 them stuck on accept. */
213 int fl
= fcntl (sock
, F_GETFL
);
215 fcntl (sock
, F_SETFL
, fl
| O_NONBLOCK
);
217 /* Set permissions for the socket. */
218 chmod (_PATH_NSCDSOCKET
, 0666);
220 /* Set the socket up to accept connections. */
221 if (listen (sock
, SOMAXCONN
) < 0)
223 dbg_log (_("cannot enable socket to accept connections: %s"),
228 /* Change to unprivileged uid/gid/groups if specifed in config file */
229 if (server_user
!= NULL
)
230 finish_drop_privileges ();
234 /* Close the connections. */
243 invalidate_cache (char *key
)
247 if (strcmp (key
, "passwd") == 0)
249 else if (strcmp (key
, "group") == 0)
251 else if (__builtin_expect (strcmp (key
, "hosts"), 0) == 0)
255 /* Re-initialize the resolver. resolv.conf might have changed. */
261 if (dbs
[number
].enabled
)
262 prune_cache (&dbs
[number
], LONG_MAX
);
266 /* Handle new request. */
268 handle_request (int fd
, request_header
*req
, void *key
, uid_t uid
)
270 if (__builtin_expect (req
->version
, NSCD_VERSION
) != NSCD_VERSION
)
274 cannot handle old request version %d; current version is %d"),
275 req
->version
, NSCD_VERSION
);
279 if (__builtin_expect (req
->type
, GETPWBYNAME
) >= GETPWBYNAME
280 && __builtin_expect (req
->type
, LASTDBREQ
) <= LASTDBREQ
)
282 struct hashentry
*cached
;
283 struct database
*db
= &dbs
[serv2db
[req
->type
]];
285 if (__builtin_expect (debug_level
, 0) > 0)
287 if (req
->type
== GETHOSTBYADDR
|| req
->type
== GETHOSTBYADDRv6
)
289 char buf
[INET6_ADDRSTRLEN
];
291 dbg_log ("\t%s (%s)", serv2str
[req
->type
],
292 inet_ntop (req
->type
== GETHOSTBYADDR
293 ? AF_INET
: AF_INET6
,
294 key
, buf
, sizeof (buf
)));
297 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
300 /* Is this service enabled? */
303 /* No, sent the prepared record. */
304 if (TEMP_FAILURE_RETRY (write (fd
, db
->disabled_iov
->iov_base
,
305 db
->disabled_iov
->iov_len
))
306 != (ssize_t
) db
->disabled_iov
->iov_len
307 && __builtin_expect (debug_level
, 0) > 0)
309 /* We have problems sending the result. */
311 dbg_log (_("cannot write result: %s"),
312 strerror_r (errno
, buf
, sizeof (buf
)));
318 /* Be sure we can read the data. */
319 if (__builtin_expect (pthread_rwlock_tryrdlock (&db
->lock
) != 0, 0))
322 pthread_rwlock_rdlock (&db
->lock
);
325 /* See whether we can handle it from the cache. */
326 cached
= (struct hashentry
*) cache_search (req
->type
, key
, req
->key_len
,
330 /* Hurray it's in the cache. */
331 if (TEMP_FAILURE_RETRY (write (fd
, cached
->packet
, cached
->total
))
333 && __builtin_expect (debug_level
, 0) > 0)
335 /* We have problems sending the result. */
337 dbg_log (_("cannot write result: %s"),
338 strerror_r (errno
, buf
, sizeof (buf
)));
341 pthread_rwlock_unlock (&db
->lock
);
346 pthread_rwlock_unlock (&db
->lock
);
348 else if (__builtin_expect (debug_level
, 0) > 0)
350 if (req
->type
== INVALIDATE
)
351 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
352 else if (req
->type
> LASTDBREQ
&& req
->type
< LASTREQ
)
353 dbg_log ("\t%s", serv2str
[req
->type
]);
355 dbg_log (_("\tinvalid request type %d"), req
->type
);
358 /* Handle the request. */
362 addpwbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
366 addpwbyuid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
370 addgrbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
374 addgrbygid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
378 addhstbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
381 case GETHOSTBYNAMEv6
:
382 addhstbynamev6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
386 addhstbyaddr (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
389 case GETHOSTBYADDRv6
:
390 addhstbyaddrv6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
398 /* Get the callers credentials. */
401 socklen_t optlen
= sizeof (caller
);
403 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) < 0)
407 dbg_log (_("error getting callers id: %s"),
408 strerror_r (errno
, buf
, sizeof (buf
)));
414 /* Some systems have no SO_PEERCRED implementation. They don't
415 care about security so we don't as well. */
420 /* Accept shutdown, getstat and invalidate only from root. For
421 the stat call also allow the user specified in the config file. */
422 if (req
->type
== GETSTAT
)
424 if (uid
== 0 || uid
== stat_uid
)
425 send_stats (fd
, dbs
);
429 if (req
->type
== INVALIDATE
)
430 invalidate_cache (key
);
432 termination_handler (0);
437 /* Ignore the command, it's nothing we know. */
443 /* This is the main loop. It is replicated in different threads but the
444 `poll' call makes sure only one thread handles an incoming connection. */
446 __attribute__ ((__noreturn__
))
449 long int my_number
= (long int) p
;
451 int run_prune
= my_number
< lastdb
&& dbs
[my_number
].enabled
;
452 time_t next_prune
= run_prune
? time (NULL
) + CACHE_PRUNE_INTERVAL
: 0;
453 static unsigned long int nready
;
456 conn
.events
= POLLRDNORM
;
463 /* One more thread available. */
464 atomic_increment (&nready
);
473 timeout
= now
< next_prune
? 1000 * (next_prune
- now
) : 0;
476 nr
= poll (&conn
, 1, timeout
);
480 /* The `poll' call timed out. It's time to clean up the
482 atomic_decrement (&nready
);
483 assert (my_number
< lastdb
);
484 prune_cache (&dbs
[my_number
], time(NULL
));
486 next_prune
= now
+ CACHE_PRUNE_INTERVAL
;
490 while ((conn
.revents
& POLLRDNORM
) == 0);
493 /* We have a new incoming connection. Accept the connection. */
494 int fd
= TEMP_FAILURE_RETRY (accept (conn
.fd
, NULL
, NULL
));
502 if (__builtin_expect (fd
, 0) < 0)
504 if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
)
505 dbg_log (_("while accepting connection: %s"),
506 strerror_r (errno
, buf
, sizeof (buf
)));
510 /* This thread is busy. */
511 atomic_decrement (&nready
);
513 /* Now read the request. */
514 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, &req
, sizeof (req
)))
518 dbg_log (_("short read while reading request: %s"),
519 strerror_r (errno
, buf
, sizeof (buf
)));
524 /* Some systems have no SO_PEERCRED implementation. They don't
525 care about security so we don't as well. */
530 socklen_t optlen
= sizeof (caller
);
532 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) < 0)
534 dbg_log (_("error getting callers id: %s"),
535 strerror_r (errno
, buf
, sizeof (buf
)));
540 if (req
.type
< GETPWBYNAME
|| req
.type
> LASTDBREQ
541 || secure
[serv2db
[req
.type
]])
546 else if (__builtin_expect (debug_level
> 0, 0))
549 socklen_t optlen
= sizeof (caller
);
551 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) == 0)
556 /* It should not be possible to crash the nscd with a silly
557 request (i.e., a terribly large key). We limit the size to 1kb. */
558 if (__builtin_expect (req
.key_len
, 1) < 0
559 || __builtin_expect (req
.key_len
, 1) > 1024)
562 dbg_log (_("key length in request too long: %d"), req
.key_len
);
569 char keybuf
[req
.key_len
];
571 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, keybuf
,
576 dbg_log (_("short read while reading request key: %s"),
577 strerror_r (errno
, buf
, sizeof (buf
)));
582 if (__builtin_expect (debug_level
, 0) > 0)
587 handle_request: request received (Version = %d) from PID %ld"),
588 req
.version
, (long int) pid
);
592 handle_request: request received (Version = %d)"), req
.version
);
595 /* Phew, we got all the data, now process it. */
596 handle_request (fd
, &req
, keybuf
, uid
);
602 /* Just determine whether any data is present. We do this to
603 measure whether clients are queued up. */
605 nr
= poll (&conn
, 1, 0);
611 atomic_increment (&nready
);
619 /* Start all the threads we want. The initial process is thread no. 1. */
627 pthread_attr_init (&attr
);
628 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
630 /* We allow less than LASTDB threads only for debugging. */
631 if (debug_level
== 0)
632 nthreads
= MAX (nthreads
, lastdb
);
634 for (i
= 1; i
< nthreads
; ++i
)
635 pthread_create (&th
, &attr
, nscd_run
, (void *) i
);
637 pthread_attr_destroy (&attr
);
639 nscd_run ((void *) 0);
643 /* Look up the uid, gid, and supplementary groups to run nscd as. When
644 this function is called, we are not listening on the nscd socket yet so
645 we can just use the ordinary lookup functions without causing a lockup */
647 begin_drop_privileges (void)
651 pwd
= getpwnam (server_user
);
655 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
656 error (EXIT_FAILURE
, 0, _("Failed to run nscd as user '%s'"),
660 server_uid
= pwd
->pw_uid
;
661 server_gid
= pwd
->pw_gid
;
663 server_groups
= (gid_t
*) xmalloc (server_ngroups
* sizeof (gid_t
));
665 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
669 server_groups
= (gid_t
*) xrealloc (server_groups
,
670 server_ngroups
* sizeof (gid_t
));
672 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
675 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
676 error (EXIT_FAILURE
, errno
, _("getgrouplist failed"));
681 /* Call setgroups(), setgid(), and setuid() to drop root privileges and
682 run nscd as the user specified in the configuration file. */
684 finish_drop_privileges (void)
686 if (setgroups (server_ngroups
, server_groups
) == -1)
688 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
689 error (EXIT_FAILURE
, errno
, _("setgroups failed"));
692 if (setgid (server_gid
) == -1)
694 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
699 if (setuid (server_uid
) == -1)
701 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);