067e36d09e32987592bec6da59cca34ad49799c0
[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 <asterisk/channel.h>
25 #include <arpa/inet.h>
26 #include <sys/socket.h>
27 #include <netdb.h>
28 #include <net/if.h>
29 #include <netinet/in_systm.h>
30 #include <netinet/ip.h>
31 #include <sys/ioctl.h>
32 #ifdef __OpenBSD__
33 #include <fcntl.h>
34 #include <net/route.h>
35
36 static ast_mutex_t routeseq_lock = AST_MUTEX_INITIALIZER;
37 #endif
38
39 #define AST_SENSE_DENY                  0
40 #define AST_SENSE_ALLOW                 1
41
42 struct ast_ha {
43         /* Host access rule */
44         struct in_addr netaddr;
45         struct in_addr netmask;
46         int sense;
47         struct ast_ha *next;
48 };
49
50 /* Default IP - if not otherwise set, don't breathe garbage */
51 static struct in_addr __ourip = { 0x00000000 };
52
53 struct my_ifreq {
54         char ifrn_name[IFNAMSIZ];       /* Interface name, e.g. "eth0", "ppp0", etc.  */
55         struct sockaddr_in ifru_addr;
56 };
57
58 void ast_free_ha(struct ast_ha *ha)
59 {
60         struct ast_ha *hal;
61         while(ha) {
62                 hal = ha;
63                 ha = ha->next;
64                 free(hal);
65         }
66 }
67
68 struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
69 {
70         struct ast_ha *ha = malloc(sizeof(struct ast_ha));
71         char *nm;
72         char tmp[256] = "";
73         struct ast_ha *prev = NULL;
74         struct ast_ha *ret;
75         ret = path;
76         while(path) {
77                 prev = path;
78                 path = path->next;
79         }
80         if (ha) {
81                 strncpy(tmp, stuff, sizeof(tmp) - 1);
82                 nm = strchr(tmp, '/');
83                 if (!nm)
84                         nm = "255.255.255.255";
85                 else {
86                         *nm = '\0';
87                         nm++;
88                 }
89                 if (!inet_aton(tmp, &ha->netaddr)) {
90                         ast_log(LOG_WARNING, "%s not a valid IP\n", tmp);
91                         free(ha);
92                         return path;
93                 }
94                 if (!inet_aton(nm, &ha->netmask)) {
95                         ast_log(LOG_WARNING, "%s not a valid netmask\n", nm);
96                         free(ha);
97                         return path;
98                 }
99                 ha->netaddr.s_addr &= ha->netmask.s_addr;
100                 if (!strncasecmp(sense, "p", 1)) {
101                         ha->sense = AST_SENSE_ALLOW;
102                 } else {
103                         ha->sense = AST_SENSE_DENY;
104                 }
105                 ha->next = NULL;
106                 if (prev)
107                         prev->next = ha;
108                 else
109                         ret = ha;
110         }
111         return ret;
112 }
113
114 int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
115 {
116         /* Start optimistic */
117         int res = AST_SENSE_ALLOW;
118         while(ha) {
119                 /* For each rule, if this address and the netmask = the net address
120                    apply the current rule */
121                 if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
122                         res = ha->sense;
123                 ha = ha->next;
124         }
125         return res;
126 }
127
128 int ast_get_ip(struct sockaddr_in *sin, char *value)
129 {
130         struct hostent *hp;
131         hp = gethostbyname(value);
132         if (hp) {
133                 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
134         } else {
135                 ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
136                 return -1;
137         }
138         return 0;
139 }
140
141 int inaddrcmp(struct sockaddr_in *sin1, struct sockaddr_in *sin2)
142 {
143         return ((sin1->sin_addr.s_addr != sin2->sin_addr.s_addr )
144                         || (sin1->sin_port != sin2->sin_port));
145 }
146
147 /* iface is the interface (e.g. eth0); address is the return value */
148 int ast_lookup_iface(char *iface, struct in_addr *address) {
149         int mysock, res = 0;
150         struct my_ifreq ifreq;
151
152         memset(&ifreq, 0, sizeof(ifreq));
153         strncpy(ifreq.ifrn_name,iface,sizeof(ifreq.ifrn_name) - 1);
154
155         mysock = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
156         res = ioctl(mysock,SIOCGIFADDR,&ifreq);
157
158         close(mysock);
159         if (res < 0) {
160                 ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
161                 memcpy((char *)address,(char *)&__ourip,sizeof(__ourip));
162                 return -1;
163         } else {
164                 memcpy((char *)address,(char *)&ifreq.ifru_addr.sin_addr,sizeof(ifreq.ifru_addr.sin_addr));
165                 return 0;
166         }
167 }
168
169 int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
170 {
171 #ifdef __OpenBSD__
172         struct sockaddr_in *sin;
173         struct sockaddr *sa;
174         struct {
175                 struct  rt_msghdr m_rtm;
176                 char    m_space[512];
177         } m_rtmsg;
178         char *cp, *p = ast_strdupa(inet_ntoa(*them));
179         int i, l, s, seq, flags;
180         pid_t pid = getpid();
181         static int routeseq;    /* Protected by "routeseq_lock" mutex */
182
183         memset(us, 0, sizeof(struct in_addr));
184
185         memset(&m_rtmsg, 0, sizeof(m_rtmsg));
186         m_rtmsg.m_rtm.rtm_type = RTM_GET;
187         m_rtmsg.m_rtm.rtm_flags = RTF_UP | RTF_HOST;
188         m_rtmsg.m_rtm.rtm_version = RTM_VERSION;
189         ast_mutex_lock(&routeseq_lock);
190         seq = ++routeseq;
191         ast_mutex_unlock(&routeseq_lock);
192         m_rtmsg.m_rtm.rtm_seq = seq;
193         m_rtmsg.m_rtm.rtm_addrs = RTA_IFA | RTA_DST;
194         m_rtmsg.m_rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
195         sin = (struct sockaddr_in *)m_rtmsg.m_space;
196         sin->sin_family = AF_INET;
197         sin->sin_len = sizeof(struct sockaddr_in);
198         sin->sin_addr = *them;
199
200         if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
201                 ast_log(LOG_ERROR, "Error opening routing socket\n");
202                 return -1;
203         }
204         flags = fcntl(s, F_GETFL);
205         fcntl(s, F_SETFL, flags | O_NONBLOCK);
206         if (write(s, (char *)&m_rtmsg, m_rtmsg.m_rtm.rtm_msglen) < 0) {
207                 ast_log(LOG_ERROR, "Error writing to routing socket: %s\n", strerror(errno));
208                 close(s);
209                 return -1;
210         }
211         do {
212                 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
213         } while (l > 0 && (m_rtmsg.m_rtm.rtm_seq != 1 || m_rtmsg.m_rtm.rtm_pid != pid));
214         if (l < 0) {
215                 if (errno != EAGAIN)
216                         ast_log(LOG_ERROR, "Error reading from routing socket\n");
217                 close(s);
218                 return -1;
219         }
220         close(s);
221
222         if (m_rtmsg.m_rtm.rtm_version != RTM_VERSION) {
223                 ast_log(LOG_ERROR, "Unsupported route socket protocol version\n");
224                 return -1;
225         }
226
227         if (m_rtmsg.m_rtm.rtm_msglen != l)
228                 ast_log(LOG_WARNING, "Message length mismatch, in packet %d, returned %d\n",
229                                 m_rtmsg.m_rtm.rtm_msglen, l);
230
231         if (m_rtmsg.m_rtm.rtm_errno) {
232                 ast_log(LOG_ERROR, "RTM_GET got %s (%d)\n",
233                                 strerror(m_rtmsg.m_rtm.rtm_errno), m_rtmsg.m_rtm.rtm_errno);
234                 return -1;
235         }
236
237         cp = (char *)m_rtmsg.m_space;
238         if (m_rtmsg.m_rtm.rtm_addrs)
239                 for (i = 1; i; i <<= 1)
240                         if (m_rtmsg.m_rtm.rtm_addrs & i) {
241                                 sa = (struct sockaddr *)cp;
242                                 if (i == RTA_IFA && sa->sa_family == AF_INET) {
243                                         sin = (struct sockaddr_in *)sa;
244                                         *us = sin->sin_addr;
245                                         ast_log(LOG_DEBUG, "Found route to %s, output from our address %s.\n", p, inet_ntoa(*us));
246                                         return 0;
247                                 }
248                                 cp += sa->sa_len > 0 ?
249                                           (1 + ((sa->sa_len - 1) | (sizeof(long) - 1))) :
250                                           sizeof(long);
251                         }
252
253         ast_log(LOG_DEBUG, "No route found for address %s!\n", p);
254         return -1;
255 #else
256         FILE *PROC;
257         unsigned int remote_ip;
258         int res = 1;
259         char line[256];
260         remote_ip = them->s_addr;
261         
262         PROC = fopen("/proc/net/route","r");
263         if (!PROC) {
264                 bzero(us,sizeof(struct in_addr));
265                 return -1;
266         }
267         /* First line contains headers */
268         fgets(line,sizeof(line),PROC);
269
270         while (!feof(PROC)) {
271                 char iface[8];
272                 unsigned int dest, gateway, mask;
273                 int i,fieldnum;
274                 char *fields[40];
275
276                 fgets(line,sizeof(line),PROC);
277
278                 fieldnum = 0;
279                 for (i=0;i<sizeof(line);i++) {
280                         char *offset;
281
282                         fields[fieldnum++] = line + i;
283                         offset = strchr(line + i,'\t');
284                         if (offset == NULL) {
285                                 /* Exit loop */
286                                 break;
287                         } else if (fieldnum >= 9) {
288                                 /* Short-circuit: can't break at 8, since the end of field 7 is figured when fieldnum=8 */
289                                 break;
290                         } else {
291                                 *offset = '\0';
292                                 i = offset - line;
293                         }
294                 }
295
296                 sscanf(fields[0],"%s",iface);
297                 sscanf(fields[1],"%x",&dest);
298                 sscanf(fields[2],"%x",&gateway);
299                 sscanf(fields[7],"%x",&mask);
300 #if 0
301                 printf("Addr: %s %08x Dest: %08x Mask: %08x\n", inet_ntoa(*them), remote_ip, dest, mask);
302 #endif          
303                 /* Looks simple, but here is the magic */
304                 if (((remote_ip & mask) ^ dest) == 0) {
305                         res = ast_lookup_iface(iface,us);
306                         break;
307                 }
308         }
309         fclose(PROC);
310         if (res == 1) {
311                 ast_log(LOG_WARNING, "Yikes!  No default route?!!\n");
312                 bzero(us,sizeof(struct in_addr));
313                 return -2;
314         } else if (res) {
315                 /* We've already warned in subroutine */
316                 return -1;
317         }
318         return 0;
319 #endif
320 }