chiark / gitweb /
do not shift by 32
[authbind.git] / helper.c
1 /*
2  *  helper.c - setuid helper program for authbind
3  *
4  *  authbind is Copyright (C) 1998 Ian Jackson
5  * 
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  * 
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  * 
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software Foundation,
18  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
19  * 
20  */
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32
33 #ifndef CONFIGDIR
34 # define CONFIGDIR "/etc/authbind"
35 #endif
36
37 static void exiterrno(int e) {
38   exit(e>0 && e<128 ? e : ENOSYS);
39 }
40
41 static void perrorfail(const char *m) {
42   int e;
43   e= errno;
44   fprintf(stderr,"libauthbind's helper: %s: %s\n",m,strerror(e));
45   exiterrno(e);
46 }
47
48 static void badusage(void) {
49   fprintf(stderr,"libauthbind's helper: bad usage\n");
50   exit(ENOSYS);
51 }
52
53 static struct sockaddr_in saddr4;
54 static struct sockaddr_in6 saddr6;
55
56 static struct sockaddr *saddr_any;
57 static const void *addr_any;
58 static size_t saddrlen_any, addrlen_any;
59
60 static void authorised(void) {
61   if (bind(0,saddr_any,saddrlen_any)) exiterrno(errno);
62   else _exit(0);
63 }
64
65 static void checkexecflagfile(const char *file) {
66   if (!access(file,X_OK)) authorised();
67   if (errno != ENOENT) exiterrno(errno);
68 }
69
70 static void hex2bytes(const char *string, unsigned char *out, int len) {
71   int i;
72   for (i=0; i<len; i++) {
73     char hex[3], *ep;
74     hex[0]= *string++;  if (!hex[0]) badusage();
75     hex[1]= *string++;  if (!hex[1]) badusage();
76     hex[2]= 0;
77     *out++ = strtoul(hex,&ep,16);
78     if (ep != &hex[2]) badusage();
79   }
80 }
81
82 int main(int argc, const char *const *argv) {
83   uid_t uid;
84   char fnbuf[300];
85   char *ep;
86   const char *np;
87   const char *tophalfchar="";
88   unsigned long port, addr4=0, haddr4=0;
89   unsigned int hport;
90   int af;
91   FILE *file;
92
93   if (argc == 3) {
94     af= AF_INET;
95     saddr_any= (void*)&saddr4;
96     saddrlen_any= sizeof(saddr4);
97     addr_any= &saddr4.sin_addr.s_addr;
98     addrlen_any= sizeof(saddr4.sin_addr.s_addr);
99     addr4= strtoul(argv[1],&ep,16);
100     if (*ep || addr4&~0x0ffffffffUL) badusage();
101     haddr4= ntohl(addr4);
102   } else if (argc == 4 && !strcmp(argv[3],"6")) {
103     af= AF_INET6;
104     saddr_any= (void*)&saddr6;
105     saddrlen_any= sizeof(saddr6);
106     addr_any= &saddr6.sin6_addr.s6_addr;
107     addrlen_any= sizeof(saddr6.sin6_addr.s6_addr);
108     hex2bytes(argv[1], saddr6.sin6_addr.s6_addr,
109               sizeof(saddr6.sin6_addr.s6_addr));
110   } else {
111     badusage();
112     abort();
113   }
114
115   port= strtoul(argv[2],&ep,16); if (*ep || port&~0x0ffffUL) badusage();
116   hport= htons(port);
117   if (hport >= IPPORT_RESERVED/2) tophalfchar= "!";
118
119   if (chdir(CONFIGDIR)) perrorfail("chdir " CONFIGDIR);
120
121   fnbuf[sizeof(fnbuf)-1]= 0;
122
123   switch (af) {
124   case AF_INET:
125     saddr4.sin_family= af;
126     saddr4.sin_port= port;
127     saddr4.sin_addr.s_addr= addr4;
128     break;
129   case AF_INET6:
130     saddr6.sin6_family= af;
131     saddr6.sin6_port= port;
132     break;
133   default:
134     abort();
135   }
136
137   snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%s%u",tophalfchar,hport);
138   if (!access(fnbuf,X_OK)) authorised();
139   if (errno != ENOENT) exiterrno(errno);
140
141   char npbuf[INET_ADDRSTRLEN + INET6_ADDRSTRLEN];
142   np= inet_ntop(af,addr_any,npbuf,sizeof(npbuf));
143   assert(np);
144
145   if (af == AF_INET) {
146     snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s:%u",tophalfchar,np,hport);
147     checkexecflagfile(fnbuf);
148   }
149
150   snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s,%u",tophalfchar,np,hport);
151   checkexecflagfile(fnbuf);
152
153   if (af == AF_INET6) {
154     char sbuf[addrlen_any*3+1], *sp = sbuf;
155     const unsigned char *ip = addr_any;
156     int i;
157     for (i=0; i<8; i++) {
158       unsigned val = 0;
159       val |= *ip++;  val <<= 8;
160       val |= *ip++;
161       if (i) *sp++ = ':';
162       sp += sprintf(sp,"%x",val);
163     }
164     snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s,%u",tophalfchar,sbuf,hport);
165     checkexecflagfile(fnbuf);
166   }
167
168   uid= getuid(); if (uid==(uid_t)-1) perrorfail("getuid");
169   snprintf(fnbuf,sizeof(fnbuf)-1,"byuid/%s%lu",tophalfchar,(unsigned long)uid);
170
171   file= fopen(fnbuf,"r");
172   if (!file) exiterrno(errno==ENOENT ? EPERM : errno);
173
174   while (fgets(fnbuf,sizeof(fnbuf)-1,file)) {
175     unsigned int a1,a2,a3,a4, alen,pmin,pmax;
176     int nchar;
177
178     if (af == AF_INET &&
179         (nchar = -1,
180          sscanf(fnbuf," %u.%u.%u.%u/%u: %u,%u %n",
181                 &a1,&a2,&a3,&a4,&alen,&pmin,&pmax,&nchar),
182          nchar == strlen(fnbuf))) {
183
184       if (alen>32 || pmin&~0x0ffff || pmax&~0x0ffff ||
185           a1&~0x0ff || a2&~0xff || a3&~0x0ff || a4&~0x0ff)
186         continue;
187
188       unsigned long thaddr, thmask;
189       thaddr= (a1<<24)|(a2<<16)|(a3<<8)|(a4);
190       thmask= alen ? 0x0ffffffffUL<<(32-alen) : 0;
191       if ((haddr4&thmask) != thaddr) continue;
192
193     } else {
194
195       char *comma = strchr(fnbuf,',');
196       if (!comma) continue;
197       *comma++ = '\0';
198
199       char *hyphen = strchr(fnbuf,'-');
200       const char *min, *max;
201       if (hyphen) {
202         *hyphen++ = '\0';
203         min = fnbuf;
204         max = hyphen;
205       } else {
206         min = fnbuf;
207         max = fnbuf;
208       }
209       unsigned char minaddr[addrlen_any];
210       unsigned char maxaddr[addrlen_any];
211       if (inet_pton(af,min,minaddr) != 1 ||
212           inet_pton(af,max,maxaddr) != 1)
213         continue;
214       if (memcmp(addr_any,minaddr,addrlen_any) < 0 ||
215           memcmp(addr_any,maxaddr,addrlen_any) > 0)
216         continue;
217
218       nchar = -1;
219       sscanf(comma," %u-%u %n",
220              &pmin,&pmax,&nchar);
221       if (nchar != strlen(comma))
222         continue;
223
224     }
225     if (hport<pmin || hport>pmax) continue;
226
227     authorised();
228   }
229   if (ferror(file)) perrorfail("read per-uid file");
230   _exit(ENOENT);
231 }