This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Use reserved port only when required for NIS look-ups


Hi all,

we have a request from user [2] to use reserved port only when required for NIS look-ups. This is the case we need to address:

When non-root user asks NIS server for any information, unprivileged port is used on client side, so there are enough ports to use. However, when root asks NIS server for any information, reserved port is used every-time. Having a client with many look-ups with root privileges we can get to a state where all reserved ports are exhausted and some other services that need to bind to a reserved port can eventually fail.

One ancient discussion about limited reserved ports count with regards to well-known ports is available at [1], but this is something different.

This is reason why we can't simply use unprivileged ports for all look-ups:

NIS server is able to be "secured" with a configuration, where administrator marks some maps as "secure" (basically maps with passwords) and only connections form a reserved port are served in that case. So if we used non-reserved ports for all look-ups, some of them wouldn't be served.

HP's solution to address this is described like as follow:

"Reserved ports are the ports from 0 to 1024. Only root users can bind to these ports. In previous releases, NIS commands attempted to bind to reserved ports by default. If there are numerous client requests, all the reserved ports can be consumed. This version of NIS enables binding to reserved ports for select commands or daemons when accessing secure maps which results in reduced usage of reserved ports by NIS. This change does not compromise performance or security."

I'd like to propose a similar behavior, ie. to limit use of reserved ports only for "secured maps" requests. The problem is, that client currently doesn't know which maps are marked "secured" on the server. Because of that we'd have to introduce a new client-side configuration option or environment variable (used in a patch attached) to define secure maps on client side. That configure option could default to "all", which would mean the same as the current behavior.

I'm not aware of any security implications that would be introduced by using unreserved ports, but I'm not a security expert.

Any comments/suggestions?

Cheers,
Honza

[1] http://sourceware.org/ml/libc-alpha/1999-06/msg00079.html
[2] https://bugzilla.redhat.com/show_bug.cgi?id=689424
diff -up glibc-2.12-2-gc4ccff1/nis/ypclnt.c.resport glibc-2.12-2-gc4ccff1/nis/ypclnt.c
--- glibc-2.12-2-gc4ccff1/nis/ypclnt.c.resport	2010-05-04 13:27:23.000000000 +0200
+++ glibc-2.12-2-gc4ccff1/nis/ypclnt.c	2012-08-10 15:10:09.967432841 +0200
@@ -719,6 +719,76 @@ __xdr_ypresp_all (XDR *xdrs, struct ypre
     }
 }
 
+/* Create a new socket for client side of communication. If the map 
+   is something other than passwd*, use unprivilege port. */
+static int 
+__get_clnt_socket(int *sock, struct sockaddr_in *clnt_sin, const char *inmap) 
+{
+  struct sockaddr_in socket_address;
+  u_short dst_port;
+  char bind_reserved = 1;
+  char *secured_maps;
+  char *end;
+  
+  if (clnt_sin->sin_port == 0)
+    {
+      if ((dst_port = pmap_getport (clnt_sin, YPPROG, YPVERS, IPPROTO_TCP)) == 0)
+        return YPERR_YPERR;
+      clnt_sin->sin_port = htons(dst_port);
+    }
+  
+  if ((*sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+    return YPERR_YPERR;
+
+  /* search for inmap in YP_SECURE_MAPS, which can be empty,
+     contain "*" or maps separated by colon */
+  if ((secured_maps = getenv("YP_SECURE_MAPS")) != NULL &&
+      secured_maps[0] != '\0' &&
+      strcmp("*", secured_maps) != 0)
+  {
+    bind_reserved = 0;
+    while (1)
+    {
+      if ((end = strchr(secured_maps, ':')) != NULL)
+      {
+        /* compare one map in the list separated by colon */
+        if (end > secured_maps &&
+            strncmp(secured_maps, inmap, end - secured_maps) == 0 &&
+            inmap[end - secured_maps] == '\0')
+        {
+          bind_reserved = 1;
+          break;
+        }
+        secured_maps = end + 1;
+      }
+      else
+      {
+        /* compare the last map */
+        if (strcmp(secured_maps, inmap) == 0)
+          bind_reserved = 1;
+        break;
+      }
+    }
+  }
+
+  /* we don't need bind to reserved port when asking for non-secure maps */ 
+  if (!bind_reserved || bindresvport(*sock, (struct sockaddr_in *) NULL) < 0)
+    {
+      memset (&socket_address, 0, sizeof (socket_address));
+      socket_address.sin_family = AF_INET;
+      socket_address.sin_addr.s_addr = htonl(INADDR_ANY);
+      socket_address.sin_port = 0;
+      if (bind (*sock, (struct sockaddr *) &socket_address,
+          sizeof (socket_address)) < 0)
+        return YPERR_YPERR;
+    }
+  if (connect (*sock, (struct sockaddr *) clnt_sin, sizeof (*clnt_sin)) < 0)
+    return YPERR_YPERR;
+  
+  return YPERR_SUCCESS;
+}
+
+
 int
 yp_all (const char *indomain, const char *inmap,
 	const struct ypall_callback *incallback)
@@ -755,6 +825,9 @@ yp_all (const char *indomain, const char
       /* We don't need the UDP connection anymore.  */
       __yp_unbind (ydb);
       ydb = NULL;
+      
+      if ((res = __get_clnt_socket(&clnt_sock, &clnt_sin, inmap)) != YPERR_SUCCESS)
+        return res;
 
       clnt = clnttcp_create (&clnt_sin, YPPROG, YPVERS, &clnt_sock, 0, 0);
       if (clnt == NULL)
@@ -783,6 +856,8 @@ yp_all (const char *indomain, const char
 	res = YPERR_SUCCESS;
 
       clnt_destroy (clnt);
+      
+      (void) __close (clnt_sock);
 
       if (res == YPERR_SUCCESS && data.status != YP_NOMORE)
 	{

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]