2 * helper.c - setuid helper program for authbind
4 * authbind is Copyright (C) 1998 Ian Jackson
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)
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.
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.
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
34 # define CONFIGDIR "/etc/authbind"
37 static void exiterrno(int e) {
38 exit(e>0 && e<128 ? e : ENOSYS);
41 static void perrorfail(const char *m) {
44 fprintf(stderr,"libauthbind's helper: %s: %s\n",m,strerror(e));
48 static void badusage(void) {
49 fprintf(stderr,"libauthbind's helper: bad usage\n");
53 static struct sockaddr_in saddr4;
54 static struct sockaddr_in6 saddr6;
56 static struct sockaddr *saddr_any;
57 static const void *addr_any;
58 static size_t saddrlen_any, addrlen_any;
60 static void authorised(void) {
61 if (bind(0,saddr_any,saddrlen_any)) exiterrno(errno);
65 static void checkexecflagfile(const char *file) {
66 if (!access(file,X_OK)) authorised();
67 if (errno != ENOENT) exiterrno(errno);
70 static void hex2bytes(const char *string, unsigned char *out, int len) {
72 for (i=0; i<len; i++) {
74 hex[0]= *string++; if (!hex[0]) badusage();
75 hex[1]= *string++; if (!hex[1]) badusage();
77 *out++ = strtoul(hex,&ep,16);
78 if (ep != &hex[2]) badusage();
82 int main(int argc, const char *const *argv) {
87 const char *tophalfchar="";
88 unsigned long port, addr4=0, haddr4=0;
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")) {
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));
115 port= strtoul(argv[2],&ep,16); if (*ep || port&~0x0ffffUL) badusage();
117 if (hport >= IPPORT_RESERVED/2) tophalfchar= "!";
119 if (chdir(CONFIGDIR)) perrorfail("chdir " CONFIGDIR);
121 fnbuf[sizeof(fnbuf)-1]= 0;
125 saddr4.sin_family= af;
126 saddr4.sin_port= port;
127 saddr4.sin_addr.s_addr= addr4;
130 saddr6.sin6_family= af;
131 saddr6.sin6_port= port;
137 snprintf(fnbuf,sizeof(fnbuf)-1,"byport/%s%u",tophalfchar,hport);
138 if (!access(fnbuf,X_OK)) authorised();
139 if (errno != ENOENT) exiterrno(errno);
141 char npbuf[INET_ADDRSTRLEN + INET6_ADDRSTRLEN];
142 np= inet_ntop(af,addr_any,npbuf,sizeof(npbuf));
146 snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s:%u",tophalfchar,np,hport);
147 checkexecflagfile(fnbuf);
150 snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s,%u",tophalfchar,np,hport);
151 checkexecflagfile(fnbuf);
153 if (af == AF_INET6) {
154 char sbuf[addrlen_any*3+1], *sp = sbuf;
155 const unsigned char *ip = addr_any;
157 for (i=0; i<8; i++) {
159 val |= *ip++; val <<= 8;
162 sp += sprintf(sp,"%x",val);
164 snprintf(fnbuf,sizeof(fnbuf)-1,"byaddr/%s%s,%u",tophalfchar,sbuf,hport);
165 checkexecflagfile(fnbuf);
168 uid= getuid(); if (uid==(uid_t)-1) perrorfail("getuid");
169 snprintf(fnbuf,sizeof(fnbuf)-1,"byuid/%s%lu",tophalfchar,(unsigned long)uid);
171 file= fopen(fnbuf,"r");
172 if (!file) exiterrno(errno==ENOENT ? EPERM : errno);
174 while (fgets(fnbuf,sizeof(fnbuf)-1,file)) {
175 unsigned int a1,a2,a3,a4, alen,pmin,pmax;
180 sscanf(fnbuf," %u.%u.%u.%u/%u: %u,%u %n",
181 &a1,&a2,&a3,&a4,&alen,&pmin,&pmax,&nchar),
182 nchar == strlen(fnbuf))) {
184 if (alen>32 || pmin&~0x0ffff || pmax&~0x0ffff ||
185 a1&~0x0ff || a2&~0xff || a3&~0x0ff || a4&~0x0ff)
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;
195 char *comma = strchr(fnbuf,',');
196 if (!comma) continue;
199 char *slash = strchr(fnbuf,'/');
200 char *hyphen = strchr(fnbuf,'-');
209 sscanf(slash," %u %n",&alen,&nchar);
210 if (nchar != strlen(slash))
212 unsigned char thaddr[addrlen_any];
213 if (inet_pton(af,fnbuf,thaddr) != 1)
215 int pfxlen_remain = alen;
217 for (i=0; i<addrlen_any; i++) {
218 int pfxlen_thisbyte = pfxlen_remain < 8 ? pfxlen_remain : 8;
219 pfxlen_remain -= pfxlen_thisbyte;
220 unsigned mask_thisbyte = 0xff ^ (0xff >> pfxlen_thisbyte);
221 unsigned thaddr_thisbyte = thaddr[i];
222 unsigned addr_thisbyte = ((unsigned char*)addr_any)[i];
223 if ((addr_thisbyte & mask_thisbyte) != thaddr_thisbyte)
226 if (pfxlen_remain) badline: continue;
229 const char *min, *max;
238 unsigned char minaddr[addrlen_any];
239 unsigned char maxaddr[addrlen_any];
240 if (inet_pton(af,min,minaddr) != 1 ||
241 inet_pton(af,max,maxaddr) != 1)
243 if (memcmp(addr_any,minaddr,addrlen_any) < 0 ||
244 memcmp(addr_any,maxaddr,addrlen_any) > 0)
249 sscanf(comma," %u-%u %n",
251 nchar == strlen(comma)) {
253 } else if (nchar = -1,
254 sscanf(comma," %u %n",
256 nchar == strlen(comma)) {
263 if (hport<pmin || hport>pmax) continue;
267 if (ferror(file)) perrorfail("read per-uid file");