diff options
Diffstat (limited to 'nscd')
-rw-r--r-- | nscd/Makefile | 46 | ||||
-rw-r--r-- | nscd/TODO | 7 | ||||
-rw-r--r-- | nscd/connections.c | 529 | ||||
-rw-r--r-- | nscd/dbg_log.c | 64 | ||||
-rw-r--r-- | nscd/dbg_log.h | 27 | ||||
-rw-r--r-- | nscd/getgrgid_r.c | 30 | ||||
-rw-r--r-- | nscd/getgrnam_r.c | 29 | ||||
-rw-r--r-- | nscd/getpwnam_r.c | 30 | ||||
-rw-r--r-- | nscd/getpwuid_r.c | 30 | ||||
-rw-r--r-- | nscd/grpcache.c | 589 | ||||
-rw-r--r-- | nscd/nscd.c | 423 | ||||
-rw-r--r-- | nscd/nscd.conf | 30 | ||||
-rw-r--r-- | nscd/nscd.h | 149 | ||||
-rw-r--r-- | nscd/nscd.init | 50 | ||||
-rw-r--r-- | nscd/nscd_conf.c | 148 | ||||
-rw-r--r-- | nscd/nscd_getgr_r.c | 211 | ||||
-rw-r--r-- | nscd/nscd_getpw_r.c | 198 | ||||
-rw-r--r-- | nscd/nscd_proto.h | 35 | ||||
-rw-r--r-- | nscd/nscd_stat.c | 87 | ||||
-rw-r--r-- | nscd/pwdcache.c | 581 |
20 files changed, 3293 insertions, 0 deletions
diff --git a/nscd/Makefile b/nscd/Makefile new file mode 100644 index 0000000..4510c4d --- /dev/null +++ b/nscd/Makefile @@ -0,0 +1,46 @@ +# Copyright (C) 1998 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If not, +# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# +# Sub-makefile for nscd portion of the library. +# +subdir := nscd + +routines := nscd_getpw_r nscd_getgr_r + +# We can later add the names of other thread packages here. +ifeq (,$(findstring linuxthreads,$(add-ons))) + +others := nscd +install-sbin := nscd + +endif + +nscd-routines := nscd connections pwdcache getpwnam_r getpwuid_r grpcache\ + getgrnam_r getgrgid_r dbg_log nscd_conf nscd_stat +extra-objs := $(nscd-routines:=.o) + +distribute := nscd.h dbg_log.h + +include ../Rules + +ifeq ($(build-shared),yes) +$(objpfx)nscd: $(nscd-routines:%=$(objpfx)%.o) $(objpfx)../linuxthreads/libpthread.so$(libpthread.so-version) $(objpfx)../nis/libnsl.so$(libnsl.so-version) +else +$(objpfx)nscd: $(nscd-routines:%=$(objpfx)%.o) $(objpfx)../linuxthreads/libpthread.a $(objpfx)../nis/libnsl.a +endif diff --git a/nscd/TODO b/nscd/TODO new file mode 100644 index 0000000..16c2835 --- /dev/null +++ b/nscd/TODO @@ -0,0 +1,7 @@ + +* We should use readv/writev for group entries, too + +* If we have reached the max. # of process, close accept socket. + ! THIS COULD CAUSE THE KERNEL TO HANG ! BE CAREFUL ! + +* Implement cache for hosts diff --git a/nscd/connections.c b/nscd/connections.c new file mode 100644 index 0000000..abde747 --- /dev/null +++ b/nscd/connections.c @@ -0,0 +1,529 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <libintl.h> +#include <locale.h> +#include <pthread.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/un.h> + +#include "nscd.h" +#include "dbg_log.h" + +/* Socket 0 in the array is named and exported into the file namespace + as a connection point for clients. */ +static int sock[MAX_NUM_CONNECTIONS]; +static int socks_active; +static fd_set read_set; +static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; + + +/* Cleanup. */ +void +close_sockets (void) +{ + int i; + + if (debug_flag) + dbg_log (_("close_sockets called")); + + pthread_mutex_lock (&sock_lock); + + /* Close sockets. */ + for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) + if (sock[i] != 0) + { + if (close (sock[i])) + dbg_log (_("socket [%d|%d] close: %s"), strerror (errno)); + + sock[i] = 0; + --socks_active; + } + + pthread_mutex_unlock (&sock_lock); +} + +void +close_socket (int conn) +{ + if (debug_flag > 2) + dbg_log (_("close socket (%d|%d)"), conn, sock[conn]); + + pthread_mutex_lock (&sock_lock); + + close (sock[conn]); + sock[conn] = 0; + --socks_active; + + pthread_mutex_unlock (&sock_lock); +} + +/* Local rountine, assigns a socket to a new connection request. */ +static void +handle_new_connection (void) +{ + int i; + + if (debug_flag > 2) + dbg_log (_("handle_new_connection")); + + pthread_mutex_lock (&sock_lock); + + if (socks_active < MAX_NUM_CONNECTIONS) + /* Find a free socket entry to use. */ + for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) + { + if (sock[i] == 0) + { + if ((sock[i] = accept (sock[0], NULL, NULL)) < 0) + { + dbg_log (_("socket accept: %s"), strerror (errno)); + return; + } + ++socks_active; + FD_SET (sock[i], &read_set); + if (debug_flag > 2) + dbg_log (_("handle_new_connection used socket %d|%d"), i, + sock[i]); + break; + } + } + else + { + int black_widow_sock; + dbg_log (_("Supported number of simultainious connections exceeded")); + dbg_log (_("Ignoring client connect request")); + /* There has to be a better way to ignore a connection request,.. + when I get my hands on a sockets wiz I'll modify this. */ + black_widow_sock = accept (sock[0], NULL, NULL); + close (black_widow_sock); + } + pthread_mutex_unlock (&sock_lock); +} + +/* Local routine, reads a request off a socket indicated by a selectset. */ +static int +handle_new_request (fd_set read_selects, int **connp, request_header **reqp, + char **key) +{ + ssize_t nbytes; + int i; + + if (debug_flag) + dbg_log ("handle_new_request"); + + /* Find the descriptor. */ + for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) + if (FD_ISSET(sock[i], &read_selects)) + break; + + if (debug_flag > 2) + dbg_log (_("handle_new_request uses socket %d"), i); + + /* Read from it. */ + nbytes = read (sock[i], *reqp, sizeof (request_header)); + if (nbytes != sizeof (request_header)) + { + /* Handle non-data read cases. */ + if (nbytes == 0) + { + /* Close socket down. */ + if (debug_flag > 2) + dbg_log (_("Real close socket %d|%d"), i, sock[i]); + + pthread_mutex_lock (&sock_lock); + FD_CLR (sock[i], &read_set); + close (sock[i]); + sock[i] = 0; + --socks_active; + pthread_mutex_unlock (&sock_lock); + } + else + if (nbytes < 0) + { + dbg_log (_("Read(%d|%d) error on get request: %s"), + i, sock[i], strerror (errno)); + exit (1); + } + else + dbg_log (_("Read, data < request buf size, ignoring data")); + + return -1; + } + else + { + *key = malloc ((*reqp)->key_len + 1); + /* Read the key from it */ + nbytes = read (sock[i], *key, (*reqp)->key_len); + if (nbytes != (*reqp)->key_len) + { + /* Handle non-data read cases. */ + if (nbytes == 0) + { + /* Close socket down. */ + if (debug_flag > 2) + dbg_log (_("Real close socket %d|%d"), i, sock[i]); + + pthread_mutex_lock (&sock_lock); + FD_CLR (sock[i], &read_set); + close (sock[i]); + sock[i] = 0; + --socks_active; + pthread_mutex_unlock (&sock_lock); + } + else + if (nbytes < 0) + { + perror (_("Read() error on get request")); + return 0; + } + else + fputs (_("Read, data < request buf size, ignoring data"), + stderr); + + free (*key); + return -1; + } + else + { + /* Ok, have a live one, A real data req buf has been obtained. */ + (*key)[(*reqp)->key_len] = '\0'; + **connp = i; + return 0; + } + } +} + +void +get_request (int *conn, request_header *req, char **key) +{ + int i, nr, done = 0; + fd_set read_selects; + + if (debug_flag) + dbg_log ("get_request"); + + /* loop, processing new connection requests until a client buffer + is read in on an existing connection. */ + while (!done) + { + /* Set up the socket descriptor mask for the select. + copy read_set into the local copy. */ + + FD_ZERO (&read_selects); + pthread_mutex_lock (&sock_lock); + for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) + { + if (FD_ISSET (sock[i], &read_set)) + FD_SET (sock[i], &read_selects); + } + pthread_mutex_unlock (&sock_lock); + /* Poll active connections using select(). */ + nr = select (FD_SETSIZE, &read_selects, NULL, NULL, NULL); + if (nr <= 0) + { + perror (_("Select new reads")); + exit (1); + } + if (FD_ISSET (sock[0], &read_selects)) + /* Handle the case of a new connection request on the named socket. */ + handle_new_connection (); + else + { + /* Read data from client specific descriptor. */ + if (handle_new_request (read_selects, &conn, &req, key) == 0) + { + FD_CLR (sock[*conn], &read_set); + done = 1; + } + } + } /* While not_done. */ +} + +void +init_sockets (void) +{ + struct sockaddr_un sock_addr; + + /* Initialize the connections db. */ + socks_active = 0; + FD_ZERO (&read_set); + + /* Create the socket. */ + sock[0] = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock[0] < 0) + { + perror (_("cannot create socket")); + exit (1); + } + /* Bind a name to the socket. */ + sock_addr.sun_family = AF_UNIX; + strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET); + if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) + { + dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno)); + exit (1); + } + /* Set permissions for the socket. */ + chmod (_PATH_NSCDSOCKET, 0666); + + /* Set the socket up to accept connections. */ + if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0) + { + perror (_("cannot enable socket to accept connections")); + exit (1); + } + + /* Add the socket to the server's set of active sockets. */ + FD_SET (sock[0], &read_set); + ++socks_active; +} + +void +pw_send_answer (int conn, struct passwd *pwd) +{ + pw_response_header resp; + + resp.version = NSCD_VERSION; + if (pwd != NULL) + { + resp.found = 1; + resp.pw_name_len = strlen (pwd->pw_name); + resp.pw_passwd_len = strlen (pwd->pw_passwd); + resp.pw_uid = pwd->pw_uid; + resp.pw_gid = pwd->pw_gid; + resp.pw_gecos_len = strlen (pwd->pw_gecos); + resp.pw_dir_len = strlen (pwd->pw_dir); + resp.pw_shell_len = strlen (pwd->pw_shell); + } + else + { + resp.found = 0; + resp.pw_name_len = 0; + resp.pw_passwd_len = 0; + resp.pw_uid = -1; + resp.pw_gid = -1; + resp.pw_gecos_len = 0; + resp.pw_dir_len = 0; + resp.pw_shell_len = 0; + } + if (sock[conn] == 0) + { + dbg_log (_("bad connection id on send response [%d|%d]"), + conn, sock[conn]); + return; + } + + /* Send response header. */ + if (write (sock[conn], &resp, sizeof (pw_response_header)) != + sizeof (pw_response_header)) + { + dbg_log (_("write incomplete on send response: %s"), strerror (errno)); + return; + } + + if (resp.found) + { + struct iovec vec[5]; + + /* Send pw_name. */ + vec[0].iov_base = pwd->pw_name; + vec[0].iov_len = resp.pw_name_len; + /* Send pw_passwd. */ + vec[1].iov_base = pwd->pw_passwd; + vec[1].iov_len = resp.pw_passwd_len; + /* Send pw_gecos. */ + vec[2].iov_base = pwd->pw_gecos; + vec[2].iov_len = resp.pw_gecos_len; + /* Send pw_dir. */ + vec[3].iov_base = pwd->pw_dir; + vec[3].iov_len = resp.pw_dir_len; + /* Send pw_shell. */ + vec[4].iov_base = pwd->pw_shell; + vec[4].iov_len = resp.pw_shell_len; + + if (writev (sock[conn], vec, 5) != (resp.pw_name_len + resp.pw_passwd_len + + resp.pw_gecos_len + resp.pw_dir_len + + resp.pw_shell_len)) + dbg_log (_("write incomplete on send passwd answer: %s"), + strerror (errno)); + } +} + +void +pw_send_disabled (int conn) +{ + pw_response_header resp; + + resp.version = NSCD_VERSION; + resp.found = -1; + resp.pw_name_len = 0; + resp.pw_passwd_len = 0; + resp.pw_uid = -1; + resp.pw_gid = -1; + resp.pw_gecos_len = 0; + resp.pw_dir_len = 0; + resp.pw_shell_len = 0; + + if (sock[conn] == 0) + { + dbg_log ("bad connection id on send response [%d|%d]", + conn, sock[conn]); + return; + } + + /* Send response header. */ + if (write (sock[conn], &resp, sizeof (pw_response_header)) + != sizeof (pw_response_header)) + dbg_log (_("write incomplete on send response: %s"), strerror (errno)); +} + +void +gr_send_answer (int conn, struct group *grp) +{ + gr_response_header resp; + + resp.version = NSCD_VERSION; + if (grp != NULL) + { + resp.found = 1; + resp.gr_name_len = strlen (grp->gr_name); + resp.gr_passwd_len = strlen (grp->gr_passwd); + resp.gr_gid = grp->gr_gid; + resp.gr_mem_len = 0; + while (grp->gr_mem[resp.gr_mem_len]) + ++resp.gr_mem_len; + } + else + { + resp.found = 0; + resp.gr_name_len = 0; + resp.gr_passwd_len = 0; + resp.gr_gid = -1; + resp.gr_mem_len = 0; + } + if (sock[conn] == 0) + { + dbg_log (_("bad connection id on send response [%d|%d]"), + conn, sock[conn]); + return; + } + + /* Send response header. */ + if (write (sock[conn], &resp, sizeof (gr_response_header)) + != sizeof (gr_response_header)) + { + dbg_log (_("write incomplete on send response: %s"), strerror (errno)); + return; + } + + if (resp.found) + { + unsigned int l = 0; + + /* Send gr_name. */ + if (write (sock[conn], grp->gr_name, resp.gr_name_len) + != resp.gr_name_len) + { + dbg_log (_("write incomplete on send response: %s"), + strerror (errno)); + return; + } + /* Send gr_passwd. */ + if (write (sock[conn], grp->gr_passwd, resp.gr_passwd_len) + != resp.gr_passwd_len) + { + dbg_log (_("write incomplete on send response: %s"), + strerror (errno)); + return; + } + + while (grp->gr_mem[l]) + { + size_t len = strlen (grp->gr_mem[l]); + + if (write (sock[conn], &len, sizeof (len)) != sizeof (len)) + { + dbg_log (_("write incomplete on send response: %s"), + strerror (errno)); + return; + } + if (write (sock[conn], grp->gr_mem[l], len) != len) + { + dbg_log (_("write incomplete on send response: %s"), + strerror (errno)); + return; + } + ++l; + } + } +} + +void +gr_send_disabled (int conn) +{ + gr_response_header resp; + + resp.version = NSCD_VERSION; + resp.found = -1; + resp.gr_name_len = 0; + resp.gr_passwd_len = 0; + resp.gr_gid = -1; + resp.gr_mem_len = 0; + + if (sock[conn] == 0) + { + dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"), + conn, sock[conn]); + return; + } + + /* Send response header. */ + if (write (sock[conn], &resp, sizeof (gr_response_header)) + != sizeof (gr_response_header)) + dbg_log (_("write incomplete on send gr_disabled response: %s"), + strerror (errno)); +} + +void +stat_send (int conn, stat_response_header *resp) +{ + if (sock[conn] == 0) + { + dbg_log (_("bad connection id on send stat response [%d|%d]"), + conn, sock[conn]); + return; + } + + /* send response header. */ + if (write (sock[conn], resp, sizeof (stat_response_header)) + != sizeof (stat_response_header)) + dbg_log (_("write incomplete on send stat response: %s"), + strerror (errno)); +} diff --git a/nscd/dbg_log.c b/nscd/dbg_log.c new file mode 100644 index 0000000..37065e4 --- /dev/null +++ b/nscd/dbg_log.c @@ -0,0 +1,64 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <stdarg.h> +#include <stdio.h> +#include <syslog.h> +#include <unistd.h> +#include "dbg_log.h" +#include "nscd.h" + +/* if in debug mode and we have a debug file, we write the messages to it, + if in debug mode and no debug file, we write the messages to stderr, + else to syslog. */ + +FILE *dbgout = NULL; +int debug_flag = 0; + +int +set_logfile (const char *logfile) +{ + dbgout = fopen (logfile, "a"); + return dbgout == NULL ? 0 : 1; +} + +void +dbg_log (const char *fmt,...) +{ + va_list ap; + char msg[512], msg2[512]; + + va_start (ap, fmt); + vsnprintf (msg2, sizeof (msg), fmt, ap); + + if (debug_flag) + { + snprintf (msg, sizeof (msg), "%d: %s\n", getpid (), msg2); + if (dbgout) + fputs (msg, dbgout); + else + fputs (msg, stderr); + } + else + { + snprintf (msg, sizeof (msg), "%d: %s", getpid (), msg2); + syslog (LOG_NOTICE, msg); + } + va_end (ap); +} diff --git a/nscd/dbg_log.h b/nscd/dbg_log.h new file mode 100644 index 0000000..c3d1dc4 --- /dev/null +++ b/nscd/dbg_log.h @@ -0,0 +1,27 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _DBG_LOG_H +#define _DBG_LOG_H 1 + +extern int debug_flag; + +extern void dbg_log (const char *, ...); + +#endif diff --git a/nscd/getgrgid_r.c b/nscd/getgrgid_r.c new file mode 100644 index 0000000..3011602 --- /dev/null +++ b/nscd/getgrgid_r.c @@ -0,0 +1,30 @@ +/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <grp.h> + + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrgid +#define DATABASE_NAME group +#define ADD_PARAMS gid_t gid +#define ADD_VARIABLES gid +#define BUFLEN NSS_BUFLEN_GROUP + +#include <nss/getXXbyYY_r.c> diff --git a/nscd/getgrnam_r.c b/nscd/getgrnam_r.c new file mode 100644 index 0000000..3575e74 --- /dev/null +++ b/nscd/getgrnam_r.c @@ -0,0 +1,29 @@ +/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <grp.h> + + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrnam +#define DATABASE_NAME group +#define ADD_PARAMS const char *name +#define ADD_VARIABLES name + +#include <nss/getXXbyYY_r.c> diff --git a/nscd/getpwnam_r.c b/nscd/getpwnam_r.c new file mode 100644 index 0000000..328c305 --- /dev/null +++ b/nscd/getpwnam_r.c @@ -0,0 +1,30 @@ +/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <pwd.h> + + +#define LOOKUP_TYPE struct passwd +#define FUNCTION_NAME getpwnam +#define DATABASE_NAME passwd +#define ADD_PARAMS const char *name +#define ADD_VARIABLES name +#define BUFLEN NSS_BUFLEN_PASSWD + +#include <nss/getXXbyYY_r.c> diff --git a/nscd/getpwuid_r.c b/nscd/getpwuid_r.c new file mode 100644 index 0000000..91bd802 --- /dev/null +++ b/nscd/getpwuid_r.c @@ -0,0 +1,30 @@ +/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <pwd.h> + + +#define LOOKUP_TYPE struct passwd +#define FUNCTION_NAME getpwuid +#define DATABASE_NAME passwd +#define ADD_PARAMS uid_t uid +#define ADD_VARIABLES uid +#define BUFLEN NSS_BUFLEN_PASSWD + +#include <nss/getXXbyYY_r.c> diff --git a/nscd/grpcache.c b/nscd/grpcache.c new file mode 100644 index 0000000..9f6c767 --- /dev/null +++ b/nscd/grpcache.c @@ -0,0 +1,589 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <grp.h> +#include <malloc.h> +#include <pthread.h> +#include <string.h> +#include <rpcsvc/nis.h> +#include <sys/types.h> + +#include "dbg_log.h" +#include "nscd.h" + +static unsigned long modulo = 211; +static unsigned long postimeout = 3600; +static unsigned long negtimeout = 60; + +static unsigned long poshit = 0; +static unsigned long posmiss = 0; +static unsigned long neghit = 0; +static unsigned long negmiss = 0; + +struct grphash +{ + time_t create; + struct grphash *next; + struct group *grp; +}; +typedef struct grphash grphash; + +struct gidhash +{ + struct gidhash *next; + struct grphash *grptr; +}; +typedef struct gidhash gidhash; + +struct neghash +{ + time_t create; + struct neghash *next; + char *key; +}; +typedef struct neghash neghash; + +static grphash *grptbl; +static gidhash *gidtbl; +static neghash *negtbl; + +static pthread_rwlock_t grplock = PTHREAD_RWLOCK_INITIALIZER; +static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER; + +static void *grptable_update (void *); +static void *negtable_update (void *); + +void +get_gr_stat (stat_response_header *stat) +{ + stat->gr_poshit = poshit; + stat->gr_posmiss = posmiss; + stat->gr_neghit = neghit; + stat->gr_negmiss = negmiss; + stat->gr_size = modulo; + stat->gr_posttl = postimeout; + stat->gr_negttl = negtimeout; +} + +void +set_grp_modulo (unsigned long mod) +{ + modulo = mod; +} + +void +set_pos_grp_ttl (unsigned long ttl) +{ + postimeout = ttl; +} + +void +set_neg_grp_ttl (unsigned long ttl) +{ + negtimeout = ttl; +} + +int +cache_grpinit () +{ + pthread_attr_t attr; + pthread_t thread; + + grptbl = calloc (1, modulo * sizeof (grphash)); + if (grptbl == NULL) + return -1; + calloc (1, modulo * sizeof (grphash)); + if (gidtbl == NULL) + return -1; + negtbl = calloc (1, modulo * sizeof (neghash)); + if (negtbl == NULL) + return -1; + + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + pthread_create (&thread, NULL, grptable_update, &attr); + pthread_create (&thread, NULL, negtable_update, &attr); + + pthread_attr_destroy (&attr); + + return 0; +} + +static struct group * +save_grp (struct group *src) +{ + struct group *dest; + unsigned long int l; + + dest = calloc (1, sizeof (struct group)); + dest->gr_name = strdup (src->gr_name); + dest->gr_passwd = strdup (src->gr_passwd); + dest->gr_gid = src->gr_gid; + + /* How many members does this group have? */ + l = 0; + while (src->gr_mem[l]) + ++l; + + dest->gr_mem = calloc (1, sizeof (char *) * (l+1)); + l = 0; + while (src->gr_mem[l]) + { + dest->gr_mem[l] = strdup (src->gr_mem[l]); + ++l; + } + + return dest; +} + +static void +free_grp (struct group *src) +{ + unsigned long int l; + + free (src->gr_name); + free (src->gr_passwd); + + l = 0; + while (src->gr_mem[l]) + { + free (src->gr_mem[l]); + ++l; + } + free (src->gr_mem); + free (src); +} + +static int +add_cache (struct group *grp) +{ + grphash *work; + unsigned long int hash = __nis_hash (grp->gr_name, + strlen (grp->gr_name)) % modulo; + + work = &grptbl[hash]; + + if (grptbl[hash].grp == NULL) + grptbl[hash].grp = save_grp (grp); + else + { + while (work->next != NULL) + work = work->next; + + work->next = calloc (1, sizeof (grphash)); + work->next->grp = save_grp (grp); + work = work->next; + } + + time (&work->create); + gidtbl[grp->gr_gid % modulo].grptr = work; + + return 0; +} + +static struct group * +cache_search_name (const char *name) +{ + grphash *work; + unsigned long int hash = __nis_hash (name, strlen(name)) % modulo; + + work = &grptbl[hash]; + + while (work->grp != NULL) + { + if (strcmp (work->grp->gr_name, name) == 0) + return work->grp; + if (work->next != NULL) + work = work->next; + else + return NULL; + } + return NULL; +} + +static struct group * +cache_search_gid (gid_t gid) +{ + gidhash *work; + + work = &gidtbl[gid % modulo]; + + while (work->grptr != NULL) + { + if (work->grptr->grp->gr_gid == gid) + return work->grptr->grp; + if (work->next != NULL) + work = work->next; + else + return NULL; + } + return NULL; +} + +static int +add_negcache (char *key) +{ + neghash *work; + unsigned long int hash = __nis_hash (key, strlen (key)) % modulo; + + work = &negtbl[hash]; + + if (negtbl[hash].key == NULL) + negtbl[hash].key = strdup (key); + else + { + while (work->next != NULL) + work = work->next; + + work->next = calloc (1, sizeof (neghash)); + work->next->key = strdup (key); + work = work->next; + } + + time (&work->create); + return 0; +} + +static int +cache_search_neg (const char *key) +{ + neghash *work; + unsigned long int hash = __nis_hash (key, strlen (key)) % modulo; + + work = &negtbl[hash]; + + while (work->key != NULL) + { + if (strcmp (work->key, key) == 0) + return 1; + if (work->next != NULL) + work = work->next; + else + return 0; + } + return 0; +} + +void * +cache_getgrnam (void *v_param) +{ + param_t *param = (param_t *)v_param; + struct group *grp, resultbuf; + + pthread_rwlock_rdlock (&grplock); + grp = cache_search_name (param->key); + + /* I don't like it to hold the read only lock longer, but it is + necessary to avoid to much malloc/free/strcpy. */ + + if (grp) + { + if (debug_flag) + dbg_log (_("Found \"%s\" in cache !"), param->key); + + ++poshit; + gr_send_answer (param->conn, grp); + close_socket (param->conn); + + pthread_rwlock_unlock (&grplock); + } + else + { + int buflen = 1024; + char *buffer = calloc (1, buflen); + int status; + + if (debug_flag) + dbg_log (_("Doesn't found \"%s\" in cache !"), param->key); + + pthread_rwlock_unlock (&grplock); + + pthread_rwlock_rdlock (&neglock); + status = cache_search_neg (param->key); + pthread_rwlock_unlock (&neglock); + + if (status == 0) + { + while (buffer != NULL + && (getgrnam_r (param->key, &resultbuf, buffer, buflen, &grp) + != 0) + && errno == ERANGE) + { + errno = 0; + buflen += 1024; + buffer = realloc (buffer, buflen); + } + + if (buffer != NULL && grp != NULL) + { + struct group *tmp; + + ++poshit; + pthread_rwlock_wrlock (&grplock); + /* While we are waiting on the lock, somebody else could + add this entry. */ + tmp = cache_search_name (param->key); + if (tmp == NULL) + add_cache (grp); + pthread_rwlock_unlock (&grplock); + } + else + { + pthread_rwlock_wrlock (&neglock); + add_negcache (param->key); + ++negmiss; + pthread_rwlock_unlock (&neglock); + } + } + else + ++neghit; + + gr_send_answer (param->conn, grp); + close_socket (param->conn); + if (buffer != NULL) + free (buffer); + } + free (param->key); + free (param); + return NULL; +} + +void * +cache_gr_disabled (void *v_param) +{ + param_t *param = (param_t *)v_param; + + gr_send_disabled (param->conn); + return NULL; +} + +void * +cache_getgrgid (void *v_param) +{ + param_t *param = (param_t *)v_param; + struct group *grp, resultbuf; + gid_t gid = strtol (param->key, NULL, 10); + + pthread_rwlock_rdlock (&grplock); + grp = cache_search_gid (gid); + + /* I don't like it to hold the read only lock longer, but it is + necessary to avoid to much malloc/free/strcpy. */ + + if (grp != NULL) + { + if (debug_flag) + dbg_log (_("Found \"%d\" in cache !\n"), gid); + + ++poshit; + gr_send_answer (param->conn, grp); + close_socket (param->conn); + + pthread_rwlock_unlock (&grplock); + } + else + { + int buflen = 1024; + char *buffer = malloc (buflen); + int status; + + if (debug_flag) + dbg_log (_("Doesn't found \"%d\" in cache !\n"), gid); + + pthread_rwlock_unlock (&grplock); + + pthread_rwlock_rdlock (&neglock); + status = cache_search_neg (param->key); + pthread_rwlock_unlock (&neglock); + + if (status == 0) + { + while (buffer != NULL + && (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0) + && errno == ERANGE) + { + errno = 0; + buflen += 1024; + buffer = realloc (buffer, buflen); + } + + if (buffer != NULL && grp != NULL) + { + struct group *tmp; + + ++posmiss; + pthread_rwlock_wrlock (&grplock); + /* While we are waiting on the lock, somebody else could + add this entry. */ + tmp = cache_search_gid (gid); + if (tmp == NULL) + add_cache (grp); + pthread_rwlock_unlock (&grplock); + } + else + { + ++negmiss; + pthread_rwlock_wrlock (&neglock); + add_negcache (param->key); + pthread_rwlock_unlock (&neglock); + } + } + else + ++neghit; + + gr_send_answer (param->conn, grp); + close_socket (param->conn); + if (buffer != NULL) + free (buffer); + } + free (param->key); + free (param); + return NULL; +} + +void * +grptable_update (void *v) +{ + time_t now; + int i; + + sleep (20); + + while (!do_shutdown) + { + if (debug_flag > 2) + dbg_log (_("(grptable_update) Wait for write lock!")); + + pthread_rwlock_wrlock (&grplock); + + if (debug_flag > 2) + dbg_log (_("(grptable_update) Have write lock")); + + time (&now); + for (i = 0; i < modulo; ++i) + { + grphash *work = &grptbl[i]; + + while (work && work->grp) + { + if ((now - work->create) >= postimeout) + { + gidhash *uh = &gidtbl[work->grp->gr_gid % modulo]; + + if (debug_flag) + dbg_log (_("Give \"%s\" free"), work->grp->gr_name); + + while (uh && uh->grptr) + { + if (uh->grptr->grp->gr_gid == work->grp->gr_gid) + { + if (debug_flag > 3) + dbg_log (_("Give gid for \"%s\" free"), + work->grp->gr_name); + if (uh->next != NULL) + { + gidhash *tmp = uh->next; + uh->grptr = tmp->grptr; + uh->next = tmp->next; + free (tmp); + } + else + uh->grptr = NULL; + } + uh = uh->next; + } + + free_grp (work->grp); + if (work->next != NULL) + { + grphash *tmp = work->next; + work->create = tmp->create; + work->next = tmp->next; + work->grp = tmp->grp; + free (tmp); + } + else + work->grp = NULL; + } + work = work->next; + } + } + if (debug_flag > 2) + dbg_log (_("(pwdtable_update) Release wait lock\n")); + pthread_rwlock_unlock (&grplock); + sleep (20); + } + return NULL; +} + +void * +negtable_update (void *v) +{ + time_t now; + int i; + + sleep (30); + + while (!do_shutdown) + { + if (debug_flag > 2) + dbg_log (_("(negtable_update) Wait for write lock!")); + + pthread_rwlock_wrlock (&neglock); + + if (debug_flag > 2) + dbg_log (_("(negtable_update) Have write lock")); + + time (&now); + for (i = 0; i < modulo; ++i) + { + neghash *work = &negtbl[i]; + + while (work && work->key) + { + if ((now - work->create) >= negtimeout) + { + if (debug_flag) + dbg_log (_("Give \"%s\" free"), work->key); + + free (work->key); + + if (work->next != NULL) + { + neghash *tmp = work->next; + work->create = tmp->create; + work->next = tmp->next; + work->key = tmp->key; + free (tmp); + } + else + work->key = NULL; + } + work = work->next; + } + } + if (debug_flag > 2) + dbg_log (_("(negtable_update) Release wait lock")); + pthread_rwlock_unlock (&neglock); + sleep (10); + } + return NULL; +} diff --git a/nscd/nscd.c b/nscd/nscd.c new file mode 100644 index 0000000..59d4a60 --- /dev/null +++ b/nscd/nscd.c @@ -0,0 +1,423 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* nscd - Name Service Cache Daemon. Caches passwd and group. */ + +#include <errno.h> +#include <getopt.h> +#include <libintl.h> +#include <locale.h> +#include <pthread.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "dbg_log.h" +#include "nscd.h" + +/* Get libc version number. */ +#include <version.h> + +#define PACKAGE _libc_intl_domainname + +/* Structure used by main() thread to keep track of the number of + active threads. Used to limit how many threads it will create + and under a shutdown condition to wait till all in-progress + requests have finished before "turning off the lights". */ + +typedef struct +{ + int num_active; + pthread_cond_t thread_exit_cv; + pthread_mutex_t mutex; +} thread_info_t; + +thread_info_t thread_info; + +int do_shutdown = 0; +int disabled_passwd = 0; +int disabled_group = 0; + +static void termination_handler (int signum); +static int check_pid (const char *file); +static int write_pid (const char *file); +static void usage (int status) __attribute__ ((noreturn)); +static void handle_requests (void); + +int +main (int argc, char **argv) +{ + int go_background = 1; + const char *conffile = _PATH_NSCDCONF; + + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); + /* Set the text message domain. */ + textdomain (PACKAGE); + + while (1) + { + int c; + int option_index = 0; + static struct option long_options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "shutdown", no_argument, NULL, 'K' }, + {NULL, 0, NULL, '\0'} + }; + + c = getopt_long (argc, argv, "df:ghKV", long_options, &option_index); + if (c == (-1)) + break; + switch (c) + { + case 'd': + debug_flag = 1; + go_background = 0; + break; + case 'f': + conffile = optarg; + break; + case 'h': + usage (EXIT_SUCCESS); + break; + case 'K': + if (getuid () != 0) + { + printf (_("Only root is allowed to use this option!\n\n")); + usage (EXIT_FAILURE); + } + { + int sock = __nscd_open_socket (); + request_header req; + ssize_t nbytes; + + if (sock == -1) + exit (EXIT_FAILURE); + + req.version = NSCD_VERSION; + req.type = SHUTDOWN; + req.key_len = 0; + nbytes = write (sock, &req, sizeof (request_header)); + close (sock); + if (nbytes != req.key_len) + exit (EXIT_FAILURE); + else + exit (EXIT_SUCCESS); + } + case 'g': + print_stat (); + exit (EXIT_SUCCESS); + case 'V': + printf ("nscd (GNU %s) %s\n", PACKAGE, VERSION); + printf (_("\ +Copyright (C) %s Free Software Foundation, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "1998"); + printf (_("Written by %s.\n"), "Thorsten Kukuk"); + exit (EXIT_SUCCESS); + default: + usage (EXIT_FAILURE); + } + } + + signal (SIGINT, termination_handler); + signal (SIGQUIT, termination_handler); + signal (SIGTERM, termination_handler); + + /* Check if we are already running. */ + if (check_pid (_PATH_NSCDPID)) + { + fputs (_("already running"), stderr); + exit (EXIT_FAILURE); + } + + /* Behave like a daemon. */ + if (go_background) + { + openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON); + + if (daemon (0, 0) < 0) + { + fprintf (stderr, _("connot auto-background: %s\n"), + strerror (errno)); + exit (EXIT_FAILURE); + } + if (write_pid (_PATH_NSCDPID) < 0) + dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno)); + + /* Ignore job control signals */ + signal (SIGTTOU, SIG_IGN); + signal (SIGTTIN, SIG_IGN); + signal (SIGTSTP, SIG_IGN); + } + /* Cleanup files created by a previous `bind' */ + unlink (_PATH_NSCDSOCKET); + + nscd_parse_file (conffile); + + /* Create first sockets */ + init_sockets (); + /* Init databases */ + cache_pwdinit (); + cache_grpinit (); + /* Handle incoming requests */ + handle_requests (); + + return 0; +} + +/* Create a socket connected to a name. */ +int +__nscd_open_socket (void) +{ + struct sockaddr_un addr; + int sock; + + sock = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + addr.sun_family = AF_UNIX; + strcpy (addr.sun_path, _PATH_NSCDSOCKET); + if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) + { + close (sock); + return -1; + } + + return sock; +} + +/* Cleanup. */ +static void +termination_handler (int signum) +{ + close_sockets (); + + /* Clean up the files created by `bind'. */ + unlink (_PATH_NSCDSOCKET); + + /* Clean up pid file. */ + unlink (_PATH_NSCDPID); + + exit (EXIT_SUCCESS); +} + +/* Display usage information and exit. */ +static void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_invocation_name); + else + { + printf (_("\ +Usage: %s [OPTION]...\n\ + -d, --debug do not fork and display messages on the current tty\n\ + -h, --help display this help and exit\n\ + -V, --version output version information and exit\n\ + -f configuration-file read configuration data from the specified file.\n\ + -K, --shutdown shut the server down.\n\ + -g Prints configuration and statistics to stdout.\n"), + program_invocation_name); + fputs (_("\ +Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"), + stdout); + } + exit (status); +} + +/* Returns 1 if the process in pid file FILE is running, 0 if not. */ +static int +check_pid (const char *file) +{ + FILE *fp; + + fp = fopen (file, "r"); + if (fp) + { + pid_t pid; + + fscanf (fp, "%d", &pid); + fclose (fp); + + if (kill (pid, 0) == 0) + return 1; + } + + return 0; +} + +/* Write the current process id to the file FILE. + Returns 0 if successful, -1 if not. */ +static int +write_pid (const char *file) +{ + FILE *fp; + + fp = fopen (file, "w"); + if (fp == NULL) + return -1; + + fprintf (fp, "%d\n", getpid ()); + if (ferror (fp)) + return -1; + + fclose (fp); + + return 0; +} + +/* Type of the lookup function for netname2user. */ +typedef int (*pwbyname_function) (const char *name, struct passwd *pw, + char *buffer, size_t buflen); + +/* Hanlde incoming requests. */ +static +void handle_requests (void) +{ + request_header req; + int conn; /* Handle on which connection (client) the request came from. */ + int done = 0; + char *key; + + while (!done) + { + key = NULL; + get_request (&conn, &req, &key); + if (debug_flag) + dbg_log (_("handle_requests: request received (Version = %d)"), + req.version); + switch (req.type) + { + case GETPWBYNAME: + { + param_t *param = malloc (sizeof (param_t)); + pthread_t thread; + + if (debug_flag) + dbg_log ("\tGETPWBYNAME (%s)", key); + param->key = key; + param->conn = conn; + if (disabled_passwd) + pthread_create (&thread, NULL, cache_pw_disabled, (void *)param); + else + pthread_create (&thread, NULL, cache_getpwnam, (void *)param); + pthread_detach (thread); + } + break; + case GETPWBYUID: + { + param_t *param = malloc (sizeof (param_t)); + pthread_t thread; + + if (debug_flag) + dbg_log ("\tGETPWBYUID (%s)", key); + param->key = key; + param->conn = conn; + if (disabled_passwd) + pthread_create (&thread, NULL, cache_pw_disabled, (void *)param); + else + pthread_create (&thread, NULL, cache_getpwuid, (void *)param); + pthread_detach (thread); + } + break; + case GETGRBYNAME: + { + param_t *param = malloc (sizeof (param_t)); + pthread_t thread; + + if (debug_flag) + dbg_log ("\tGETGRBYNAME (%s)", key); + param->key = key; + param->conn = conn; + if (disabled_group) + pthread_create (&thread, NULL, cache_gr_disabled, (void *)param); + else + pthread_create (&thread, NULL, cache_getgrnam, (void *)param); + pthread_detach (thread); + } + break; + case GETGRBYGID: + { + param_t *param = malloc (sizeof (param_t)); + pthread_t thread; + + if (debug_flag) + dbg_log ("\tGETGRBYGID (%s)", key); + param->key = key; + param->conn = conn; + if (disabled_group) + pthread_create (&thread, NULL, cache_gr_disabled, (void *)param); + else + pthread_create (&thread, NULL, cache_getgrgid, (void *)param); + pthread_detach (thread); + } + break; + case GETHOSTBYNAME: + /* Not yetimplemented. */ + close_socket (conn); + break; + case GETHOSTBYADDR: + /* Not yet implemented. */ + close_socket (conn); + break; + case SHUTDOWN: + do_shutdown = 1; + close_socket (0); + close_socket (conn); + /* Clean up the files created by `bind'. */ + unlink (_PATH_NSCDSOCKET); + /* Clean up pid file. */ + unlink (_PATH_NSCDPID); + done = 1; + break; + case GETSTAT: + { + stat_response_header resp; + + if (debug_flag) + dbg_log ("\tGETSTAT"); + + get_pw_stat (&resp); + get_gr_stat (&resp); + resp.debug_level = debug_flag; + resp.pw_enabled = !disabled_passwd; + resp.gr_enabled = !disabled_group; + + stat_send (conn, &resp); + + close_socket (conn); + } + break; + default: + dbg_log (_("Unknown request (%d)"), req.type); + break; + } + } +} diff --git a/nscd/nscd.conf b/nscd/nscd.conf new file mode 100644 index 0000000..5d8c7f3 --- /dev/null +++ b/nscd/nscd.conf @@ -0,0 +1,30 @@ +# +# /etc/nscd.conf +# +# An example Name Service Cache config file. This file is needed by nscd. +# +# Legal entries are: +# +# logfile <file> +# enable-cache <service> <yes|no> +# debug-level <level> +# positive-time-to-live <service> <time in seconds> +# negative-time-to-live <service> <time in seconds> +# suggested-size <service> <prime number> +# +# Currently supported cache names (services): passwd, group +# + + +# logfile /var/adm/nscd.log +# enable-cache hosts no + + debug-level 0 + + positive-time-to-live passwd 600 + negative-time-to-live passwd 20 + suggested-size passwd 211 + + positive-time-to-live group 3600 + negative-time-to-live group 60 + suggested-size group 211 diff --git a/nscd/nscd.h b/nscd/nscd.h new file mode 100644 index 0000000..4835542 --- /dev/null +++ b/nscd/nscd.h @@ -0,0 +1,149 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _NSCD_H +#define _NSCD_H 1 + +#include <grp.h> +#include <pwd.h> + +/* Version number of the daemon interface */ +#define NSCD_VERSION 1 + +/* How many threads do we spawn maximal ? */ +#define MAX_NUM_CONNECTIONS 16 + +/* Services provided */ +typedef enum +{ + GETPWBYNAME, + GETPWBYUID, + GETGRBYNAME, + GETGRBYGID, + GETHOSTBYNAME, + GETHOSTBYADDR, + SHUTDOWN, /* Shut the server down */ + GETSTAT /* Get the server statistic */ +} request_type; + +/* Header common to all requests */ +typedef struct +{ + /* Version number of the daemon interface */ + int version; + /* Service requested */ + request_type type; + /* key len */ + ssize_t key_len; +} request_header; + +typedef struct +{ + int version; + int found; + ssize_t pw_name_len; + ssize_t pw_passwd_len; + uid_t pw_uid; + gid_t pw_gid; + ssize_t pw_gecos_len; + ssize_t pw_dir_len; + ssize_t pw_shell_len; +} pw_response_header; + +typedef struct +{ + int version; + int found; + ssize_t gr_name_len; + ssize_t gr_passwd_len; + gid_t gr_gid; + ssize_t gr_mem_len; +} gr_response_header; + +typedef struct +{ + int debug_level; + int pw_enabled; + unsigned long pw_poshit; + unsigned long pw_posmiss; + unsigned long pw_neghit; + unsigned long pw_negmiss; + unsigned long pw_size; + unsigned long pw_posttl; + unsigned long pw_negttl; + int gr_enabled; + unsigned long gr_poshit; + unsigned long gr_posmiss; + unsigned long gr_neghit; + unsigned long gr_negmiss; + unsigned long gr_size; + unsigned long gr_posttl; + unsigned long gr_negttl; +} stat_response_header; + +#define _PATH_NSCDPID "/var/run/nscd.pid" +#define _PATH_NSCDSOCKET "/var/run/.nscd_socket" +#define _PATH_NSCDCONF "/etc/nscd.conf" + +typedef struct +{ + char *key; + int conn; +} param_t; + +extern int do_shutdown; /* 1 if we should quit the programm. */ +extern int disabled_passwd; +extern int disabled_group; + +extern int nscd_parse_file __P ((const char *fname)); +extern int set_logfile __P ((const char *logfile)); +extern void set_pos_pwd_ttl __P ((unsigned long)); +extern void set_neg_pwd_ttl __P ((unsigned long)); +extern void set_pos_grp_ttl __P ((unsigned long)); +extern void set_neg_grp_ttl __P ((unsigned long)); +extern void set_pwd_modulo __P ((unsigned long)); +extern void set_grp_modulo __P ((unsigned long)); + +extern void init_sockets __P ((void)); +extern void close_socket __P ((int conn)); +extern void close_sockets __P ((void)); +extern void get_request __P ((int *conn, request_header *req, char **key)); +extern void pw_send_answer __P ((int conn, struct passwd *pwd)); +extern void pw_send_disabled __P ((int conn)); +extern void gr_send_answer __P ((int conn, struct group *grp)); +extern void gr_send_disabled __P ((int conn)); + +extern int cache_pwdinit __P ((void)); +extern void *cache_getpwnam __P ((void *param)); +extern void *cache_getpwuid __P ((void *param)); +extern void *cache_pw_disabled __P ((void *param)); + +extern int cache_grpinit __P ((void)); +extern void *cache_getgrnam __P ((void *param)); +extern void *cache_getgrgid __P ((void *param)); +extern void *cache_gr_disabled __P ((void *param)); + +extern int __nscd_open_socket __P ((void)); + +extern void get_pw_stat __P ((stat_response_header *resp)); +extern void get_gr_stat __P ((stat_response_header *resp)); +extern void print_stat __P ((void)); +extern void stat_send __P ((int conn, stat_response_header *resp)); + +#endif diff --git a/nscd/nscd.init b/nscd/nscd.init new file mode 100644 index 0000000..097ce42 --- /dev/null +++ b/nscd/nscd.init @@ -0,0 +1,50 @@ +#!/bin/sh +# +# nscd: Starts the Name Switch Cache Daemon +# +# chkconfig: 345 52 25 +# description: This is a daemon which handles passwd and group lookups +# for running programs and cache the results for the next +# query. You should start this daemon only if you use +# slow Services like NIS or NIS+ + +# Source function library. +. /etc/rc.d/init.d/functions + +# See how we were called. +case "$1" in + start) + test -f /etc/nscd.conf -a -f /usr/sbin/nscd || exit 0 + secure="" +# for table in passwd group +# do +# if egrep '^'$table':.*nisplus' /etc/nsswitch.conf >/dev/null +# then +# /usr/lib/nscd_nischeck $table || +# secure="$secure -S $table,yes" +# fi +# done + echo -n "Starting Name Switch Cache Daemon: " + daemon nscd $secure + echo + touch /var/lock/subsys/nscd + ;; + stop) + test -f /usr/sbin/nscd || exit 0 + echo -n "Stopping Name Switch Cache Daemon: " + /usr/sbin/nscd -K + rm -f /var/lock/subsys/nscd + echo nscd + ;; + status) + status nscd + ;; + restart) + $0 stop + $0 start + ;; + *) + echo "Usage: /etc/rc.d/init.d/nscd.init {start|stop|status|restart}" + ;; +esac +exit 0 diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c new file mode 100644 index 0000000..59c225e --- /dev/null +++ b/nscd/nscd_conf.c @@ -0,0 +1,148 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <ctype.h> +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "dbg_log.h" +#include "nscd.h" + +int +nscd_parse_file (const char *fname) +{ + FILE *fp; + char *line, *cp, *entry, *arg1, *arg2; + size_t len; + + /* Open the configuration file. */ + fp = fopen (fname, "r"); + if (fp == NULL) + return -1; + + line = NULL; + len = 0; + + do + { + ssize_t n = getline (&line, &len, fp); + if (n < 0) + break; + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + /* Because the file format does not know any form of quoting we + can search forward for the next '#' character and if found + make it terminating the line. */ + cp = strchr (line, '#'); + if (cp != NULL) + *cp = '\0'; + + /* If the line is blank it is ignored. */ + if (line[0] == '\0') + continue; + + entry = line; + while (isspace (*entry) && *entry != '\0') + ++entry; + cp = entry; + while (!isspace (*cp) && *cp != '\0') + ++cp; + arg1 = cp; + ++arg1; + *cp = '\0'; + if (*cp = '\0' || strlen (entry) == 0) + dbg_log (_("Parse error: %s"), line); + while (isspace (*arg1) && *arg1 != '\0') + ++arg1; + cp = arg1; + while (!isspace (*cp) && *cp != '\0') + ++cp; + arg2 = cp; + ++arg2; + *cp = '\0'; + if (strlen (arg2) > 0) + { + while (isspace (*arg2) && *arg2 != '\0') + ++arg2; + cp = arg2; + while (!isspace (*cp) && *cp != '\0') + ++cp; + *cp = '\0'; + } + + if (strcmp (entry, "positive-time-to-live") == 0) + { + if (strcmp (arg1, "passwd") == 0) + set_pos_pwd_ttl (atol (arg2)); + else if (strcmp (arg1, "group") == 0) + set_pos_grp_ttl (atol (arg2)); + else + dbg_log ("server %s is not supported\n", arg1); + } + else if (strcmp (entry, "negative-time-to-live") == 0) + { + if (strcmp (arg1, "passwd") == 0) + set_neg_pwd_ttl (atol (arg2)); + else if (strcmp (arg1, "group") == 0) + set_neg_grp_ttl (atol (arg2)); + else + dbg_log (_("service %s is not supported"), arg1); + } + else if (strcmp (entry, "suggested-size") == 0) + { + if (strcmp (arg1, "passwd") == 0) + set_pwd_modulo (atol (arg2)); + else if (strcmp (arg1, "group") == 0) + set_grp_modulo (atol (arg2)); + else + dbg_log (_("service %s is not supported"), arg1); + } + else if (strcmp (entry, "enable-cache") ==0) + { + if (strcmp (arg1, "passwd") == 0 + && strcmp (arg2, "no") == 0) + disabled_passwd = 1; + } + else if (strcmp (entry, "logfile") == 0) + { + if (!set_logfile (arg1)) + dbg_log (_("Could not create log file \"%s\""), arg1); + } + else if (strcmp (entry, "debug-level") == 0) + { + int level = atoi (arg1); + if (level > 0) + debug_flag = level; + } + else + dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2); + } + while (!feof (fp)); + + /* Free the buffer. */ + free (line); + /* Close configuration file. */ + fclose (fp); + + return 0; +} diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c new file mode 100644 index 0000000..6739657 --- /dev/null +++ b/nscd/nscd_getgr_r.c @@ -0,0 +1,211 @@ +/* Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "nscd.h" +#include "nscd_proto.h" + +static int __nscd_getgr_r (const char *key, request_type type, + struct group *resultbuf, char *buffer, + size_t buflen); + +int +__nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer, + size_t buflen) +{ + if (name == NULL) + return 1; + + return __nscd_getgr_r (name, GETGRBYNAME, resultbuf, buffer, buflen); +} + +int +__nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer, + size_t buflen) +{ + char *p = buffer; + char plen; + + plen = snprintf (buffer, buflen, "%d", gid); + if (plen == -1) + { + __set_errno (ERANGE); + return -1; + } + p = buffer + plen + 1; + + return __nscd_getgr_r (buffer, GETGRBYGID, resultbuf, p, buflen - plen -1); +} + +/* Create a socket connected to a name. */ +static int +nscd_open_socket (void) +{ + struct sockaddr_un addr; + int sock; + + sock = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + addr.sun_family = AF_UNIX; + strcpy (addr.sun_path, _PATH_NSCDSOCKET); + if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) + { + close (sock); + return -1; + } + + return sock; +} + +static int +__nscd_getgr_r (const char *key, request_type type, struct group *resultbuf, + char *buffer, size_t buflen) +{ + int sock = nscd_open_socket (); + request_header req; + gr_response_header gr_resp; + ssize_t nbytes; + + if (sock == -1) + return 1; + + req.version = NSCD_VERSION; + req.type = type; + req.key_len = strlen (key); + nbytes = write (sock, &req, sizeof (request_header)); + if (nbytes != sizeof (request_header)) + { + close (sock); + return 1; + } + + nbytes = write (sock, key, req.key_len); + if (nbytes != req.key_len) + { + close (sock); + return 1; + } + + nbytes = read (sock, &gr_resp, sizeof (gr_response_header)); + if (nbytes != sizeof (gr_response_header)) + { + close (sock); + return 1; + } + + if (gr_resp.found == -1) + { + close (sock); + return 1; + } + + if (gr_resp.found == 1) + { + size_t i; + char *p = buffer; + + if (buflen < gr_resp.gr_name_len + 1) + { + __set_errno (ERANGE); + close (sock); + return -1; + } + resultbuf->gr_name = p; + p += gr_resp.gr_name_len + 1; + buflen -= (gr_resp.gr_name_len + 1); + nbytes = read (sock, resultbuf->gr_name, gr_resp.gr_name_len); + if (nbytes != gr_resp.gr_name_len) + { + close (sock); + return 1; + } + resultbuf->gr_name[gr_resp.gr_name_len] = '\0'; + + if (buflen < gr_resp.gr_passwd_len + 1) + { + __set_errno (ERANGE); + close (sock); + return -1; + } + resultbuf->gr_passwd = p; + p += gr_resp.gr_passwd_len + 1; + buflen -= (gr_resp.gr_passwd_len + 1); + nbytes = read (sock, resultbuf->gr_passwd, gr_resp.gr_passwd_len); + if (nbytes != gr_resp.gr_passwd_len) + { + close (sock); + return 1; + } + resultbuf->gr_passwd[gr_resp.gr_passwd_len] = '\0'; + + resultbuf->gr_gid = gr_resp.gr_gid; + + if (buflen < ((gr_resp.gr_mem_len + 1) * sizeof (char *))) + { + __set_errno (ERANGE); + close (sock); + return -1; + } + resultbuf->gr_mem = (char **)p; + p += ((gr_resp.gr_mem_len + 1) * sizeof (char *)); + buflen -= ((gr_resp.gr_mem_len + 1) * sizeof (char *)); + + resultbuf->gr_mem[gr_resp.gr_mem_len] = NULL; + + for (i = 0; i < gr_resp.gr_mem_len; ++i) + { + size_t len; + nbytes = read (sock, &len, sizeof (len)); + if (nbytes != sizeof (len)) + { + close (sock); + return 1; + } + + if (buflen < (len + 1)) + { + __set_errno (ERANGE); + close (sock); + return -1; + } + resultbuf->gr_mem[i] = p; + p += len + 1; + buflen -= (len + 1); + nbytes = read (sock, resultbuf->gr_mem[i], len); + resultbuf->gr_mem[i][len] = '\0'; + if (nbytes != len) + { + close (sock); + return 1; + } + } + } + close (sock); + return 0; +} diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c new file mode 100644 index 0000000..c956abc --- /dev/null +++ b/nscd/nscd_getpw_r.c @@ -0,0 +1,198 @@ +/* Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/un.h> + +#include "nscd.h" + +static int __nscd_getpw_r (const char *key, request_type type, + struct passwd *resultbuf, char *buffer, + size_t buflen); + +int +__nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer, + size_t buflen) +{ + if (name == NULL) + return 1; + + return __nscd_getpw_r (name, GETPWBYNAME, resultbuf, buffer, buflen); +} + +int +__nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer, + size_t buflen) +{ + char *p = buffer; + char plen; + + plen = snprintf (buffer, buflen, "%d", uid); + if (plen == -1) + { + __set_errno (ERANGE); + return -1; + } + p = buffer + plen + 1; + + return __nscd_getpw_r (buffer, GETPWBYUID, resultbuf, p, buflen - plen -1); +} + +/* Create a socket connected to a name. */ +static int +nscd_open_socket (void) +{ + struct sockaddr_un addr; + int sock; + + sock = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + addr.sun_family = AF_UNIX; + strcpy (addr.sun_path, _PATH_NSCDSOCKET); + if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) + { + close (sock); + return -1; + } + + return sock; +} + +static int +__nscd_getpw_r (const char *key, request_type type, struct passwd *resultbuf, + char *buffer, size_t buflen) +{ + int sock = nscd_open_socket (); + request_header req; + pw_response_header pw_resp; + ssize_t nbytes; + + if (sock == -1) + return 1; + + req.version = NSCD_VERSION; + req.type = type; + req.key_len = strlen (key); + nbytes = write (sock, &req, sizeof (request_header)); + if (nbytes != sizeof (request_header)) + { + close (sock); + return 1; + } + + nbytes = write (sock, key, req.key_len); + if (nbytes != req.key_len) + { + close (sock); + return 1; + } + + nbytes = read (sock, &pw_resp, sizeof (pw_response_header)); + if (nbytes != sizeof (pw_response_header)) + { + close (sock); + return 1; + } + + if (pw_resp.found == -1) + { + close (sock); + return 1; + } + + if (pw_resp.found == 1) + { + struct iovec vec[5]; + char *p = buffer; + + if (buflen < pw_resp.pw_name_len + 1 + pw_resp.pw_passwd_len + 1 + + pw_resp.pw_gecos_len + 1 + pw_resp.pw_dir_len + 1 + + pw_resp.pw_shell_len + 1) + { + __set_errno (ERANGE); + close (sock); + return -1; + } + + /* get pw_name */ + vec[0].iov_base = p; + vec[0].iov_len = pw_resp.pw_name_len; + p += pw_resp.pw_name_len + 1; + buflen -= (pw_resp.pw_name_len + 1); + /* get pw_passwd */ + vec[1].iov_base = p; + vec[1].iov_len = pw_resp.pw_passwd_len; + p += pw_resp.pw_passwd_len + 1; + buflen -= (pw_resp.pw_passwd_len + 1); + /* get pw_gecos */ + vec[2].iov_base = p; + vec[2].iov_len = pw_resp.pw_gecos_len; + p += pw_resp.pw_gecos_len + 1; + buflen -= (pw_resp.pw_gecos_len + 1); + /* get pw_dir */ + vec[3].iov_base = p; + vec[3].iov_len = pw_resp.pw_dir_len; + p += pw_resp.pw_dir_len + 1; + buflen -= (pw_resp.pw_dir_len + 1); + /* get pw_pshell */ + vec[4].iov_base = p; + vec[4].iov_len = pw_resp.pw_dir_len; + p += pw_resp.pw_dir_len + 1; + buflen -= (pw_resp.pw_dir_len + 1); + + nbytes = readv (sock, vec, 5); + if (nbytes != pw_resp.pw_name_len + 1 + pw_resp.pw_passwd_len + 1 + + pw_resp.pw_gecos_len + 1 + pw_resp.pw_dir_len + 1 + + pw_resp.pw_shell_len + 1) + { + close (sock); + return 1; + } + + resultbuf->pw_name = vec[0].iov_base; + resultbuf->pw_name[pw_resp.pw_name_len] = '\0'; + resultbuf->pw_passwd = vec[1].iov_base; + resultbuf->pw_passwd[pw_resp.pw_passwd_len] = '\0'; + resultbuf->pw_uid = pw_resp.pw_uid; + resultbuf->pw_gid = pw_resp.pw_gid; + resultbuf->pw_gecos = vec[2].iov_base; + resultbuf->pw_gecos[pw_resp.pw_gecos_len] = '\0'; + resultbuf->pw_dir = vec[3].iov_base; + resultbuf->pw_dir[pw_resp.pw_dir_len] = '\0'; + resultbuf->pw_shell = vec[4].iov_base; + resultbuf->pw_shell[pw_resp.pw_shell_len] = '\0'; + + close (sock); + return 0; + } + else + { + close (sock); + return -1; + } +} diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h new file mode 100644 index 0000000..6f7b30d --- /dev/null +++ b/nscd/nscd_proto.h @@ -0,0 +1,35 @@ +/* Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _NSCD_PROTO_H +#define _NSCD_PROTO_H 1 + +#include <grp.h> +#include <pwd.h> + +extern int __nscd_getpwnam_r __P ((const char *name, struct passwd *resultbuf, + char *buffer, size_t buflen)); +extern int __nscd_getpwuid_r __P ((uid_t uid, struct passwd *resultbuf, + char *buffer, size_t buflen)); +extern int __nscd_getgrnam_r __P ((const char *name, struct group *resultbuf, + char *buffer, size_t buflen)); +extern int __nscd_getgrgid_r __P ((uid_t uid, struct group *resultbuf, + char *buffer, size_t buflen)); + +#endif /* _NSCD_PROTO_H */ diff --git a/nscd/nscd_stat.c b/nscd/nscd_stat.c new file mode 100644 index 0000000..d818288 --- /dev/null +++ b/nscd/nscd_stat.c @@ -0,0 +1,87 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include "nscd.h" + +void +print_stat (void) +{ + int sock = __nscd_open_socket (); + request_header req; + stat_response_header resp; + ssize_t nbytes; + + if (sock == -1) + { + fputs (_("nscd not running!\n"), stdout); + exit (EXIT_FAILURE); + } + + req.version = NSCD_VERSION; + req.type = GETSTAT; + req.key_len = 0; + nbytes = write (sock, &req, sizeof (request_header)); + if (nbytes != sizeof (request_header)) + { + perror (_("write incomplete")); + close (sock); + exit (EXIT_FAILURE); + } + + nbytes = read (sock, &resp, sizeof (stat_response_header)); + if (nbytes != sizeof (stat_response_header)) + { + perror (_("read incomplete")); + close (sock); + exit (EXIT_FAILURE); + } + + close (sock); + + printf (_("nscd configuration:\n\n")); + printf (_("%12d server debug level\n\n"), resp.debug_level); + + printf (_("passwd cache:\n\n")); + printf (_("%12s cache is enabled\n"), resp.pw_enabled ? _("Yes") : _("No")); + printf (_("%12ld cache hits on positive entries\n"), resp.pw_poshit); + printf (_("%12ld cache hits on negative entries\n"), resp.pw_neghit); + printf (_("%12ld cache misses on positive entries\n"), resp.pw_posmiss); + printf (_("%12ld cache misses on negative entries\n"), resp.pw_negmiss); + printf (_("%12ld suggested size\n"), resp.pw_size); + printf (_("%12ld seconds time to live for positive entries\n"), + resp.pw_posttl); + printf (_("%12ld seconds time to live for negative entries\n\n"), + resp.pw_negttl); + + printf (_("group cache:\n\n")); + printf (_("%12s cache is enabled\n"), resp.gr_enabled ? _("Yes") : _("No")); + printf (_("%12ld cache hits on positive entries\n"), resp.gr_poshit); + printf (_("%12ld cache hits on negative entries\n"), resp.gr_neghit); + printf (_("%12ld cache misses on positive entries\n"), resp.gr_posmiss); + printf (_("%12ld cache misses on negative entries\n"), resp.gr_negmiss); + printf (_("%12ld suggested size\n"), resp.gr_size); + printf (_("%12ld seconds time to live for positive entries\n"), + resp.gr_posttl); + printf (_("%12ld seconds time to live for negative entries\n"), + resp.gr_negttl); +} diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c new file mode 100644 index 0000000..a367666 --- /dev/null +++ b/nscd/pwdcache.c @@ -0,0 +1,581 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <malloc.h> +#include <pthread.h> +#include <pwd.h> +#include <string.h> +#include <rpcsvc/nis.h> +#include <sys/types.h> + +#include "dbg_log.h" +#include "nscd.h" + +static unsigned long int modulo = 211; +static unsigned long int postimeout = 600; +static unsigned long int negtimeout = 20; + +static unsigned long int poshit = 0; +static unsigned long int posmiss = 0; +static unsigned long int neghit = 0; +static unsigned long int negmiss = 0; + +struct pwdhash +{ + time_t create; + struct pwdhash *next; + struct passwd *pwd; +}; +typedef struct pwdhash pwdhash; + +struct uidhash +{ + struct uidhash *next; + struct pwdhash *pwptr; +}; +typedef struct uidhash uidhash; + +struct neghash +{ + time_t create; + struct neghash *next; + char *key; +}; +typedef struct neghash neghash; + +static pwdhash *pwdtbl; +static uidhash *uidtbl; +static neghash *negtbl; + +static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER; +static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER; + +static void *pwdtable_update (void *); +static void *negtable_update (void *); + +void +get_pw_stat (stat_response_header *stat) +{ + stat->pw_poshit = poshit; + stat->pw_posmiss = posmiss; + stat->pw_neghit = neghit; + stat->pw_negmiss = negmiss; + stat->pw_size = modulo; + stat->pw_posttl = postimeout; + stat->pw_negttl = negtimeout; +} + +void +set_pwd_modulo (unsigned long int mod) +{ + modulo = mod; +} + +void +set_pos_pwd_ttl (unsigned long int ttl) +{ + postimeout = ttl; +} + +void +set_neg_pwd_ttl (unsigned long int ttl) +{ + negtimeout = ttl; +} + +int +cache_pwdinit () +{ + pthread_t thread; + + pwdtbl = calloc (modulo, sizeof (pwdhash)); + if (pwdtbl == NULL) + return -1; + uidtbl = calloc (modulo, sizeof (pwdhash)); + if (uidtbl == NULL) + return -1; + negtbl = calloc (modulo, sizeof (neghash)); + if (negtbl == NULL) + return -1; + + pthread_create (&thread, NULL, pwdtable_update, (void *)NULL); + pthread_detach (thread); + pthread_create (&thread, NULL, negtable_update, (void *)NULL); + pthread_detach (thread); + return 0; +} + +static struct passwd * +save_pwd (struct passwd *src) +{ + struct passwd *dest; + + dest = calloc (1, sizeof (struct passwd)); + dest->pw_name = strdup (src->pw_name); + dest->pw_passwd = strdup (src->pw_passwd); + dest->pw_uid = src->pw_uid; + dest->pw_gid = src->pw_gid; + dest->pw_gecos = strdup (src->pw_gecos); + dest->pw_dir = strdup (src->pw_dir); + dest->pw_shell = strdup (src->pw_shell); + + return dest; +} + +static void +free_pwd (struct passwd *src) +{ + free (src->pw_name); + free (src->pw_passwd); + free (src->pw_gecos); + free (src->pw_dir); + free (src->pw_shell); + free (src); +} + +static int +add_cache (struct passwd *pwd) +{ + pwdhash *work; + unsigned long int hash = __nis_hash (pwd->pw_name, + strlen (pwd->pw_name)) % modulo; + + if (debug_flag) + dbg_log (_("add_cache (%s)"), pwd->pw_name); + + work = &pwdtbl[hash]; + + if (pwdtbl[hash].pwd == NULL) + pwdtbl[hash].pwd = save_pwd (pwd); + else + { + while (work->next != NULL) + work = work->next; + + work->next = calloc (1, sizeof (pwdhash)); + work->next->pwd = save_pwd (pwd); + work = work->next; + } + /* Set a pointer from the pwuid hash table to the pwname hash table */ + time (&work->create); + uidtbl[pwd->pw_uid % modulo].pwptr = work; + + return 0; +} + +static struct passwd * +cache_search_name (const char *name) +{ + pwdhash *work; + unsigned long int hash = __nis_hash (name, strlen (name)) % modulo; + + work = &pwdtbl[hash]; + + while (work->pwd != NULL) + { + if (strcmp (work->pwd->pw_name, name) == 0) + return work->pwd; + if (work->next != NULL) + work = work->next; + else + return NULL; + } + return NULL; +} + +static struct passwd * +cache_search_uid (uid_t uid) +{ + uidhash *work; + + work = &uidtbl[uid % modulo]; + + while (work->pwptr != NULL) + { + if (work->pwptr->pwd->pw_uid == uid) + return work->pwptr->pwd; + if (work->next != NULL) + work = work->next; + else + return NULL; + } + return NULL; +} + +static int +add_negcache (char *key) +{ + neghash *work; + unsigned long int hash = __nis_hash (key, strlen (key)) % modulo; + + if (debug_flag) + dbg_log (_("add_netgache (%s|%ld)"), key, hash); + + work = &negtbl[hash]; + + if (negtbl[hash].key == NULL) + { + negtbl[hash].key = strdup (key); + negtbl[hash].next = NULL; + } + else + { + while (work->next != NULL) + work = work->next; + + work->next = calloc (1, sizeof (neghash)); + work->next->key = strdup (key); + work = work->next; + } + /* Set a pointer from the pwuid hash table to the pwname hash table */ + time (&work->create); + + return 0; +} + +static int +cache_search_neg (const char *key) +{ + neghash *work; + unsigned long int hash = __nis_hash (key, strlen (key)) % modulo; + + work = &negtbl[hash]; + + if (debug_flag) + dbg_log (_("cache_search_neg (%s|%ld)"), key, hash); + + while (work->key != NULL) + { + if (strcmp (work->key, key) == 0) + return 1; + if (work->next != NULL) + work = work->next; + else + return 0; + } + return 0; +} + +void * +cache_getpwnam (void *v_param) +{ + param_t *param = (param_t *)v_param; + struct passwd *pwd, resultbuf; + + pthread_rwlock_rdlock (&pwdlock); + pwd = cache_search_name (param->key); + + /* I don't like it to hold the read only lock longer, but it is + necessary to avoid to much malloc/free/strcpy. */ + + if (pwd != NULL) + { + if (debug_flag) + dbg_log (_("Found \"%s\" in cache !"), param->key); + + ++poshit; + pw_send_answer (param->conn, pwd); + close_socket (param->conn); + + pthread_rwlock_unlock (&pwdlock); + pwd = &resultbuf; + } + else + { + int status; + int buflen = 1024; + char *buffer = malloc (buflen); + + if (debug_flag) + dbg_log (_("Doesn't found \"%s\" in cache !"), param->key); + + pthread_rwlock_unlock (&pwdlock); + + pthread_rwlock_rdlock (&neglock); + status = cache_search_neg (param->key); + pthread_rwlock_unlock (&neglock); + + if (status == 0) + { + while (buffer != NULL + && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd) + != 0) + && errno == ERANGE) + { + errno = 0; + buflen += 1024; + buffer = realloc (buffer, buflen); + } + + if (buffer != NULL && pwd != NULL) + { + struct passwd *tmp; + + ++posmiss; + pthread_rwlock_wrlock (&pwdlock); + /* While we are waiting on the lock, somebody else could + add this entry. */ + tmp = cache_search_name (param->key); + if (tmp == NULL) + add_cache (pwd); + pthread_rwlock_unlock (&pwdlock); + } + else + { + ++negmiss; + pthread_rwlock_wrlock (&neglock); + add_negcache (param->key); + pthread_rwlock_unlock (&neglock); + } + } + else + ++neghit; + pw_send_answer (param->conn, pwd); + close_socket (param->conn); + if (buffer != NULL) + free (buffer); + } + free (param->key); + free (param); + return NULL; +} + +void * +cache_pw_disabled (void *v_param) +{ + param_t *param = (param_t *)v_param; + + pw_send_disabled (param->conn); + return NULL; +} + +void * +cache_getpwuid (void *v_param) +{ + param_t *param = (param_t *)v_param; + struct passwd *pwd, resultbuf; + uid_t uid = strtol (param->key, NULL, 10); + + pthread_rwlock_rdlock (&pwdlock); + pwd = cache_search_uid (uid); + + /* I don't like it to hold the read only lock longer, but it is + necessary to avoid to much malloc/free/strcpy. */ + + if (pwd != NULL) + { + if (debug_flag) + dbg_log (_("Found \"%d\" in cache !"), uid); + + ++poshit; + pw_send_answer (param->conn, pwd); + close_socket (param->conn); + + pthread_rwlock_unlock (&pwdlock); + } + else + { + int buflen = 1024; + char *buffer = malloc (buflen); + int status; + + if (debug_flag) + dbg_log (_("Doesn't found \"%d\" in cache !"), uid); + + pthread_rwlock_unlock (&pwdlock); + + pthread_rwlock_rdlock (&neglock); + status = cache_search_neg (param->key); + pthread_rwlock_unlock (&neglock); + + if (status == 0) + { + while (buffer != NULL + && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0) + && errno == ERANGE) + { + errno = 0; + buflen += 1024; + buffer = realloc (buffer, buflen); + } + + if (buffer != NULL && pwd != NULL) + { + struct passwd *tmp; + + ++posmiss; + pthread_rwlock_wrlock (&pwdlock); + /* While we are waiting on the lock, somebody else could + add this entry. */ + tmp = cache_search_uid (uid); + if (tmp == NULL) + add_cache (pwd); + pthread_rwlock_unlock (&pwdlock); + } + else + { + ++negmiss; + pthread_rwlock_wrlock (&neglock); + add_negcache (param->key); + pthread_rwlock_unlock (&neglock); + } + } + else + ++neghit; + + pw_send_answer (param->conn, pwd); + close_socket (param->conn); + if (buffer != NULL) + free (buffer); + } + free (param->key); + free (param); + return NULL; +} + +void * +pwdtable_update (void *v) +{ + time_t now; + int i; + + sleep (20); + + while (!do_shutdown) + { + if (debug_flag > 2) + dbg_log (_("(pwdtable_update) Wait for write lock!")); + + pthread_rwlock_wrlock (&pwdlock); + + if (debug_flag > 2) + dbg_log (_("(pwdtable_update) Have write lock")); + + time (&now); + for (i = 0; i < modulo; ++i) + { + pwdhash *work = &pwdtbl[i]; + + while (work && work->pwd) + { + if ((now - work->create) >= postimeout) + { + uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo]; + + if (debug_flag) + dbg_log (_("Give \"%s\" free"), work->pwd->pw_name); + + while (uh != NULL && uh->pwptr) + { + if (uh->pwptr->pwd->pw_uid == work->pwd->pw_uid) + { + if (debug_flag) + dbg_log (_("Give uid for \"%s\" free"), + work->pwd->pw_name); + if (uh->next != NULL) + { + uidhash *tmp = uh->next; + uh->pwptr = tmp->pwptr; + uh->next = tmp->next; + free (tmp); + } + else + uh->pwptr = NULL; + } + uh = uh->next; + } + + free_pwd (work->pwd); + if (work->next != NULL) + { + pwdhash *tmp = work->next; + work->create = tmp->create; + work->next = tmp->next; + work->pwd = tmp->pwd; + free (tmp); + } + else + work->pwd = NULL; + } + work = work->next; + } + } + if (debug_flag > 2) + dbg_log (_("(pwdtable_update) Release wait lock")); + pthread_rwlock_unlock (&pwdlock); + sleep (20); + } + return NULL; +} + +void * +negtable_update (void *v) +{ + time_t now; + int i; + + sleep (30); + + while (!do_shutdown) + { + if (debug_flag > 2) + dbg_log (_("(negtable_update) Wait for write lock!")); + + pthread_rwlock_wrlock (&neglock); + + if (debug_flag) + dbg_log (_("(negtable_update) Have write lock")); + + time (&now); + for (i = 0; i < modulo; ++i) + { + neghash *work = &negtbl[i]; + + while (work && work->key) + { + if ((now - work->create) >= negtimeout) + { + if (debug_flag) + dbg_log (_("Give \"%s\" free"), work->key); + + free (work->key); + + if (work->next != NULL) + { + neghash *tmp = work->next; + work->create = tmp->create; + work->next = tmp->next; + work->key = tmp->key; + free (tmp); + } + else + work->key = NULL; + } + work = work->next; + } + } + if (debug_flag) + dbg_log (_("(negtable_update) Release wait lock")); + + pthread_rwlock_unlock (&neglock); + sleep (10); + } + return NULL; +} |