9c832ed4a4ed15c3bb145e45aeecedaaa229af47
[asterisk/asterisk.git] / acl.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Various sorts of access control
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <pthread.h>
17 #include <string.h>
18 #include <sys/time.h>
19 #include <signal.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <asterisk/acl.h>
23 #include <asterisk/logger.h>
24 #include <arpa/inet.h>
25 #include <sys/socket.h>
26 #include <netdb.h>
27 #include <net/if.h>
28 #include <netinet/in_systm.h>
29 #include <netinet/ip.h>
30 #include <sys/ioctl.h>
31
32 #define AST_SENSE_DENY                  0
33 #define AST_SENSE_ALLOW                 1
34
35 struct ast_ha {
36         /* Host access rule */
37         struct in_addr netaddr;
38         struct in_addr netmask;
39         int sense;
40         struct ast_ha *next;
41 };
42
43 /* Default IP - if not otherwise set, don't breathe garbage */
44 static struct in_addr __ourip = { 0x00000000 };
45
46 struct my_ifreq {
47         char ifrn_name[IFNAMSIZ];       /* Interface name, e.g. "eth0", "ppp0", etc.  */
48         struct sockaddr_in ifru_addr;
49 };
50
51 void ast_free_ha(struct ast_ha *ha)
52 {
53         struct ast_ha *hal;
54         while(ha) {
55                 hal = ha;
56                 ha = ha->next;
57                 free(hal);
58         }
59 }
60
61 struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
62 {
63         struct ast_ha *ha = malloc(sizeof(struct ast_ha));
64         char *nm;
65         struct ast_ha *prev = NULL;
66         struct ast_ha *ret;
67         ret = path;
68         while(path) {
69                 prev = path;
70                 path = path->next;
71         }
72         if (ha) {
73                 char *stringp=NULL;
74                 stringp=stuff;
75                 strsep(&stringp, "/");
76                 nm = strsep(&stringp, "/");
77                 if (!nm)
78                         nm = "255.255.255.255";
79                 if (!inet_aton(stuff, &ha->netaddr)) {
80                         ast_log(LOG_WARNING, "%s not a valid IP\n", stuff);
81                         free(ha);
82                         return NULL;
83                 }
84                 if (!inet_aton(nm, &ha->netmask)) {
85                         ast_log(LOG_WARNING, "%s not a valid netmask\n", nm);
86                         free(ha);
87                         return NULL;
88                 }
89                 ha->netaddr.s_addr &= ha->netmask.s_addr;
90                 if (!strncasecmp(sense, "p", 1)) {
91                         ha->sense = AST_SENSE_ALLOW;
92                 } else {
93                         ha->sense = AST_SENSE_DENY;
94                 }
95                 ha->next = NULL;
96                 if (prev)
97                         prev->next = ha;
98                 else
99                         ret = ha;
100         }
101         return ret;
102 }
103
104 int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
105 {
106         /* Start optimistic */
107         int res = AST_SENSE_ALLOW;
108         while(ha) {
109                 /* For each rule, if this address and the netmask = the net address
110                    apply the current rule */
111                 if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
112                         res = ha->sense;
113                 ha = ha->next;
114         }
115         return res;
116 }
117
118 int ast_get_ip(struct sockaddr_in *sin, char *value)
119 {
120         struct hostent *hp;
121         hp = gethostbyname(value);
122         if (hp) {
123                 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
124         } else {
125                 ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
126                 return -1;
127         }
128         return 0;
129 }
130
131 int inaddrcmp(struct sockaddr_in *sin1, struct sockaddr_in *sin2)
132 {
133         return ((sin1->sin_addr.s_addr != sin2->sin_addr.s_addr )
134                         || (sin1->sin_port != sin2->sin_port));
135 }
136
137 /* iface is the interface (e.g. eth0); address is the return value */
138 int ast_lookup_iface(char *iface, struct in_addr *address) {
139         int mysock, res = 0;
140         struct my_ifreq ifreq;
141
142         memset(&ifreq, 0, sizeof(ifreq));
143         strncpy(ifreq.ifrn_name,iface,sizeof(ifreq.ifrn_name) - 1);
144
145         mysock = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
146         res = ioctl(mysock,SIOCGIFADDR,&ifreq);
147
148         close(mysock);
149         if (res < 0) {
150                 ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
151                 memcpy((char *)address,(char *)&__ourip,sizeof(__ourip));
152                 return -1;
153         } else {
154                 memcpy((char *)address,(char *)&ifreq.ifru_addr.sin_addr,sizeof(ifreq.ifru_addr.sin_addr));
155                 return 0;
156         }
157 }
158
159 int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
160 {
161         FILE *PROC;
162         unsigned int remote_ip;
163         int res = 1;
164         char line[256];
165         remote_ip = them->s_addr;
166         
167         PROC = fopen("/proc/net/route","r");
168         if (!PROC) {
169                 bzero(us,sizeof(struct in_addr));
170                 return -1;
171         }
172         /* First line contains headers */
173         fgets(line,sizeof(line),PROC);
174
175         while (!feof(PROC)) {
176                 char iface[8];
177                 unsigned int dest, gateway, mask;
178                 int i,fieldnum;
179                 char *fields[40];
180
181                 fgets(line,sizeof(line),PROC);
182
183                 fieldnum = 0;
184                 for (i=0;i<sizeof(line);i++) {
185                         char *offset;
186
187                         fields[fieldnum++] = line + i;
188                         offset = strchr(line + i,'\t');
189                         if (offset == NULL) {
190                                 /* Exit loop */
191                                 break;
192                         } else if (fieldnum >= 9) {
193                                 /* Short-circuit: can't break at 8, since the end of field 7 is figured when fieldnum=8 */
194                                 break;
195                         } else {
196                                 *offset = '\0';
197                                 i = offset - line;
198                         }
199                 }
200
201                 sscanf(fields[0],"%s",iface);
202                 sscanf(fields[1],"%x",&dest);
203                 sscanf(fields[2],"%x",&gateway);
204                 sscanf(fields[7],"%x",&mask);
205 #if 0
206                 printf("Addr: %s %08x Dest: %08x Mask: %08x\n", inet_ntoa(*them), remote_ip, dest, mask);
207 #endif          
208                 /* Looks simple, but here is the magic */
209                 if (((remote_ip & mask) ^ dest) == 0) {
210                         res = ast_lookup_iface(iface,us);
211                         break;
212                 }
213         }
214         fclose(PROC);
215         if (res == 1) {
216                 ast_log(LOG_WARNING, "Yikes!  No default route?!!\n");
217                 bzero(us,sizeof(struct in_addr));
218                 return -2;
219         } else if (res) {
220                 /* We've already warned in subroutine */
221                 return -1;
222         }
223         return 0;
224 }
225
226