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
31 #include <arpa/inet.h>
32 #include <sys/param.h>
34 #include <sys/socket.h>
41 /* Wrapper functions with error checking for standard functions. */
42 extern void *xmalloc (size_t n
);
43 extern void *xcalloc (size_t n
, size_t s
);
44 extern void *xrealloc (void *o
, size_t n
);
46 /* Support to run nscd as an unprivileged user */
47 const char *server_user
;
48 static uid_t server_uid
;
49 static gid_t server_gid
;
50 static gid_t
*server_groups
;
54 static int server_ngroups
= NGROUPS
;
56 static void begin_drop_privileges (void);
57 static void finish_drop_privileges (void);
60 /* Mapping of request type to database. */
61 static const dbtype serv2db
[LASTDBREQ
+ 1] =
63 [GETPWBYNAME
] = pwddb
,
65 [GETGRBYNAME
] = grpdb
,
67 [GETHOSTBYNAME
] = hstdb
,
68 [GETHOSTBYNAMEv6
] = hstdb
,
69 [GETHOSTBYADDR
] = hstdb
,
70 [GETHOSTBYADDRv6
] = hstdb
,
73 /* Map request type to a string. */
74 const char *serv2str
[LASTREQ
] =
76 [GETPWBYNAME
] = "GETPWBYNAME",
77 [GETPWBYUID
] = "GETPWBYUID",
78 [GETGRBYNAME
] = "GETGRBYNAME",
79 [GETGRBYGID
] = "GETGRBYGID",
80 [GETHOSTBYNAME
] = "GETHOSTBYNAME",
81 [GETHOSTBYNAMEv6
] = "GETHOSTBYNAMEv6",
82 [GETHOSTBYADDR
] = "GETHOSTBYADDR",
83 [GETHOSTBYADDRv6
] = "GETHOSTBYADDRv6",
84 [SHUTDOWN
] = "SHUTDOWN",
85 [GETSTAT
] = "GETSTAT",
86 [INVALIDATE
] = "INVALIDATE"
89 /* The control data structures for the services. */
90 static struct database dbs
[lastdb
] =
93 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
96 .filename
= "/etc/passwd",
98 .disabled_iov
= &pwd_iov_disabled
,
103 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
106 .filename
= "/etc/group",
108 .disabled_iov
= &grp_iov_disabled
,
113 .lock
= PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
,
116 .filename
= "/etc/hosts",
118 .disabled_iov
= &hst_iov_disabled
,
124 /* Number of seconds between two cache pruning runs. */
125 #define CACHE_PRUNE_INTERVAL 15
127 /* Number of threads to use. */
130 /* Socket for incoming connections. */
134 /* Initialize database information structures. */
136 nscd_init (const char *conffile
)
138 struct sockaddr_un sock_addr
;
141 /* Read the configuration file. */
142 if (nscd_parse_file (conffile
, dbs
) != 0)
144 /* We couldn't read the configuration file. Disable all services
145 by shutting down the srever. */
146 dbg_log (_("cannot read configuration file; this is fatal"));
150 /* Secure mode and unprivileged mode are incompatible */
151 if (server_user
!= NULL
&& secure_in_use
)
153 dbg_log (_("Cannot run nscd in secure mode as unprivileged user"));
157 /* Look up unprivileged uid/gid/groups before we start listening on the
159 if (server_user
!= NULL
)
160 begin_drop_privileges ();
163 /* No configuration for this value, assume a default. */
164 nthreads
= 2 * lastdb
;
166 for (cnt
= 0; cnt
< lastdb
; ++cnt
)
167 if (dbs
[cnt
].enabled
)
169 pthread_rwlock_init (&dbs
[cnt
].lock
, NULL
);
171 dbs
[cnt
].array
= (struct hashentry
**)
172 calloc (dbs
[cnt
].module
, sizeof (struct hashentry
*));
173 if (dbs
[cnt
].array
== NULL
)
175 dbg_log (_("while allocating cache: %s"), strerror (errno
));
179 if (dbs
[cnt
].check_file
)
181 /* We need the modification date of the file. */
184 if (stat (dbs
[cnt
].filename
, &st
) < 0)
186 /* We cannot stat() the file, disable file checking. */
187 dbg_log (_("cannot stat() file `%s': %s"),
188 dbs
[cnt
].filename
, strerror (errno
));
189 dbs
[cnt
].check_file
= 0;
192 dbs
[cnt
].file_mtime
= st
.st_mtime
;
196 /* Create the socket. */
197 sock
= socket (AF_UNIX
, SOCK_STREAM
, 0);
200 dbg_log (_("cannot open socket: %s"), strerror (errno
));
203 /* Bind a name to the socket. */
204 sock_addr
.sun_family
= AF_UNIX
;
205 strcpy (sock_addr
.sun_path
, _PATH_NSCDSOCKET
);
206 if (bind (sock
, (struct sockaddr
*) &sock_addr
, sizeof (sock_addr
)) < 0)
208 dbg_log ("%s: %s", _PATH_NSCDSOCKET
, strerror (errno
));
212 /* Set permissions for the socket. */
213 chmod (_PATH_NSCDSOCKET
, 0666);
215 /* Set the socket up to accept connections. */
216 if (listen (sock
, SOMAXCONN
) < 0)
218 dbg_log (_("cannot enable socket to accept connections: %s"),
223 /* Change to unprivileged uid/gid/groups if specifed in config file */
224 if (server_user
!= NULL
)
225 finish_drop_privileges ();
229 /* Close the connections. */
237 invalidate_cache (char *key
)
241 if (strcmp (key
, "passwd") == 0)
243 else if (strcmp (key
, "group") == 0)
245 else if (__builtin_expect (strcmp (key
, "hosts"), 0) == 0)
250 if (dbs
[number
].enabled
)
251 prune_cache (&dbs
[number
], LONG_MAX
);
255 /* Handle new request. */
257 handle_request (int fd
, request_header
*req
, void *key
, uid_t uid
)
259 if (__builtin_expect (req
->version
, NSCD_VERSION
) != NSCD_VERSION
)
263 cannot handle old request version %d; current version is %d"),
264 req
->version
, NSCD_VERSION
);
268 if (__builtin_expect (req
->type
, GETPWBYNAME
) >= GETPWBYNAME
269 && __builtin_expect (req
->type
, LASTDBREQ
) <= LASTDBREQ
)
271 struct hashentry
*cached
;
272 struct database
*db
= &dbs
[serv2db
[req
->type
]];
274 if (__builtin_expect (debug_level
, 0) > 0)
276 if (req
->type
== GETHOSTBYADDR
|| req
->type
== GETHOSTBYADDRv6
)
278 char buf
[INET6_ADDRSTRLEN
];
280 dbg_log ("\t%s (%s)", serv2str
[req
->type
],
281 inet_ntop (req
->type
== GETHOSTBYADDR
282 ? AF_INET
: AF_INET6
,
283 key
, buf
, sizeof (buf
)));
286 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
289 /* Is this service enabled? */
292 /* No, sent the prepared record. */
293 if (TEMP_FAILURE_RETRY (write (fd
, db
->disabled_iov
->iov_base
,
294 db
->disabled_iov
->iov_len
))
295 != (ssize_t
) db
->disabled_iov
->iov_len
296 && __builtin_expect (debug_level
, 0) > 0)
298 /* We have problems sending the result. */
300 dbg_log (_("cannot write result: %s"),
301 strerror_r (errno
, buf
, sizeof (buf
)));
307 /* Be sure we can read the data. */
308 if (__builtin_expect (pthread_rwlock_tryrdlock (&db
->lock
) != 0, 0))
311 pthread_rwlock_rdlock (&db
->lock
);
314 /* See whether we can handle it from the cache. */
315 cached
= (struct hashentry
*) cache_search (req
->type
, key
, req
->key_len
,
319 /* Hurray it's in the cache. */
320 if (TEMP_FAILURE_RETRY (write (fd
, cached
->packet
, cached
->total
))
322 && __builtin_expect (debug_level
, 0) > 0)
324 /* We have problems sending the result. */
326 dbg_log (_("cannot write result: %s"),
327 strerror_r (errno
, buf
, sizeof (buf
)));
330 pthread_rwlock_unlock (&db
->lock
);
335 pthread_rwlock_unlock (&db
->lock
);
337 else if (__builtin_expect (debug_level
, 0) > 0)
339 if (req
->type
== INVALIDATE
)
340 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
342 dbg_log ("\t%s", serv2str
[req
->type
]);
345 /* Handle the request. */
349 addpwbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
353 addpwbyuid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
357 addgrbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
361 addgrbygid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
365 addhstbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
368 case GETHOSTBYNAMEv6
:
369 addhstbynamev6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
373 addhstbyaddr (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
376 case GETHOSTBYADDRv6
:
377 addhstbyaddrv6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
383 /* Accept shutdown, getstat and invalidate only from root */
384 if (secure_in_use
&& uid
== 0)
386 if (req
->type
== GETSTAT
)
387 send_stats (fd
, dbs
);
388 else if (req
->type
== INVALIDATE
)
389 invalidate_cache (key
);
391 termination_handler (0);
395 /* Some systems have no SO_PEERCRED implementation. They don't
396 care about security so we don't as well. */
399 socklen_t optlen
= sizeof (caller
);
401 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) < 0)
405 dbg_log (_("error getting callers id: %s"),
406 strerror_r (errno
, buf
, sizeof (buf
)));
412 if (req
->type
== GETSTAT
)
413 send_stats (fd
, dbs
);
414 else if (req
->type
== INVALIDATE
)
415 invalidate_cache (key
);
417 termination_handler (0);
423 /* Ignore the command, it's nothing we know. */
429 /* This is the main loop. It is replicated in different threads but the
430 `poll' call makes sure only one thread handles an incoming connection. */
432 __attribute__ ((__noreturn__
))
435 long int my_number
= (long int) p
;
437 int run_prune
= my_number
< lastdb
&& dbs
[my_number
].enabled
;
438 time_t now
= time (NULL
);
439 time_t next_prune
= now
+ CACHE_PRUNE_INTERVAL
;
440 int timeout
= run_prune
? 1000 * (next_prune
- now
) : -1;
443 conn
.events
= POLLRDNORM
;
447 int nr
= poll (&conn
, 1, timeout
);
451 /* The `poll' call timed out. It's time to clean up the cache. */
452 assert (my_number
< lastdb
);
454 prune_cache (&dbs
[my_number
], now
);
455 next_prune
= now
+ CACHE_PRUNE_INTERVAL
;
456 timeout
= 1000 * (next_prune
- now
);
460 /* We have a new incoming connection. */
461 if (conn
.revents
& (POLLRDNORM
|POLLERR
|POLLHUP
|POLLNVAL
))
463 /* Accept the connection. */
464 int fd
= TEMP_FAILURE_RETRY (accept (conn
.fd
, NULL
, NULL
));
472 if (__builtin_expect (fd
, 0) < 0)
474 dbg_log (_("while accepting connection: %s"),
475 strerror_r (errno
, buf
, sizeof (buf
)));
479 /* Now read the request. */
480 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, &req
,
485 dbg_log (_("short read while reading request: %s"),
486 strerror_r (errno
, buf
, sizeof (buf
)));
491 /* Some systems have no SO_PEERCRED implementation. They don't
492 care about security so we don't as well. */
497 socklen_t optlen
= sizeof (caller
);
499 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
,
500 &caller
, &optlen
) < 0)
502 dbg_log (_("error getting callers id: %s"),
503 strerror_r (errno
, buf
, sizeof (buf
)));
508 if (req
.type
< GETPWBYNAME
|| req
.type
> LASTDBREQ
509 || secure
[serv2db
[req
.type
]])
514 else if (__builtin_expect (debug_level
> 0, 0))
517 socklen_t optlen
= sizeof (caller
);
519 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
,
520 &caller
, &optlen
) == 0)
525 /* It should not be possible to crash the nscd with a silly
526 request (i.e., a terribly large key). We limit the size
528 if (__builtin_expect (req
.key_len
, 1) < 0
529 || __builtin_expect (req
.key_len
, 1) > 1024)
532 dbg_log (_("key length in request too long: %d"), req
.key_len
);
539 char keybuf
[req
.key_len
];
541 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, keybuf
,
546 dbg_log (_("short read while reading request key: %s"),
547 strerror_r (errno
, buf
, sizeof (buf
)));
552 if (__builtin_expect (debug_level
, 0) > 0)
557 handle_request: request received (Version = %d) from PID %ld"),
558 req
.version
, (long int) pid
);
562 handle_request: request received (Version = %d)"), req
.version
);
565 /* Phew, we got all the data, now process it. */
566 handle_request (fd
, &req
, keybuf
, uid
);
576 timeout
= now
< next_prune
? 1000 * (next_prune
- now
) : 0;
582 /* Start all the threads we want. The initial process is thread no. 1. */
590 pthread_attr_init (&attr
);
591 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
593 /* We allow less than LASTDB threads only for debugging. */
594 if (debug_level
== 0)
595 nthreads
= MAX (nthreads
, lastdb
);
597 for (i
= 1; i
< nthreads
; ++i
)
598 pthread_create (&th
, &attr
, nscd_run
, (void *) i
);
600 pthread_attr_destroy (&attr
);
602 nscd_run ((void *) 0);
606 /* Look up the uid, gid, and supplementary groups to run nscd as. When
607 this function is called, we are not listening on the nscd socket yet so
608 we can just use the ordinary lookup functions without causing a lockup */
610 begin_drop_privileges (void)
614 pwd
= getpwnam (server_user
);
618 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
619 error (EXIT_FAILURE
, 0, _("Failed to run nscd as user '%s'"),
623 server_uid
= pwd
->pw_uid
;
624 server_gid
= pwd
->pw_gid
;
626 server_groups
= (gid_t
*) xmalloc (server_ngroups
* sizeof (gid_t
));
628 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
632 server_groups
= (gid_t
*) xrealloc (server_groups
,
633 server_ngroups
* sizeof (gid_t
));
635 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
638 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
639 error (EXIT_FAILURE
, errno
, _("getgrouplist failed"));
644 /* Call setgroups(), setgid(), and setuid() to drop root privileges and
645 run nscd as the user specified in the configuration file. */
647 finish_drop_privileges (void)
649 if (setgroups (server_ngroups
, server_groups
) == -1)
651 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
652 error (EXIT_FAILURE
, errno
, _("setgroups failed"));
655 if (setgid (server_gid
) == -1)
657 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
662 if (setuid (server_uid
) == -1)
664 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);