1 /* Inner loops of cache daemon.
2 Copyright (C) 1998, 1999, 2000, 2001, 2002 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
)
174 error (EXIT_FAILURE
, errno
, "while allocating cache");
176 if (dbs
[cnt
].check_file
)
178 /* We need the modification date of the file. */
181 if (stat (dbs
[cnt
].filename
, &st
) < 0)
184 /* We cannot stat() the file, disable file checking. */
185 dbg_log (_("cannot stat() file `%s': %s"),
187 strerror_r (errno
, buf
, sizeof (buf
)));
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 /* Set permissions for the socket. */
212 chmod (_PATH_NSCDSOCKET
, 0666);
214 /* Set the socket up to accept connections. */
215 if (listen (sock
, SOMAXCONN
) < 0)
217 dbg_log (_("cannot enable socket to accept connections: %s"),
222 /* Change to unprivileged uid/gid/groups if specifed in config file */
223 if (server_user
!= NULL
)
224 finish_drop_privileges ();
228 /* Close the connections. */
236 invalidate_cache (char *key
)
240 if (strcmp (key
, "passwd") == 0)
242 else if (strcmp (key
, "group") == 0)
244 else if (__builtin_expect (strcmp (key
, "hosts"), 0) == 0)
249 if (dbs
[number
].enabled
)
250 prune_cache (&dbs
[number
], LONG_MAX
);
254 /* Handle new request. */
256 handle_request (int fd
, request_header
*req
, void *key
, uid_t uid
)
258 if (__builtin_expect (debug_level
, 0) > 0)
259 dbg_log (_("handle_request: request received (Version = %d)"),
262 if (__builtin_expect (req
->version
, NSCD_VERSION
) != NSCD_VERSION
)
266 cannot handle old request version %d; current version is %d"),
267 req
->version
, NSCD_VERSION
);
271 if (__builtin_expect (req
->type
, GETPWBYNAME
) >= GETPWBYNAME
272 && __builtin_expect (req
->type
, LASTDBREQ
) <= LASTDBREQ
)
274 struct hashentry
*cached
;
275 struct database
*db
= &dbs
[serv2db
[req
->type
]];
277 if (__builtin_expect (debug_level
, 0) > 0)
279 if (req
->type
== GETHOSTBYADDR
|| req
->type
== GETHOSTBYADDRv6
)
281 char buf
[INET6_ADDRSTRLEN
];
283 dbg_log ("\t%s (%s)", serv2str
[req
->type
],
284 inet_ntop (req
->type
== GETHOSTBYADDR
285 ? AF_INET
: AF_INET6
,
286 key
, buf
, sizeof (buf
)));
289 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
292 /* Is this service enabled? */
295 /* No, sent the prepared record. */
296 if (TEMP_FAILURE_RETRY (write (fd
, db
->disabled_iov
->iov_base
,
297 db
->disabled_iov
->iov_len
))
298 != db
->disabled_iov
->iov_len
299 && __builtin_expect (debug_level
, 0) > 0)
301 /* We have problems sending the result. */
303 dbg_log (_("cannot write result: %s"),
304 strerror_r (errno
, buf
, sizeof (buf
)));
310 /* Be sure we can read the data. */
311 pthread_rwlock_rdlock (&db
->lock
);
313 /* See whether we can handle it from the cache. */
314 cached
= (struct hashentry
*) cache_search (req
->type
, key
, req
->key_len
,
318 /* Hurray it's in the cache. */
319 if (TEMP_FAILURE_RETRY (write (fd
, cached
->packet
, cached
->total
))
321 && __builtin_expect (debug_level
, 0) > 0)
323 /* We have problems sending the result. */
325 dbg_log (_("cannot write result: %s"),
326 strerror_r (errno
, buf
, sizeof (buf
)));
329 pthread_rwlock_unlock (&db
->lock
);
334 pthread_rwlock_unlock (&db
->lock
);
336 else if (__builtin_expect (debug_level
, 0) > 0)
338 if (req
->type
== INVALIDATE
)
339 dbg_log ("\t%s (%s)", serv2str
[req
->type
], (char *)key
);
341 dbg_log ("\t%s", serv2str
[req
->type
]);
344 /* Handle the request. */
348 addpwbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
352 addpwbyuid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
356 addgrbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
360 addgrbygid (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
364 addhstbyname (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
367 case GETHOSTBYNAMEv6
:
368 addhstbynamev6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
372 addhstbyaddr (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
375 case GETHOSTBYADDRv6
:
376 addhstbyaddrv6 (&dbs
[serv2db
[req
->type
]], fd
, req
, key
, uid
);
382 /* Accept shutdown, getstat and invalidate only from root */
383 if (secure_in_use
&& uid
== 0)
385 if (req
->type
== GETSTAT
)
386 send_stats (fd
, dbs
);
387 else if (req
->type
== INVALIDATE
)
388 invalidate_cache (key
);
390 termination_handler (0);
394 /* Some systems have no SO_PEERCRED implementation. They don't
395 care about security so we don't as well. */
398 socklen_t optlen
= sizeof (caller
);
400 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
, &caller
, &optlen
) < 0)
404 dbg_log (_("error getting callers id: %s"),
405 strerror_r (errno
, buf
, sizeof (buf
)));
411 if (req
->type
== GETSTAT
)
412 send_stats (fd
, dbs
);
413 else if (req
->type
== INVALIDATE
)
414 invalidate_cache (key
);
416 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 now
= time (NULL
);
438 time_t next_prune
= now
+ CACHE_PRUNE_INTERVAL
;
439 int timeout
= run_prune
? 1000 * (next_prune
- now
) : -1;
442 conn
.events
= POLLRDNORM
;
446 int nr
= poll (&conn
, 1, timeout
);
450 /* The `poll' call timed out. It's time to clean up the cache. */
451 assert (my_number
< lastdb
);
453 prune_cache (&dbs
[my_number
], now
);
454 next_prune
= now
+ CACHE_PRUNE_INTERVAL
;
455 timeout
= 1000 * (next_prune
- now
);
459 /* We have a new incoming connection. */
460 if (conn
.revents
& (POLLRDNORM
|POLLERR
|POLLHUP
|POLLNVAL
))
462 /* Accept the connection. */
463 int fd
= accept (conn
.fd
, NULL
, NULL
);
468 if (__builtin_expect (fd
, 0) < 0)
470 dbg_log (_("while accepting connection: %s"),
471 strerror_r (errno
, buf
, sizeof (buf
)));
475 /* Now read the request. */
476 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, &req
,
481 dbg_log (_("short read while reading request: %s"),
482 strerror_r (errno
, buf
, sizeof (buf
)));
487 /* Some systems have no SO_PEERCRED implementation. They don't
488 care about security so we don't as well. */
493 socklen_t optlen
= sizeof (caller
);
495 if (getsockopt (fd
, SOL_SOCKET
, SO_PEERCRED
,
496 &caller
, &optlen
) < 0)
498 dbg_log (_("error getting callers id: %s"),
499 strerror_r (errno
, buf
, sizeof (buf
)));
504 if (req
.type
< GETPWBYNAME
|| req
.type
> LASTDBREQ
505 || secure
[serv2db
[req
.type
]])
510 /* It should not be possible to crash the nscd with a silly
511 request (i.e., a terribly large key). We limit the size
513 if (__builtin_expect (req
.key_len
, 1) < 0
514 || __builtin_expect (req
.key_len
, 1) > 1024)
517 dbg_log (_("key length in request too long: %d"), req
.key_len
);
524 char keybuf
[req
.key_len
];
526 if (__builtin_expect (TEMP_FAILURE_RETRY (read (fd
, keybuf
,
531 dbg_log (_("short read while reading request key: %s"),
532 strerror_r (errno
, buf
, sizeof (buf
)));
537 /* Phew, we got all the data, now process it. */
538 handle_request (fd
, &req
, keybuf
, uid
);
548 timeout
= now
< next_prune
? 1000 * (next_prune
- now
) : 0;
554 /* Start all the threads we want. The initial process is thread no. 1. */
562 pthread_attr_init (&attr
);
563 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
565 /* We allow less than LASTDB threads only for debugging. */
566 if (debug_level
== 0)
567 nthreads
= MAX (nthreads
, lastdb
);
569 for (i
= 1; i
< nthreads
; ++i
)
570 pthread_create (&th
, &attr
, nscd_run
, (void *) i
);
572 pthread_attr_destroy (&attr
);
574 nscd_run ((void *) 0);
578 /* Look up the uid, gid, and supplementary groups to run nscd as. When
579 this function is called, we are not listening on the nscd socket yet so
580 we can just use the ordinary lookup functions without causing a lockup */
582 begin_drop_privileges (void)
586 pwd
= getpwnam (server_user
);
590 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
591 error (EXIT_FAILURE
, 0, _("Failed to run nscd as user '%s'"),
595 server_uid
= pwd
->pw_uid
;
596 server_gid
= pwd
->pw_gid
;
598 server_groups
= (gid_t
*) xmalloc (server_ngroups
* sizeof (gid_t
));
600 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
604 server_groups
= (gid_t
*) xrealloc (server_groups
,
605 server_ngroups
* sizeof (gid_t
));
607 if (getgrouplist (server_user
, server_gid
, server_groups
, &server_ngroups
)
610 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
611 error (EXIT_FAILURE
, errno
, _("getgrouplist failed"));
616 /* Call setgroups(), setgid(), and setuid() to drop root privileges and
617 run nscd as the user specified in the configuration file. */
619 finish_drop_privileges (void)
621 if (setgroups (server_ngroups
, server_groups
) == -1)
623 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
624 error (EXIT_FAILURE
, errno
, _("setgroups failed"));
627 if (setgid (server_gid
) == -1)
629 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);
634 if (setuid (server_uid
) == -1)
636 dbg_log (_("Failed to run nscd as user '%s'"), server_user
);