1 /* Inner loops of cache daemon.
2 Copyright (C) 1998,1999,2000,2001,2002,2003 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
32 #include <arpa/inet.h>
33 #include <sys/param.h>
35 #include <sys/socket.h>
42 /* Wrapper functions with error checking for standard functions. */
43 extern void *xmalloc (size_t n
);
44 extern void *xcalloc (size_t n
, size_t s
);
45 extern void *xrealloc (void *o
, size_t n
);
47 /* Support to run nscd as an unprivileged user */
48 const char *server_user
;
49 static uid_t server_uid
;
50 static gid_t server_gid
;
51 const char *stat_user
;
53 static gid_t
*server_groups
;
57 static int server_ngroups
= NGROUPS
;
59 static void begin_drop_privileges (void);
60 static void finish_drop_privileges (void);
63 /* Mapping of request type to database. */
64 static const dbtype serv2db
[LASTDBREQ
+ 1] =
66 [GETPWBYNAME
] = pwddb
,
68 [GETGRBYNAME
] = grpdb
,
70 [GETHOSTBYNAME
] = hstdb
,
71 [GETHOSTBYNAMEv6
] = hstdb
,
72 [GETHOSTBYADDR
] = hstdb
,
73 [GETHOSTBYADDRv6
] = hstdb
,
76 /* Map request type to a string. */
77 const char *serv2str
[LASTREQ
] =
79 [GETPWBYNAME
] = "GETPWBYNAME",
80 [GETPWBYUID
] = "GETPWBYUID",
81 [GETGRBYNAME
] = "GETGRBYNAME",
82 [GETGRBYGID
] = "GETGRBYGID",
83 [GETHOSTBYNAME
] = "GETHOSTBYNAME",
84 [GETHOSTBYNAMEv6
] = "GETHOSTBYNAMEv6",
85 [GETHOSTBYADDR
] = "GETHOSTBYADDR",
86 [GETHOSTBYADDRv6
] = "GETHOSTBYADDRv6",
87 [SHUTDOWN
] = "SHUTDOWN",
88 [GETSTAT
] = "GETSTAT",
89 [INVALIDATE
] = "INVALIDATE"
92 /* The control data structures for the services. */
93 struct database dbs
[lastdb
] =
96 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
99 .filename
= "/etc/passwd",
101 .disabled_iov
= &pwd_iov_disabled
,
106 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
109 .filename
= "/etc/group",
111 .disabled_iov
= &grp_iov_disabled
,
116 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
119 .filename
= "/etc/hosts",
121 .disabled_iov
= &hst_iov_disabled
,
127 /* Number of seconds between two cache pruning runs. */
128 #define CACHE_PRUNE_INTERVAL 15
130 /* Number of threads to use. */
133 /* Socket for incoming connections. */
136 /* Number of times clients had to wait. */
137 unsigned long int client_queued
;
140 /* Initialize database information structures. */
144 struct sockaddr_un sock_addr
;
147 /* Secure mode and unprivileged mode are incompatible */
148 if (server_user
!= NULL
&& secure_in_use
)
150 dbg_log (_("Cannot run nscd in secure mode as unprivileged user"));
154 /* Look up unprivileged uid/gid/groups before we start listening on the
156 if (server_user
!= NULL
)
157 begin_drop_privileges ();
160 /* No configuration for this value, assume a default. */
161 nthreads
= 2 * lastdb
;
163 for (cnt
= 0; cnt
< lastdb
; ++cnt
)
164 if (dbs
[cnt
].enabled
)
166 pthread_rwlock_init (&dbs
[cnt
].lock
, NULL
);
168 dbs
[cnt
].array
= (struct hashentry
**)
169 calloc (dbs
[cnt
].module
, sizeof (struct hashentry
*));
170 if (dbs
[cnt
].array
== NULL
)
172 dbg_log (_("while allocating cache: %s"), strerror (errno
));
176 if (dbs
[cnt
].check_file
)
178 /* We need the modification date of the file. */
181 if (stat (dbs
[cnt
].filename
, &st
) < 0)
183 /* We cannot stat() the file, disable file checking. */
184 dbg_log (_("cannot stat() file `%s': %s"),
185 dbs
[cnt
].filename
, strerror (errno
));
186 dbs
[cnt
].check_file
= 0;
189 dbs
[cnt
].file_mtime
= st
.st_mtime
;
193 /* Create the socket. */
194 sock
= socket (AF_UNIX
, SOCK_STREAM
, 0);
197 dbg_log (_("cannot open socket: %s"), strerror (errno
));
200 /* Bind a name to the socket. */
201 sock_addr
.sun_family
= AF_UNIX
;
202 strcpy (sock_addr
.sun_path
, _PATH_NSCDSOCKET
);
203 if (bind (sock
, (struct sockaddr
*) &sock_addr
, sizeof (sock_addr
)) < 0)
205 dbg_log ("%s: %s", _PATH_NSCDSOCKET
, strerror (errno
));
209 /* Set permissions for the socket. */
210 chmod (_PATH_NSCDSOCKET
, 0666);
212 /* Set the socket up to accept connections. */
213 if (listen (sock
, SOMAXCONN
) < 0)
215 dbg_log (_("cannot enable socket to accept connections: %s"),
220 /* Change to unprivileged uid/gid/groups if specifed in config file */
221 if (server_user
!= NULL
)
222 finish_drop_privileges ();
226 /* Close the connections. */
235 invalidate_cache (char *key
)
239 if (strcmp (key
, "passwd") == 0)
241 else if (strcmp (key
, "group") == 0)
243 else if (__builtin_expect (strcmp (key
, "hosts"), 0) == 0)
248 if (dbs
[number
].enabled
)
249 prune_cache (&dbs
[number
], LONG_MAX
);
253 /* Handle new request. */
255 handle_request (int fd
, request_header
*req
, void *key
, uid_t uid
)
257 if (__builtin_expect (req
->version
, NSCD_VERSION
) != NSCD_VERSION
)
261 cannot handle old request version %d; current version is %d"),
262 req
->version
, NSCD_VERSION
);
266 if (__builtin_expect (req
->type
, GETPWBYNAME
) >= GETPWBYNAME
267 && __builtin_expect (req
->type
, LASTDBREQ
) <= LASTDBREQ
)
269 struct hashentry
*cached
;
270 struct database
*db
= &dbs
[serv2db
[req
->type
]];
272 if (__builtin_expect (debug_level
, 0) > 0)
274 if (req
->type
== GETHOSTBYADDR
|| req
->type
== GETHOSTBYADDRv6
)
276 char buf
[INET6_ADDRSTRLEN
];
278 dbg_log ("\t%s (%s)", serv2str
[req
->type
],
279 inet_ntop (req
->type
== GETHOSTBYADDR
280 ? AF_INET
: AF_INET6
,
281 key
, buf
, sizeof (buf
)));
284 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
287 /* Is this service enabled? */
290 /* No, sent the prepared record. */
291 if (TEMP_FAILURE_RETRY (write (fd
, db
->disabled_iov
->iov_base
,
292 db
->disabled_iov
->iov_len
))
293 != (ssize_t
) db
->disabled_iov
->iov_len
294 && __builtin_expect (debug_level
, 0) > 0)
296 /* We have problems sending the result. */
298 dbg_log (_("cannot write result: %s"),
299 strerror_r (errno
, buf
, sizeof (buf
)));
305 /* Be sure we can read the data. */
306 if (__builtin_expect (pthread_rwlock_tryrdlock (&db
->lock
) != 0, 0))
309 pthread_rwlock_rdlock (&db
->lock
);
312 /* See whether we can handle it from the cache. */
313 cached
= (struct hashentry
*) cache_search (req
->type
, key
, req
->key_len
,
317 /* Hurray it's in the cache. */
318 if (TEMP_FAILURE_RETRY (write (fd
, cached
->packet
, cached
->total
))
320 && __builtin_expect (debug_level
, 0) > 0)
322 /* We have problems sending the result. */
324 dbg_log (_("cannot write result: %s"),
325 strerror_r (errno
, buf
, sizeof (buf
)));
328 pthread_rwlock_unlock (&db
->lock
);
333 pthread_rwlock_unlock (&db
->lock
);
335 else if (__builtin_expect (debug_level
, 0) > 0)
337 if (req
->type
== INVALIDATE
)
338 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
340 dbg_log ("\t%s", serv2str
[req
->type
]);
343 /* Handle the request. */
347 addpwbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
351 addpwbyuid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
355 addgrbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
359 addgrbygid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
363 addhstbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
366 case GETHOSTBYNAMEv6
:
367 addhstbynamev6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
371 addhstbyaddr (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
374 case GETHOSTBYADDRv6
:
375 addhstbyaddrv6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
383 /* Get the callers credentials. */
386 socklen_t optlen
= sizeof (caller
);
388 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) < 0)
392 dbg_log (_("error getting callers id: %s"),
393 strerror_r (errno
, buf
, sizeof (buf
)));
399 /* Some systems have no SO_PEERCRED implementation. They don't
400 care about security so we don't as well. */
405 /* Accept shutdown, getstat and invalidate only from root. For
406 the stat call also allow the user specified in the config file. */
407 if (req
->type
== GETSTAT
)
409 if (uid
== 0 || uid
== stat_uid
)
410 send_stats (fd
, dbs
);
414 if (req
->type
== INVALIDATE
)
415 invalidate_cache (key
);
417 termination_handler (0);
422 /* Ignore the command, it's nothing we know. */
428 /* This is the main loop. It is replicated in different threads but the
429 `poll' call makes sure only one thread handles an incoming connection. */
431 __attribute__ ((__noreturn__
))
434 long int my_number
= (long int) p
;
436 int run_prune
= my_number
< lastdb
&& dbs
[my_number
].enabled
;
437 time_t next_prune
= run_prune
? time (NULL
) + CACHE_PRUNE_INTERVAL
: 0;
438 static unsigned long int nready
;
441 conn
.events
= POLLRDNORM
;
447 /* One more thread available. */
448 atomic_increment (&nready
);
454 time_t now
= time (NULL
);
455 int timeout
= now
< next_prune
? 1000 * (next_prune
- now
) : 0;
457 nr
= poll (&conn
, 1, timeout
);
461 /* The `poll' call timed out. It's time to clean up the
463 atomic_decrement (&nready
);
464 assert (my_number
< lastdb
);
465 prune_cache (&dbs
[my_number
], time(NULL
));
467 next_prune
= now
+ CACHE_PRUNE_INTERVAL
;
471 while ((conn
.revents
& POLLRDNORM
) == 0);
474 /* We have a new incoming connection. Accept the connection. */
475 int fd
= TEMP_FAILURE_RETRY (accept (conn
.fd
, NULL
, NULL
));
483 if (__builtin_expect (fd
, 0) < 0)
485 dbg_log (_("while accepting connection: %s"),
486 strerror_r (errno
, buf
, sizeof (buf
)));
490 /* This thread is busy. */
491 atomic_decrement (&nready
);
493 /* Now read the request. */
494 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, &req
, sizeof (req
)))
498 dbg_log (_("short read while reading request: %s"),
499 strerror_r (errno
, buf
, sizeof (buf
)));
504 /* Some systems have no SO_PEERCRED implementation. They don't
505 care about security so we don't as well. */
510 socklen_t optlen
= sizeof (caller
);
512 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) < 0)
514 dbg_log (_("error getting callers id: %s"),
515 strerror_r (errno
, buf
, sizeof (buf
)));
520 if (req
.type
< GETPWBYNAME
|| req
.type
> LASTDBREQ
521 || secure
[serv2db
[req
.type
]])
526 else if (__builtin_expect (debug_level
> 0, 0))
529 socklen_t optlen
= sizeof (caller
);
531 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) == 0)
536 /* It should not be possible to crash the nscd with a silly
537 request (i.e., a terribly large key). We limit the size to 1kb. */
538 if (__builtin_expect (req
.key_len
, 1) < 0
539 || __builtin_expect (req
.key_len
, 1) > 1024)
542 dbg_log (_("key length in request too long: %d"), req
.key_len
);
549 char keybuf
[req
.key_len
];
551 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, keybuf
,
556 dbg_log (_("short read while reading request key: %s"),
557 strerror_r (errno
, buf
, sizeof (buf
)));
562 if (__builtin_expect (debug_level
, 0) > 0)
567 handle_request: request received (Version = %d) from PID %ld"),
568 req
.version
, (long int) pid
);
572 handle_request: request received (Version = %d)"), req
.version
);
575 /* Phew, we got all the data, now process it. */
576 handle_request (fd
, &req
, keybuf
, uid
);
582 /* Just determine whether any data is present. We do this to
583 measure whether clients are queued up. */
585 nr
= poll (&conn
, 1, 0);
591 atomic_increment (&nready
);
599 /* Start all the threads we want. The initial process is thread no. 1. */
607 pthread_attr_init (&attr
);
608 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
610 /* We allow less than LASTDB threads only for debugging. */
611 if (debug_level
== 0)
612 nthreads
= MAX (nthreads
, lastdb
);
614 for (i
= 1; i
< nthreads
; ++i
)
615 pthread_create (&th
, &attr
, nscd_run
, (void *) i
);
617 pthread_attr_destroy (&attr
);
619 nscd_run ((void *) 0);
623 /* Look up the uid, gid, and supplementary groups to run nscd as. When
624 this function is called, we are not listening on the nscd socket yet so
625 we can just use the ordinary lookup functions without causing a lockup */
627 begin_drop_privileges (void)
631 pwd
= getpwnam (server_user
);
635 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
636 error (EXIT_FAILURE
, 0, _("Failed to run nscd as user '%s'"),
640 server_uid
= pwd
->pw_uid
;
641 server_gid
= pwd
->pw_gid
;
643 server_groups
= (gid_t
*) xmalloc (server_ngroups
* sizeof (gid_t
));
645 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
649 server_groups
= (gid_t
*) xrealloc (server_groups
,
650 server_ngroups
* sizeof (gid_t
));
652 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
655 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
656 error (EXIT_FAILURE
, errno
, _("getgrouplist failed"));
661 /* Call setgroups(), setgid(), and setuid() to drop root privileges and
662 run nscd as the user specified in the configuration file. */
664 finish_drop_privileges (void)
666 if (setgroups (server_ngroups
, server_groups
) == -1)
668 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
669 error (EXIT_FAILURE
, errno
, _("setgroups failed"));
672 if (setgid (server_gid
) == -1)
674 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
679 if (setuid (server_uid
) == -1)
681 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);