Fix gr_name pointer [PR/1702].
[kopensolaris-gnu/glibc.git] / nscd / connections.c
index 8e6839a..651084c 100644 (file)
@@ -1,5 +1,5 @@
 /* Inner loops of cache daemon.
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
@@ -24,6 +24,8 @@
 #include <pthread.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <libintl.h>
+#include <arpa/inet.h>
 #include <sys/param.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
@@ -59,38 +61,48 @@ const char *serv2str[LASTREQ] =
   [GETHOSTBYADDR] = "GETHOSTBYADDR",
   [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
   [SHUTDOWN] = "SHUTDOWN",
-  [GETSTAT] = "GETSTAT"
+  [GETSTAT] = "GETSTAT",
+  [INVALIDATE] = "INVALIDATE"
 };
 
 /* The control data structures for the services.  */
 static struct database dbs[lastdb] =
 {
   [pwddb] = {
-    lock: PTHREAD_RWLOCK_INITIALIZER,
-    enabled: 1,
+    lock: PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    enabled: 0,
     check_file: 1,
     filename: "/etc/passwd",
     module: 211,
-    disabled_iov: &pwd_iov_disabled
+    disabled_iov: &pwd_iov_disabled,
+    postimeout: 3600,
+    negtimeout: 20
   },
   [grpdb] = {
-    lock: PTHREAD_RWLOCK_INITIALIZER,
-    enabled: 1,
+    lock: PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    enabled: 0,
     check_file: 1,
     filename: "/etc/group",
     module: 211,
-    disabled_iov: &grp_iov_disabled
+    disabled_iov: &grp_iov_disabled,
+    postimeout: 3600,
+    negtimeout: 60
   },
   [hstdb] = {
-    lock: PTHREAD_RWLOCK_INITIALIZER,
-    enabled: 1,
+    lock: PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    enabled: 0,
     check_file: 1,
     filename: "/etc/hosts",
     module: 211,
-    disabled_iov: &hst_iov_disabled
+    disabled_iov: &hst_iov_disabled,
+    postimeout: 3600,
+    negtimeout: 20
   }
 };
 
+/* Number of seconds between two cache pruning runs.  */
+#define CACHE_PRUNE_INTERVAL   15
+
 /* Number of threads to use.  */
 int nthreads = -1;
 
@@ -182,13 +194,29 @@ close_sockets (void)
   close (sock);
 }
 
+static void
+invalidate_cache (char *key)
+{
+  dbtype number;
+
+  if (strcmp (key, "passwd") == 0)
+    number = pwddb;
+  else if (strcmp (key, "group") == 0)
+    number = grpdb;
+  else if (strcmp (key, "hosts") == 0)
+    number = hstdb;
+  else return;
+
+  prune_cache (&dbs[number], LONG_MAX);
+}
+
 
 /* Handle new request.  */
 static void
-handle_request (int fd, request_header *req, void *key)
+handle_request (int fd, request_header *req, void *key, uid_t uid)
 {
   if (debug_level > 0)
-    dbg_log (_("handle_requests: request received (Version = %d)"),
+    dbg_log (_("handle_request: request received (Version = %d)"),
             req->version);
 
   if (req->version != NSCD_VERSION)
@@ -205,12 +233,24 @@ cannot handle old request version %d; current version is %d"),
       struct database *db = &dbs[serv2db[req->type]];
 
       if (debug_level > 0)
-       dbg_log ("\t%s (%s)", serv2str[req->type], key);
+       {
+         if (req->type == GETHOSTBYADDR || req->type == GETHOSTBYADDRv6)
+           {
+             char buf[INET6_ADDRSTRLEN];
+
+             dbg_log ("\t%s (%s)", serv2str[req->type],
+                      inet_ntop (req->type == GETHOSTBYADDR
+                                 ? AF_INET : AF_INET6,
+                                 key, buf, sizeof (buf)));
+           }
+         else
+           dbg_log ("\t%s (%s)", serv2str[req->type], key);
+       }
 
       /* Is this service enabled?  */
       if (!db->enabled)
        {
-         /* No sent the prepared record.  */
+         /* No, sent the prepared record.  */
          if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
                                         db->disabled_iov->iov_len))
              != db->disabled_iov->iov_len)
@@ -229,7 +269,7 @@ cannot handle old request version %d; current version is %d"),
 
       /* See whether we can handle it from the cache.  */
       cached = (struct hashentry *) cache_search (req->type, key, req->key_len,
-                                                 db);
+                                                 db, uid);
       if (cached != NULL)
        {
          /* Hurray it's in the cache.  */
@@ -249,55 +289,94 @@ cannot handle old request version %d; current version is %d"),
 
       pthread_rwlock_unlock (&db->lock);
     }
