Merge anthm's ODBC sanity check fix (bug #3529)
[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 <string.h>
17 #include <sys/time.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <asterisk/acl.h>
22 #include <asterisk/logger.h>
23 #include <asterisk/channel.h>
24 #include <asterisk/options.h>
25 #include <asterisk/utils.h>
26 #include <asterisk/lock.h>
27 #include <asterisk/srv.h>
28 #include <arpa/inet.h>
29 #include <sys/socket.h>
30 #include <netdb.h>
31 #include <net/if.h>
32 #include <netinet/in_systm.h>
33 #include <netinet/ip.h>
34 #include <sys/ioctl.h>
35 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
36 #include <fcntl.h>
37 #include <net/route.h>
38
39 AST_MUTEX_DEFINE_STATIC(routeseq_lock);
40 #endif
41
42 #if defined (SOLARIS)
43 #include <sys/sockio.h>
44 #endif
45
46 struct ast_netsock {
47         ASTOBJ_COMPONENTS(struct ast_netsock);
48         struct sockaddr_in bindaddr;
49         int sockfd;
50         int *ioref;
51         struct io_context *ioc;
52 };
53
54
55 struct ast_ha {
56         /* Host access rule */
57         struct in_addr netaddr;
58         struct in_addr netmask;
59         int sense;
60         struct ast_ha *next;
61 };
62
63 /* Default IP - if not otherwise set, don't breathe garbage */
64 static struct in_addr __ourip = { 0x00000000 };
65
66 struct my_ifreq {
67         char ifrn_name[IFNAMSIZ];       /* Interface name, e.g. "eth0", "ppp0", etc.  */
68         struct sockaddr_in ifru_addr;
69 };
70
71 /* Free HA structure */
72 void ast_free_ha(struct ast_ha *ha)
73 {
74         struct ast_ha *hal;
75         while(ha) {
76                 hal = ha;
77                 ha = ha->next;
78                 free(hal);
79         }
80 }
81
82 /* Copy HA structure */
83 static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
84 {
85         memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
86         memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
87         to->sense = from->sense;
88 }
89
90 /* Create duplicate of ha structure */
91 static struct ast_ha *ast_duplicate_ha(struct ast_ha *original)
92 {
93         struct ast_ha *new_ha = malloc(sizeof(struct ast_ha));
94
95         /* Copy from original to new object */
96         ast_copy_ha(original, new_ha); 
97
98         return(new_ha);
99
100 }
101
102 /* Create duplicate HA link list */
103 /*  Used in chan_sip2 templates */
104 struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
105 {
106         struct ast_ha *start=original;
107         struct ast_ha *ret = NULL;
108         struct ast_ha *link,*prev=NULL;
109
110         while(start) {
111                 link = ast_duplicate_ha(start);  /* Create copy of this object */
112                 if (prev)
113                         prev->next = link;              /* Link previous to this object */
114
115                 if (!ret) 
116                         ret = link;             /* Save starting point */
117
118                 start = start->next;            /* Go to next object */
119                 prev = link;                    /* Save pointer to this object */
120         }
121         return (ret);                           /* Return start of list */
122 }
123
124 struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
125 {
126         struct ast_ha *ha = malloc(sizeof(struct ast_ha));
127         char *nm="255.255.255.255";
128         char tmp[256] = "";
129         struct ast_ha *prev = NULL;
130         struct ast_ha *ret;
131         int x,z;
132         unsigned int y;
133         ret = path;
134         while(path) {
135                 prev = path;
136                 path = path->next;
137         }
138         if (ha) {
139                 strncpy(tmp, stuff, sizeof(tmp) - 1);
140                 nm = strchr(tmp, '/');
141                 if (!nm)
142                         nm = "255.255.255.255";
143                 else {
144                         *nm = '\0';
145                         nm++;
146                 }
147                 if (!strchr(nm, '.')) {
148                         if ((sscanf(nm, "%i", &x) == 1) && (x >= 0) && (x <= 32)) {
149                                 y = 0;
150                                 for (z=0;z<x;z++) {
151                                         y >>= 1;
152                                         y |= 0x80000000;
153                                 }
154                                 ha->netmask.s_addr = htonl(y);
155                         }
156                 } else if (!inet_aton(nm, &ha->netmask)) {
157                         ast_log(LOG_WARNING, "%s is not a valid netmask\n", nm);
158                         free(ha);
159                         return path;
160                 }
161                 if (!inet_aton(tmp, &ha->netaddr)) {
162                         ast_log(LOG_WARNING, "%s is not a valid IP\n", tmp);
163                         free(ha);
164                         return path;
165                 }
166                 ha->netaddr.s_addr &= ha->netmask.s_addr;
167                 if (!strncasecmp(sense, "p", 1)) {
168                         ha->sense = AST_SENSE_ALLOW;
169                 } else {
170                         ha->sense = AST_SENSE_DENY;
171                 }
172                 ha->next = NULL;
173                 if (prev)
174                         prev->next = ha;
175                 else
176                         ret = ha;
177         }
178         ast_log(LOG_DEBUG, "%s/%s appended to acl for peer\n",stuff, nm);
179         return ret;
180 }
181
182 int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
183 {
184         /* Start optimistic */
185         int res = AST_SENSE_ALLOW;
186         while(ha) {
187                 char iabuf[INET_ADDRSTRLEN];
188                 char iabuf2[INET_ADDRSTRLEN];
189                 /* DEBUG */
190                 ast_log(LOG_DEBUG,
191                         "##### Testing %s with %s\n",
192                         ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr),
193                         ast_inet_ntoa(iabuf2, sizeof(iabuf2), ha->netaddr));
194                 /* For each rule, if this address and the netmask = the net address
195                    apply the current rule */
196                 if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
197                         res = ha->sense;
198                 ha = ha->next;
199         }
200         return res;
201 }
202
203 int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service)
204 {
205         struct hostent *hp;
206         struct ast_hostent ahp;
207         char srv[256];
208         char host[256];
209         int tportno = ntohs(sin->sin_port);
210         if (inet_aton(value, &sin->sin_addr))
211                 return 0;
212         if (service) {
213                 snprintf(srv, sizeof(srv), "%s.%s", service, value);
214                 if (ast_get_srv(NULL, host, sizeof(host), &tportno, srv) > 0) {
215                         sin->sin_port = htons(tportno);
216                         value = host;
217                 }
218         }
219         hp = ast_gethostbyname(value, &ahp);
220         if (hp) {
221                 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
222         } else {
223                 ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
224                 return -1;
225         }
226         return 0;
227 }
228
229 int ast_get_ip(struct sockaddr_in *sin, const char *value)
230 {
231         return ast_get_ip_or_srv(sin, value, NULL);
232 }
233
234 /* iface is the interface (e.g. eth0); address is the return value */
235 int ast_lookup_iface(char *iface, struct in_addr *address) 
236 {
237         int mysock, res = 0;
238         struct my_ifreq ifreq;
239
240         memset(&ifreq, 0, sizeof(ifreq));
241         strncpy(ifreq.ifrn_name,iface,sizeof(ifreq.ifrn_name) - 1);
242
243         mysock = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
244         res = ioctl(mysock,SIOCGIFADDR,&ifreq);
245
246         close(mysock);
247         if (res < 0) {
248                 ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
249                 memcpy((char *)address,(char *)&__ourip,sizeof(__ourip));
250                 return -1;
251         } else {
252                 memcpy((char *)address,(char *)&ifreq.ifru_addr.sin_addr,sizeof(ifreq.ifru_addr.sin_addr));
253                 return 0;
254         }
255 }
256
257 int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
258 {
259         int s;
260         struct sockaddr_in sin;
261         socklen_t slen;
262
263         s = socket(PF_INET, SOCK_DGRAM, 0);
264         if (s == -1) {
265                 ast_log(LOG_WARNING, "Cannot create socket\n");
266                 return -1;
267         }
268         sin.sin_family = AF_INET;
269         sin.sin_port = 5060;
270         sin.sin_addr = *them;
271         if (connect(s, (struct sockaddr *)&sin, sizeof(sin))) {
272                 ast_log(LOG_WARNING, "Cannot connect\n");
273                 close(s);
274                 return -1;
275         }
276         slen = sizeof(sin);
277         if (getsockname(s, (struct sockaddr *)&sin, &slen)) {
278                 ast_log(LOG_WARNING, "Cannot get socket name\n");
279                 close(s);
280                 return -1;
281         }
282         close(s);
283         *us = sin.sin_addr;
284         return 0;
285 }
286
287 int ast_netsock_sockfd(struct ast_netsock *ns)
288 {
289         if (ns)
290                 return ns->sockfd;
291         return -1;
292 }
293
294 struct ast_netsock *ast_netsock_bindaddr(struct ast_netsock_list *list, struct io_context *ioc, struct sockaddr_in *bindaddr, int tos, ast_io_cb callback, void *data)
295 {
296         int netsocket = -1;
297         int *ioref;
298         char iabuf[INET_ADDRSTRLEN];
299         
300         struct ast_netsock *ns;
301         
302         /* Make a UDP socket */
303         netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
304         
305         if (netsocket < 0) {
306                 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
307                 return NULL;
308         }
309         if (bind(netsocket,(struct sockaddr *)bindaddr, sizeof(struct sockaddr_in))) {
310                 ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr->sin_addr), ntohs(bindaddr->sin_port), strerror(errno));
311                 close(netsocket);
312                 return NULL;
313         }
314         if (option_verbose > 1)
315                 ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
316
317         if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) 
318                 ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
319
320         /* Establish I/O callback for socket read */
321         ioref = ast_io_add(ioc, netsocket, callback, AST_IO_IN, data);
322         if (!ioref) {
323                 ast_log(LOG_WARNING, "Out of memory!\n");
324                 close(netsocket);
325                 return NULL;
326         }
327         
328         ns = malloc(sizeof(struct ast_netsock));
329         if (ns) {
330                 ASTOBJ_INIT(ns);
331                 ns->ioref = ioref;
332                 ns->ioc = ioc;
333                 ns->sockfd = netsocket;
334                 memcpy(&ns->bindaddr, bindaddr, sizeof(ns->bindaddr));
335                 ASTOBJ_CONTAINER_LINK(list, ns);
336         } else {
337                 ast_log(LOG_WARNING, "Out of memory!\n");
338                 ast_io_remove(ioc, ioref);
339                 close(netsocket);
340         }
341         return ns;
342 }
343
344 static void ast_netsock_destroy(struct ast_netsock *netsock)
345 {
346         ast_io_remove(netsock->ioc, netsock->ioref);
347         close(netsock->sockfd);
348         free(netsock);
349 }
350
351 int ast_netsock_init(struct ast_netsock_list *list)
352 {
353         memset(list, 0, sizeof(struct ast_netsock_list));
354         ASTOBJ_CONTAINER_INIT(list);
355         return 0;
356 }
357
358 int ast_netsock_release(struct ast_netsock_list *list)
359 {
360         ASTOBJ_CONTAINER_DESTROYALL(list, ast_netsock_destroy);
361         ASTOBJ_CONTAINER_DESTROY(list);
362         return 0;
363 }
364
365 struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struct io_context *ioc, const char *bindinfo, int defaultport, int tos, ast_io_cb callback, void *data)
366 {
367         struct sockaddr_in sin;
368         char *tmp;
369         char *port;
370         int portno;
371         memset(&sin, 0, sizeof(sin));
372         sin.sin_family = AF_INET;
373         sin.sin_port = htons(defaultport);
374         tmp = ast_strdupa(bindinfo);
375         if (tmp) {
376                 port = strchr(tmp, ':');
377                 if (port) {
378                         *port = '\0';
379                         port++;
380                         if ((portno = atoi(port)) > 0) 
381                                 sin.sin_port = htons(portno);
382                 }
383                 inet_aton(tmp, &sin.sin_addr);
384                 return ast_netsock_bindaddr(list, ioc, &sin, tos, callback, data);
385         } else
386                 ast_log(LOG_WARNING, "Out of memory!\n");
387         return NULL;
388 }
389
390 int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr)
391 {
392         char ourhost[256];
393         struct ast_hostent ahp;
394         struct hostent *hp;
395         struct in_addr saddr;
396
397         /* just use the bind address if it is nonzero */
398         if (ntohl(bindaddr.sin_addr.s_addr)) {
399                 memcpy(ourip, &bindaddr.sin_addr, sizeof(*ourip));
400                 return 0;
401         }
402         /* try to use our hostname */
403         if (gethostname(ourhost, sizeof(ourhost))) {
404                 ast_log(LOG_WARNING, "Unable to get hostname\n");
405         } else {
406                 hp = ast_gethostbyname(ourhost, &ahp);
407                 if (hp) {
408                         memcpy(ourip, hp->h_addr, sizeof(*ourip));
409                         return 0;
410                 }
411         }
412         /* A.ROOT-SERVERS.NET. */
413         if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip))
414                 return 0;
415         return -1;
416 }
417