local-bindresvport_blacklist
authorGNU Libc Maintainers <debian-glibc@lists.debian.org>
Sat, 29 Jun 2024 10:27:34 +0000 (13:27 +0300)
committerAdrian Bunk <bunk@debian.org>
Sat, 29 Jun 2024 10:27:34 +0000 (13:27 +0300)
Patch from the OpenSUSE glibc

Gbp-Pq: Topic any
Gbp-Pq: Name local-bindresvport_blacklist.diff

sunrpc/bindrsvprt.c

index da33c05101d9430530461e4941ca7cdc81a0e46f..71221701f5d683b1a8e740b09a1fb2ebceae136c 100644 (file)
  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
+#include <ctype.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <libc-lock.h>
 
+#define STARTPORT 600
+#define LOWPORT 512
+#define ENDPORT (IPPORT_RESERVED - 1)
+#define NPORTS (ENDPORT - STARTPORT + 1)
+
+/*
+ * Read the file /etc/rpc.blacklisted, so that we don't bind
+ * to this ports.
+ */
+
+static int blacklist_read;
+static int *list;
+static int list_size = 0;
+
+static void
+load_blacklist (void)
+{
+  FILE *fp;
+  char *buf = NULL;
+  size_t buflen = 0;
+  int size = 0, ptr = 0;
+
+  blacklist_read = 1;
+
+  fp = fopen ("/etc/bindresvport.blacklist", "r");
+  if (NULL == fp)
+    return;
+
+  while (!feof_unlocked (fp))
+    {
+      unsigned long port;
+      char *tmp, *cp;
+      ssize_t n = __getline (&buf, &buflen, fp);
+      if (n < 1)
+        break;
+
+      cp = buf;
+      tmp = strchr (cp, '#');  /* remove comments */
+      if (tmp)
+        *tmp = '\0';
+      while (isspace ((int)*cp))    /* remove spaces and tabs */
+        ++cp;
+      if (*cp == '\0')        /* ignore empty lines */
+        continue;
+      if (cp[strlen (cp) - 1] == '\n')
+        cp[strlen (cp) - 1] = '\0';
+
+      port = strtoul (cp, &tmp, 0);
+      while (isspace(*tmp))
+        ++tmp;
+      if (*tmp != '\0' || (port == ULONG_MAX && errno == ERANGE))
+       continue;
+
+      /* Don't bother with out-of-range ports */
+      if (port < LOWPORT || port > ENDPORT)
+        continue;
+
+      if (ptr >= size)
+       {
+         size += 10;
+         list = realloc (list, size * sizeof (int));
+         if (list == NULL)
+           {
+             free (buf);
+             return;
+           }
+       }
+
+      list[ptr++] = port;
+    }
+
+  fclose (fp);
+
+  if (buf)
+    free (buf);
+
+  list_size = ptr;
+}
+
 /*
  * Locks the static variables in this file.
  */
@@ -48,15 +130,13 @@ __libc_lock_define_initialized (static, lock);
 int
 bindresvport (int sd, struct sockaddr_in *sin)
 {
+  static short startport = STARTPORT;
   static short port;
   struct sockaddr_in myaddr;
   int i;
 
-#define STARTPORT 600
-#define LOWPORT 512
-#define ENDPORT (IPPORT_RESERVED - 1)
-#define NPORTS (ENDPORT - STARTPORT + 1)
-  static short startport = STARTPORT;
+  if (!blacklist_read)
+    load_blacklist ();
 
   if (sin == (struct sockaddr_in *) 0)
     {
@@ -75,6 +155,7 @@ bindresvport (int sd, struct sockaddr_in *sin)
       port = (__getpid () % NPORTS) + STARTPORT;
     }
 
+  __set_errno (EADDRINUSE);
   /* Initialize to make gcc happy.  */
   int res = -1;
 
@@ -86,12 +167,22 @@ bindresvport (int sd, struct sockaddr_in *sin)
  again:
   for (i = 0; i < nports; ++i)
     {
-      sin->sin_port = htons (port++);
-      if (port > endport)
-       port = startport;
+      int j;
+
+      sin->sin_port = htons (port);
+
+      /* Check, if this port is not blacklisted.  */
+      for (j = 0; j < list_size; j++)
+       if (port == list[j])
+         goto try_next_port;
+
       res = __bind (sd, sin, sizeof (struct sockaddr_in));
       if (res >= 0 || errno != EADDRINUSE)
        break;
+
+try_next_port:
+      if (++port > endport)
+       port = startport;
     }
 
   if (i == nports && startport != LOWPORT)