chiark / gitweb /
wip v6 support etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 1 Jun 2012 22:29:44 +0000 (23:29 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 1 Jun 2012 22:29:44 +0000 (23:29 +0100)
authbind.1
helper.c

index 2b372eaf08396cf2c5bc5e6af200e35e88999404..09f9fc2c2b02e52c8cc6e0ffbe8b38c0ecf4514c 100644 (file)
@@ -65,7 +65,7 @@ of files in a configuration area,
 .BR /etc/authbind .
 .PP
 Firstly,
-.BI /etc/authbind/byport/ port
+.BR /etc/authbind/byport/ [ ! ]\fIport\fR
 is tested.  If this file is accessible for execution to the calling
 user, according to
 .BR access (2),
@@ -84,11 +84,11 @@ call, usually
 .RI ( "Permission denied" ).
 .PP
 Secondly, if that test fails to resolve the matter,
-.BI /etc/authbind/byaddr/ addr : port
+.BR /etc/authbind/byaddr/ \fIaddr\fR : [ ! ]\fIport\fR
 is tested, in the same manner as above.
 .PP
 Thirdly, if the question is still unresolved, the file
-.BI /etc/authbind/byuid/ uid
+.BR /etc/authbind/byuid/ [ ! ]\fIuid\fR
 will be opened and read.  If the file does not exist then the binding
 is not authorised and
 .B bind
@@ -97,15 +97,19 @@ will return
 .RI ( "Operation not permitted" ", or " "Not owner" ).
 If the file does exist it will be searched for a line of the form
 .nf
-.IB            addr / length : min\-port , max\-port
+.IB            addr4 / length : min\-port , max\-port
+.IR            addrmin [\fB-\fR addrmax ]\fB:\fR min\-port \fB,\fR max\-port
 .fi
-matching the request (ie, the initial
+matching the request.   The first form requires that the initial
 .I length
 bits of
 .I addr
 match those in the proposed
 .B bind
-call, and the proposed port number lies is in the inclusive range
+call.  The second form requires that the address lies in the
+relevant range (inclusive at both ends).  Addresses can
+be in any form acceptable to inet_pton.  In both cases
+the proposed port number must lie is in the inclusive range
 specified.  If such a line is found then the binding is authorised.
 Otherwise it is not, and
 .B bind
@@ -134,6 +138,17 @@ files are silently ignored (as are lines whose
 has non-zero bits more than
 .I length
 from the top).
+.TP
+Authorising binding to ports from 512 to 1023 inclusive is
+not recommended.  Some protocols (including some versions of NFS)
+authorise clients by seeing that they are using a port number in this
+range.  So by authorising a program to be a server for such a port,
+you are also authorising it to impersonate the whole host for those
+protocols.  To make sure that this isn't done by accident,
+if the port number requested is in the range 512-1023, all the files
+checked and read will have the additional
+.B !
+character.
 .SH MECHANISM
 The shared library loaded using
 .B LD_PRELOAD
@@ -178,7 +193,8 @@ the program's stderr, was well as returning -1 from
 .BR bind .
 .SH BUGS
 .B authbind
-currently only supports IPv4 sockets.  Programs which open other kinds
+currently only supports IPv4 and IPv6 sockets.
+Programs which open other kinds
 of sockets will not benefit from
 .BR authbind ,
 but it won't get in their way.
@@ -234,11 +250,6 @@ to happen and
 signal to be delivered.  Programs should not rely on standard
 libraries not doing these things.
 .PP
-Ports from 512 to 1023 inclusive cannot be used with
-.B authbind
-because that would create a security hole, in conjection with
-.BR rshd .
-.PP
 The access control configuration scheme is somewhat strange.
 .SH FILES AND ENVIRONMENT VARIABLES
 .TP
index 7cfd91ad48e14e8fdfbfc7a5eaa10241f8acc949..7c50551c6bd54ec8c697fbf1b168c499135eab09 100644 (file)
--- a/helper.c
+++ b/helper.c
@@ -53,67 +53,148 @@ static void badusage(void) {
 }
 
 static struct sockaddr_in saddr4;
+static struct sockaddr_in6 saddr6;
+
+static struct sockaddr *saddr_any;
+static const void *addr_any;
+static size_t saddrlen_any, addrlen_any;
 
 static void authorised(void) {
-  if (bind(0,&saddr4,sizeof(saddr4))) exiterrno(errno);
+  if (bind(0,saddr_any,saddrlen_any)) exiterrno(errno);
   else _exit(0);
 }
 
+static void hex2bytes(const char *string, unsigned char *out, int len) {
+  int i;
+  for (i=0; i<len; i++) {
+    char hex[3], *ep;
+    hex[0]= *string++;  if (!hex[0]) badusage();
+    hex[1]= *string++;  if (!hex[1]) badusage();
+    hex[2]= 0;
+    *out++ = strtoul(hex,&ep,16);
+    if (ep != &hex[2]) badusage();
+  }
+}
+
 int main(int argc, const char *const *argv) {
   uid_t uid;
-  char fnbuf[100];
+  char fnbuf[300];
   char *ep;
   const char *np;
-  unsigned long addr, port, haddr, thaddr, thmask;
+  const char *tophalfchar="";
+  unsigned long port, addr4=0, haddr4=0;
   unsigned int hport, a1,a2,a3,a4, alen,pmin,pmax;
-  int nchar;
+  int nchar, af;
   FILE *file;
 
-  if (argc != 3) badusage(); 
-  addr= strtoul(argv[1],&ep,16); if (*ep || addr&~0x0ffffffffUL) badusage();
+  if (argc == 3) {
+    af= AF_INET;
+    saddr_any= (void*)&saddr4;
+    saddrlen_any= sizeof(saddr4);
+    addr_any= &saddr4.sin_addr.s_addr;
+    addrlen_any= sizeof(saddr4.sin_addr.s_addr);
+    addr4= strtoul(argv[1],&ep,16);
+    if (*ep || addr4&~0x0ffffffffUL) badusage();
+    haddr4= ntohl(addr4);
+  } else if (argc == 4 && !strcmp(argv[3],"6")) {
+    af= AF_INET6;
+    saddr_any= (void*)&saddr6;
+    saddrlen_any= sizeof(saddr6);
+    addr_any= &saddr6.sin6_addr.s6_addr;
+    addrlen_any= sizeof(saddr6.sin6_addr.s6_addr);
+    hex2bytes(argv[1], saddr6.sin6_addr.s6_addr,
+             sizeof(saddr6.sin6_addr.s6_addr));
+  } else {
+    badusage();
+    abort();
+  }
+
   port= strtoul(argv[2],&ep,16); if (*ep || port&~0x0ffffUL) badusage();
   hport= htons(port);
-  if (hport >= IPPORT_RESERVED/2) _exit(EPERM);
+  if (hport >= IPPORT_RESERVED/2) tophalfchar= "!";
 
   if (chdir(CONFIGDIR)) perrorfail("chdir " CONFIGDIR);
 
   fnbuf[sizeof(fnbuf)-1]= 0;
-  memset(&saddr4,0,sizeof(saddr4));
-  saddr4.sin_family= AF_INET;
-  saddr4.sin_port= port;
-  saddr4.sin_addr.s_addr= addr;
 
-  snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%u",hport);
+  switch (af) {
+  case AF_INET:
+    saddr4.sin_family= af;
+    saddr4.sin_port= port;
+    saddr4.sin_addr.s_addr= addr4;
+    break;
+  case AF_INET6:
+    saddr6.sin6_family= af;
+    saddr6.sin6_port= port;
+    break;
+  default:
+    abort();
+  }
+
+  snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%s%u",tophalfchar,hport);
   if (!access(fnbuf,X_OK)) authorised();
   if (errno != ENOENT) exiterrno(errno);
 
-  np= inet_ntoa(saddr4.sin_addr); assert(np);
-  snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s:%u",np,hport);
+  char npbuf[INET_ADDRSTRLEN + INET6_ADDRSTRLEN];
+  np= inet_ntop(af,addr_any,npbuf,addrlen_any);
+  assert(np);
+
+  snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s:%s%u",np,tophalfchar,hport);
   if (!access(fnbuf,X_OK)) authorised();
   if (errno != ENOENT) exiterrno(errno);
 
   uid= getuid(); if (uid==(uid_t)-1) perrorfail("getuid");
-  snprintf(fnbuf,sizeof(fnbuf)-1,"byuid/%lu",(unsigned long)uid);
+  snprintf(fnbuf,sizeof(fnbuf)-1,"byuid/%s%lu",tophalfchar,(unsigned long)uid);
 
   file= fopen(fnbuf,"r");
   if (!file) exiterrno(errno==ENOENT ? EPERM : errno);
 
-  haddr= ntohl(addr);
-
   while (fgets(fnbuf,sizeof(fnbuf)-1,file)) {
     nchar= -1;
-    sscanf(fnbuf," %u.%u.%u.%u/%u:%u,%u %n",
-          &a1,&a2,&a3,&a4,&alen,&pmin,&pmax,&nchar);
-    if (nchar != strlen(fnbuf) ||
-       alen>32 || pmin&~0x0ffff || pmax&~0x0ffff ||
-       a1&~0x0ff || a2&~0xff || a3&~0x0ff || a4&~0x0ff)
+
+    char *colon = strchr(fnbuf,'/');
+    if (!colon) continue;
+    *colon++ = '\0';
+
+    sscanf(fnbuf," %u.%u.%u.%u/%u%n",
+          &a1,&a2,&a3,&a4,&alen,&nchar);
+    if (nchar == strlen(fnbuf)) {
+      if (alen>32 || pmin&~0x0ffff || pmax&~0x0ffff ||
+         a1&~0x0ff || a2&~0xff || a3&~0x0ff || a4&~0x0ff)
+       continue;
+
+      unsigned long thaddr, thmask;
+      thaddr= (a1<<24)|(a2<<16)|(a3<<8)|(a4);
+      thmask= 0x0ffffffffUL<<(32-alen);
+      if ((haddr4&thmask) != thaddr) continue;
+    } else {
+      char *hyphen = strchr(fnbuf,'-');
+      const char *min, *max;
+      if (hyphen) {
+       *hyphen++ = '\0';
+       min = fnbuf;
+       max = hyphen;
+      } else {
+       min = fnbuf;
+       max = fnbuf;
+      }
+      unsigned char minaddr[addrlen_any];
+      unsigned char maxaddr[addrlen_any];
+      if (inet_pton(af,min,minaddr) != 1 ||
+         inet_pton(af,max,maxaddr) != 1)
+        continue;
+      if (memcmp(addr_any,minaddr,addrlen_any) < 0 ||
+         memcmp(addr_any,maxaddr,addrlen_any) > 0)
+        continue;
+    }
+
+    sscanf(colon," %u,%u %n",
+          &pmin,&pmax,&nchar);
+    if (nchar != strlen(colon))
       continue;
     
     if (hport<pmin || hport>pmax) continue;
 
-    thaddr= (a1<<24)|(a2<<16)|(a3<<8)|(a4);
-    thmask= 0x0ffffffffUL<<(32-alen);
-    if ((haddr&thmask) != thaddr) continue;
     authorised();
   }
   if (ferror(file)) perrorfail("read per-uid file");