chiark / gitweb /
wip v6 support etc.
[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 const char *rcsid="$Id$";
38
39 static void exiterrno(int e) {
40   exit(e>0 && e<128 ? e : ENOSYS);
41 }
42
43 static void perrorfail(const char *m) {
44   int e;
45   e= errno;
46   fprintf(stderr,"libauthbind's helper: %s: %s\n",m,strerror(e));
47   exiterrno(e);
48 }
49
50 static void badusage(void) {
51   fprintf(stderr,"libauthbind's helper: bad usage\n (%s)\n",rcsid);
52   exit(ENOSYS);
53 }
54
55 static struct sockaddr_in saddr4;
56 static struct sockaddr_in6 saddr6;
57
58 static struct sockaddr *saddr_any;
59 static const void *addr_any;
60 static size_t saddrlen_any, addrlen_any;
61
62 static void authorised(void) {
63   if (bind(0,saddr_any,saddrlen_any)) exiterrno(errno);
64   else _exit(0);
65 }
66
67 static void hex2bytes(const char *string, unsigned char *out, int len) {
68   int i;
69   for (i=0; i<len; i++) {
70     char hex[3], *ep;
71     hex[0]= *string++;  if (!hex[0]) badusage();
72     hex[1]= *string++;  if (!hex[1]) badusage();
73     hex[2]= 0;
74     *out++ = strtoul(hex,&ep,16);
75     if (ep != &hex[2]) badusage();
76   }
77 }
78
79 int main(int argc, const char *const *argv) {
80   uid_t uid;
81   char fnbuf[300];
82   char *ep;
83   const char *np;
84   const char *tophalfchar="";
85   unsigned long port, addr4=0, haddr4=0;
86   unsigned int hport, a1,a2,a3,a4, alen,pmin,pmax;
87   int nchar, af;
88   FILE *file;
89
90   if (argc == 3) {
91     af= AF_INET;
92     saddr_any= (void*)&saddr4;
93     saddrlen_any= sizeof(saddr4);
94     addr_any= &saddr4.sin_addr.s_addr;
95     addrlen_any= sizeof(saddr4.sin_addr.s_addr);
96     addr4= strtoul(argv[1],&ep,16);
97     if (*ep || addr4&~0x0ffffffffUL) badusage();
98     haddr4= ntohl(addr4);
99   } else if (argc == 4 && !strcmp(argv[3],"6")) {
100     af= AF_INET6;
101     saddr_any= (void*)&saddr6;
102     saddrlen_any= sizeof(saddr6);
103     addr_any= &saddr6.sin6_addr.s6_addr;
104     addrlen_any= sizeof(saddr6.sin6_addr.s6_addr);
105     hex2bytes(argv[1], saddr6.sin6_addr.s6_addr,
106               sizeof(saddr6.sin6_addr.s6_addr));
107   } else {
108     badusage();
109     abort();
110   }
111
112   port= strtoul(argv[2],&ep,16); if (*ep || port&~0x0ffffUL) badusage();
113   hport= htons(port);
114   if (hport >= IPPORT_RESERVED/2) tophalfchar= "!";
115
116   if (chdir(CONFIGDIR)) perrorfail("chdir " CONFIGDIR);
117
118   fnbuf[sizeof(fnbuf)-1]= 0;
119
120   switch (af) {
121   case AF_INET:
122     saddr4.sin_family= af;
123     saddr4.sin_port= port;
124     saddr4.sin_addr.s_addr= addr4;
125     break;
126   case AF_INET6:
127     saddr6.sin6_family= af;
128     saddr6.sin6_port= port;
129     break;
130   default:
131     abort();
132   }
133
134   snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%s%u",tophalfchar,hport);
135   if (!access(fnbuf,X_OK)) authorised();
136   if (errno != ENOENT) exiterrno(errno);
137
138   char npbuf[INET_ADDRSTRLEN + INET6_ADDRSTRLEN];
139   np= inet_ntop(af,addr_any,npbuf,addrlen_any);
140   assert(np);
141
142   snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s:%s%u",np,tophalfchar,hport);
143   if (!access(fnbuf,X_OK)) authorised();
144   if (errno != ENOENT) exiterrno(errno);
145
146   uid= getuid(); if (uid==(uid_t)-1) perrorfail("getuid");
147   snprintf(fnbuf,sizeof(fnbuf)-1,"byuid/%s%lu",tophalfchar,(unsigned long)uid);
148
149   file= fopen(fnbuf,"r");
150   if (!file) exiterrno(errno==ENOENT ? EPERM : errno);
151
152   while (fgets(fnbuf,sizeof(fnbuf)-1,file)) {
153     nchar= -1;
154
155     char *colon = strchr(fnbuf,'/');
156     if (!colon) continue;
157     *colon++ = '\0';
158
159     sscanf(fnbuf," %u.%u.%u.%u/%u%n",
160            &a1,&a2,&a3,&a4,&alen,&nchar);
161     if (nchar == strlen(fnbuf)) {
162       if (alen>32 || pmin&~0x0ffff || pmax&~0x0ffff ||
163           a1&~0x0ff || a2&~0xff || a3&~0x0ff || a4&~0x0ff)
164         continue;
165
166       unsigned long thaddr, thmask;
167       thaddr= (a1<<24)|(a2<<16)|(a3<<8)|(a4);
168       thmask= 0x0ffffffffUL<<(32-alen);
169       if ((haddr4&thmask) != thaddr) continue;
170     } else {
171       char *hyphen = strchr(fnbuf,'-');
172       const char *min, *max;
173       if (hyphen) {
174         *hyphen++ = '\0';
175         min = fnbuf;
176         max = hyphen;
177       } else {
178         min = fnbuf;
179         max = fnbuf;
180       }
181       unsigned char minaddr[addrlen_any];
182       unsigned char maxaddr[addrlen_any];
183       if (inet_pton(af,min,minaddr) != 1 ||
184           inet_pton(af,max,maxaddr) != 1)
185         continue;
186       if (memcmp(addr_any,minaddr,addrlen_any) < 0 ||
187           memcmp(addr_any,maxaddr,addrlen_any) > 0)
188         continue;
189     }
190
191     sscanf(colon," %u,%u %n",
192            &pmin,&pmax,&nchar);
193     if (nchar != strlen(colon))
194       continue;
195     
196     if (hport<pmin || hport>pmax) continue;
197
198     authorised();
199   }
200   if (ferror(file)) perrorfail("read per-uid file");
201   _exit(ENOENT);
202 }