chiark / gitweb /
restore ignores which were deleted by mistake by NMUer's tools
[authbind.git] / helper.c
index e086e9e23808f9fa6c84d6d5ff822230e3ebf78e..a58e7aae666f30899a9673f2efbaf7b070b8bdd2 100644 (file)
--- a/helper.c
+++ b/helper.c
@@ -1,31 +1,22 @@
 /*
- * setuid.  Invoked with socket on stdin.
- *  Usage:  helper <addr> <port>
- * both are hex strings, padded to the right length.
- * they are pairs of hex digits for each byte (network byte order)
+ *  helper.c - setuid helper program for authbind
  *
- * If /etc/authbind cannot be chdir'd into, is an error.
- *
- * First, check /etc/authbind/byport/<port> with access(2,X_OK).
- *  If OK, then authorised.
- *  If ENOENT then keep looking.
- *  Otherwise, not authorised, errno=whatever
- *
- * Then check /etc/authbind/byboth/<addr>:<port> likewise.
- *
- * Then try to read /etc/authbind/byuid/<uid> (with superuser privs!)
- *  If ENOENT, then not authorised, errno=EPERM
- *  If cannot open, then not authorised, errno=whatever
- *  If it contains a line of the form
- *     <addr>/<length>:<port-min>,<port-max>
- *    then authorised, otherwise not authorised, errno=ENOENT
- *  If read error then is an error
- *
- * In each case,
- *  <addr> is dotted quad
- *  <port> is decimal in host order
- *  <length> is prefix length (so 0.0.0.0/32 matches any)
- *  <uid> is decimal unsigned
+ *  authbind is Copyright (C) 1998 Ian Jackson
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ * 
+ *  This program 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 General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+ * 
  */
 
 #include <errno.h>
@@ -39,7 +30,9 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#define CONFIGDIR "/etc/authbind"
+#ifndef CONFIGDIR
+# define CONFIGDIR "/etc/authbind"
+#endif
 
 static void exiterrno(int e) {
   exit(e>0 && e<128 ? e : ENOSYS);
@@ -53,71 +46,222 @@ static void perrorfail(const char *m) {
 }
 
 static void badusage(void) {
-  fputs("libauthbind's helper: bad usage\n",stderr);
+  fprintf(stderr,"libauthbind's helper: bad usage\n");
   exit(ENOSYS);
 }
 
-static struct sockaddr_in saddr;
+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,&saddr,sizeof(saddr))) exiterrno(errno);
+  if (bind(0,saddr_any,saddrlen_any)) exiterrno(errno);
   else _exit(0);
 }
 
+static void checkexecflagfile(const char *file) {
+  if (!access(file,X_OK)) authorised();
+  if (errno != ENOENT) exiterrno(errno);
+}
+
+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;
-  unsigned int hport, a1,a2,a3,a4, alen,pmin,pmax;
-  int nchar;
+  const char *tophalfchar="";
+  unsigned long port, addr4=0, haddr4=0;
+  unsigned int hport;
+  int 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) tophalfchar= "!";
 
   if (chdir(CONFIGDIR)) perrorfail("chdir " CONFIGDIR);
 
   fnbuf[sizeof(fnbuf)-1]= 0;
-  memset(&saddr,0,sizeof(saddr));
-  saddr.sin_family= AF_INET;
-  saddr.sin_port= port;
-  saddr.sin_addr.s_addr= addr;
-  hport= htons(port);
 
-  snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%u",hport);
-  if (!access(fnbuf,X_OK)) authorised();
-  if (errno != ENOENT) exiterrno(errno);
+  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();
+  }
 
-  np= inet_ntoa(saddr.sin_addr); assert(np);
-  snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s:%u",np,hport);
+  snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%s%u",tophalfchar,hport);
   if (!access(fnbuf,X_OK)) authorised();
   if (errno != ENOENT) exiterrno(errno);
 
+  char npbuf[INET_ADDRSTRLEN + INET6_ADDRSTRLEN];
+  np= inet_ntop(af,addr_any,npbuf,sizeof(npbuf));
+  assert(np);
+
+  if (af == AF_INET) {
+    snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s:%u",tophalfchar,np,hport);
+    checkexecflagfile(fnbuf);
+  }
+
+  snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s,%u",tophalfchar,np,hport);
+  checkexecflagfile(fnbuf);
+
+  if (af == AF_INET6) {
+    char sbuf[addrlen_any*3+1], *sp = sbuf;
+    const unsigned char *ip = addr_any;
+    int i;
+    for (i=0; i<8; i++) {
+      unsigned val = 0;
+      val |= *ip++;  val <<= 8;
+      val |= *ip++;
+      if (i) *sp++ = ':';
+      sp += sprintf(sp,"%x",val);
+    }
+    snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s,%u",tophalfchar,sbuf,hport);
+    checkexecflagfile(fnbuf);
+  }
+
   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)
-      continue;
-    
+    unsigned int a1,a2,a3,a4, alen,pmin,pmax;
+    int nchar;
+
+    if (af == AF_INET &&
+       (nchar = -1,
+        sscanf(fnbuf," %u.%u.%u.%u/%u: %u,%u %n",
+               &a1,&a2,&a3,&a4,&alen,&pmin,&pmax,&nchar),
+        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= alen ? 0x0ffffffffUL<<(32-alen) : 0;
+      if ((haddr4&thmask) != thaddr) continue;
+
+    } else {
+
+      char *comma = strchr(fnbuf,',');
+      if (!comma) continue;
+      *comma++ = '\0';
+
+      char *slash = strchr(fnbuf,'/');
+      char *hyphen = strchr(fnbuf,'-');
+
+      if (slash && hyphen)
+       continue;
+
+      if (slash) {
+       int alen;
+       *slash++ = '\0';
+       nchar = -1;
+       sscanf(slash," %u %n",&alen,&nchar);
+       if (nchar != strlen(slash))
+         continue;
+       unsigned char thaddr[addrlen_any];
+       if (inet_pton(af,fnbuf,thaddr) != 1)
+         continue;
+       int pfxlen_remain = alen;
+       int i;
+       for (i=0; i<addrlen_any; i++) {
+         int pfxlen_thisbyte = pfxlen_remain < 8 ? pfxlen_remain : 8;
+         pfxlen_remain -= pfxlen_thisbyte;
+         unsigned mask_thisbyte = 0xff ^ (0xff >> pfxlen_thisbyte);
+         unsigned thaddr_thisbyte = thaddr[i];
+         unsigned addr_thisbyte = ((unsigned char*)addr_any)[i];
+         if ((addr_thisbyte & mask_thisbyte) != thaddr_thisbyte)
+           goto badline;
+       }
+       if (pfxlen_remain) badline: continue;
+       /* hooray */
+      } else {
+       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;
+      }
+
+      if (nchar = -1,
+         sscanf(comma," %u-%u %n",
+                &pmin,&pmax,&nchar),
+         nchar == strlen(comma)) {
+       /* good */
+      } else if (nchar = -1,
+                sscanf(comma," %u %n",
+                       &pmin,&nchar),
+                nchar == strlen(comma)) {
+       pmax = pmin;
+      } else {
+       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");