chiark / gitweb /
e086e9e23808f9fa6c84d6d5ff822230e3ebf78e
[authbind.git] / helper.c
1 /*
2  * setuid.  Invoked with socket on stdin.
3  *  Usage:  helper <addr> <port>
4  * both are hex strings, padded to the right length.
5  * they are pairs of hex digits for each byte (network byte order)
6  *
7  * If /etc/authbind cannot be chdir'd into, is an error.
8  *
9  * First, check /etc/authbind/byport/<port> with access(2,X_OK).
10  *  If OK, then authorised.
11  *  If ENOENT then keep looking.
12  *  Otherwise, not authorised, errno=whatever
13  *
14  * Then check /etc/authbind/byboth/<addr>:<port> likewise.
15  *
16  * Then try to read /etc/authbind/byuid/<uid> (with superuser privs!)
17  *  If ENOENT, then not authorised, errno=EPERM
18  *  If cannot open, then not authorised, errno=whatever
19  *  If it contains a line of the form
20  *     <addr>/<length>:<port-min>,<port-max>
21  *    then authorised, otherwise not authorised, errno=ENOENT
22  *  If read error then is an error
23  *
24  * In each case,
25  *  <addr> is dotted quad
26  *  <port> is decimal in host order
27  *  <length> is prefix length (so 0.0.0.0/32 matches any)
28  *  <uid> is decimal unsigned
29  */
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41
42 #define CONFIGDIR "/etc/authbind"
43
44 static void exiterrno(int e) {
45   exit(e>0 && e<128 ? e : ENOSYS);
46 }
47
48 static void perrorfail(const char *m) {
49   int e;
50   e= errno;
51   fprintf(stderr,"libauthbind's helper: %s: %s\n",m,strerror(e));
52   exiterrno(e);
53 }
54
55 static void badusage(void) {
56   fputs("libauthbind's helper: bad usage\n",stderr);
57   exit(ENOSYS);
58 }
59
60 static struct sockaddr_in saddr;
61
62 static void authorised(void) {
63   if (bind(0,&saddr,sizeof(saddr))) exiterrno(errno);
64   else _exit(0);
65 }
66
67 int main(int argc, const char *const *argv) {
68   uid_t uid;
69   char fnbuf[100];
70   char *ep;
71   const char *np;
72   unsigned long addr, port, haddr, thaddr, thmask;
73   unsigned int hport, a1,a2,a3,a4, alen,pmin,pmax;
74   int nchar;
75   FILE *file;
76
77   if (argc != 3) badusage(); 
78   addr= strtoul(argv[1],&ep,16); if (*ep || addr&~0x0ffffffffUL) badusage();
79   port= strtoul(argv[2],&ep,16); if (*ep || port&~0x0ffffUL) badusage();
80
81   if (chdir(CONFIGDIR)) perrorfail("chdir " CONFIGDIR);
82
83   fnbuf[sizeof(fnbuf)-1]= 0;
84   memset(&saddr,0,sizeof(saddr));
85   saddr.sin_family= AF_INET;
86   saddr.sin_port= port;
87   saddr.sin_addr.s_addr= addr;
88   hport= htons(port);
89
90   snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%u",hport);
91   if (!access(fnbuf,X_OK)) authorised();
92   if (errno != ENOENT) exiterrno(errno);
93
94   np= inet_ntoa(saddr.sin_addr); assert(np);
95   snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s:%u",np,hport);
96   if (!access(fnbuf,X_OK)) authorised();
97   if (errno != ENOENT) exiterrno(errno);
98
99   uid= getuid(); if (uid==(uid_t)-1) perrorfail("getuid");
100   snprintf(fnbuf,sizeof(fnbuf)-1,"byuid/%lu",(unsigned long)uid);
101
102   file= fopen(fnbuf,"r");
103   if (!file) exiterrno(errno==ENOENT ? EPERM : errno);
104
105   haddr= ntohl(addr);
106
107   while (fgets(fnbuf,sizeof(fnbuf)-1,file)) {
108     nchar= -1;
109     sscanf(fnbuf," %u.%u.%u.%u/%u:%u,%u %n",
110            &a1,&a2,&a3,&a4,&alen,&pmin,&pmax,&nchar);
111     if (nchar != strlen(fnbuf) ||
112         alen>32 || pmin&~0x0ffff || pmax&~0x0ffff ||
113         a1&~0x0ff || a2&~0xff || a3&~0x0ff || a4&~0x0ff)
114       continue;
115     
116     if (hport<pmin || hport>pmax) continue;
117
118     thaddr= (a1<<24)|(a2<<16)|(a3<<8)|(a4);
119     thmask= 0x0ffffffffUL<<(32-alen);
120     if ((haddr&thmask) != thaddr) continue;
121     authorised();
122   }
123   if (ferror(file)) perrorfail("read per-uid file");
124   _exit(ENOENT);
125 }