-  else
-    if (debug_level > 0)
-      dbg_log ("\t%s", serv2str[req->type]);
+  else if (debug_level > 0)
+    {
+      if (req->type == INVALIDATE)
+       dbg_log ("\t%s (%s)", serv2str[req->type], key);
+      else
+       dbg_log ("\t%s", serv2str[req->type]);
+    }
 
   /* Handle the request.  */
   switch (req->type)
     {
     case GETPWBYNAME:
-      addpwbyname (&dbs[serv2db[req->type]], fd, req, key);
+      addpwbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETPWBYUID:
-      addpwbyuid (&dbs[serv2db[req->type]], fd, req, key);
+      addpwbyuid (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETGRBYNAME:
-      addgrbyname (&dbs[serv2db[req->type]], fd, req, key);
+      addgrbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETGRBYGID:
-      addgrbygid (&dbs[serv2db[req->type]], fd, req, key);
+      addgrbygid (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETHOSTBYNAME:
-      addhstbyname (&dbs[serv2db[req->type]], fd, req, key);
+      addhstbyname (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETHOSTBYNAMEv6:
-      addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key);
+      addhstbynamev6 (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETHOSTBYADDR:
-      addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key);
+      addhstbyaddr (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETHOSTBYADDRv6:
-      addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key);
+      addhstbyaddrv6 (&dbs[serv2db[req->type]], fd, req, key, uid);
       break;
 
     case GETSTAT:
-      send_stats (fd, dbs);
-      break;
-
     case SHUTDOWN:
-      termination_handler (0);
+    case INVALIDATE:
+      /* Accept shutdown, getstat and invalidate only from root */
+      if (secure_in_use && uid == 0)
+       {
+         if (req->type == GETSTAT)
+           send_stats (fd, dbs);
+         else if (req->type == INVALIDATE)
+           invalidate_cache (key);
+         else
+           termination_handler (0);
+       }
+      else
+       {
+         struct ucred caller;
+         socklen_t optlen = sizeof (caller);
+
+         /* Some systems have no SO_PEERCRED implementation.  They don't
+            care about security so we don't as well.  */
+#ifdef SO_PEERCRED
+         if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
+           {
+             char buf[256];
+
+             dbg_log (_("error getting callers id: %s"),
+                      strerror_r (errno, buf, sizeof (buf)));
+           }
+         else
+           if (caller.uid == 0)
+#endif
+             {
+               if (req->type == GETSTAT)
+                 send_stats (fd, dbs);
+               else if (req->type == INVALIDATE)
+                 invalidate_cache (key);
+               else
+                 termination_handler (0);
+             }
+       }
       break;
 
     default:
-      abort ();
+      /* Ignore the command, it's nothing we know.  */
+      break;
     }
 }
 
@@ -308,11 +387,11 @@ static void *
 __attribute__ ((__noreturn__))
 nscd_run (void *p)
 {
-  int my_number = (int) p;
+  long int my_number = (long int) p;
   struct pollfd conn;
   int run_prune = my_number < lastdb && dbs[my_number].enabled;
   time_t now = time (NULL);
-  time_t next_prune = now + 15;
+  time_t next_prune = now + CACHE_PRUNE_INTERVAL;
   int timeout = run_prune ? 1000 * (next_prune - now) : -1;
 
   conn.fd = sock;
@@ -328,7 +407,7 @@ nscd_run (void *p)
          assert (my_number < lastdb);
          now = time (NULL);
          prune_cache (&dbs[my_number], now);
-         next_prune = now + 15;
+         next_prune = now + CACHE_PRUNE_INTERVAL;
          timeout = 1000 * (next_prune - now);
          continue;
        }
@@ -340,6 +419,7 @@ nscd_run (void *p)
          int fd = accept (conn.fd, NULL, NULL);
          request_header req;
          char buf[256];
+         uid_t uid = 0;
 
          if (fd < 0)
            {
@@ -358,12 +438,35 @@ nscd_run (void *p)
              continue;
            }
 
+         /* Some systems have no SO_PEERCRED implementation.  They don't
+            care about security so we don't as well.  */
+#ifdef SO_PEERCRED
+         if (secure_in_use)
+           {
+             struct ucred caller;
+             socklen_t optlen = sizeof (caller);
+
+             if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED,
+                             &caller, &optlen) < 0)
+               {
+                 dbg_log (_("error getting callers id: %s"),
+                          strerror_r (errno, buf, sizeof (buf)));
+                 close (fd);
+                 continue;
+               }
+
+             if (req.type < GETPWBYNAME || req.type > LASTDBREQ
+                 || secure[serv2db[req.type]])
+               uid = caller.uid;
+           }
+#endif
+
          /* It should not be possible to crash the nscd with a silly
-            request (i.e., a terribly large key.  We limit the size
+            request (i.e., a terribly large key).  We limit the size
             to 1kb.  */
          if (req.key_len < 0 || req.key_len > 1024)
            {
-             dbg_log (_("key length in request to long: %Zd"), req.key_len);
+             dbg_log (_("key length in request too long: %zd"), req.key_len);
              close (fd);
              continue;
            }
@@ -382,7 +485,7 @@ nscd_run (void *p)
                }
 
              /* Phew, we got all the data, now process it.  */
-             handle_request (fd, &req, keybuf);
+             handle_request (fd, &req, keybuf, uid);
 
              /* We are done.  */
              close (fd);
@@ -402,7 +505,7 @@ nscd_run (void *p)
 void
 start_threads (void)
 {
-  int i;
+  long int i;
   pthread_attr_t attr;
   pthread_t th;