Let's also include aclocal.m4
[asterisk-bristuff.git] / main / acl.c
blob4a5e16485aa608203360fdce5cc03584e1c66b6f
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Various sorts of access control
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/time.h>
34 #include <signal.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <sys/socket.h>
40 #include <netdb.h>
41 #include <net/if.h>
42 #include <netinet/in_systm.h>
43 #include <netinet/ip.h>
44 #include <sys/ioctl.h>
46 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__Darwin__)
47 #include <fcntl.h>
48 #include <net/route.h>
49 #endif
51 #if defined(SOLARIS)
52 #include <sys/sockio.h>
53 #include <net/if.h>
54 #elif defined(HAVE_GETIFADDRS)
55 #include <ifaddrs.h>
56 #endif
58 /* netinet/ip.h may not define the following (See RFCs 791 and 1349) */
59 #if !defined(IPTOS_LOWCOST)
60 #define IPTOS_LOWCOST 0x02
61 #endif
63 #if !defined(IPTOS_MINCOST)
64 #define IPTOS_MINCOST IPTOS_LOWCOST
65 #endif
67 #include "asterisk/acl.h"
68 #include "asterisk/logger.h"
69 #include "asterisk/channel.h"
70 #include "asterisk/options.h"
71 #include "asterisk/utils.h"
72 #include "asterisk/lock.h"
73 #include "asterisk/srv.h"
75 struct ast_ha {
76 /* Host access rule */
77 struct in_addr netaddr;
78 struct in_addr netmask;
79 int sense;
80 struct ast_ha *next;
83 /* Default IP - if not otherwise set, don't breathe garbage */
84 static struct in_addr __ourip = { .s_addr = 0x00000000, };
86 struct my_ifreq {
87 char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "eth0", "ppp0", etc. */
88 struct sockaddr_in ifru_addr;
91 #if (!defined(SOLARIS) && !defined(HAVE_GETIFADDRS))
92 static int get_local_address(struct in_addr *ourip)
94 return -1;
96 #else
97 static void score_address(const struct sockaddr_in *sin, struct in_addr *best_addr, int *best_score)
99 const char *address;
100 int score;
102 address = ast_inet_ntoa(sin->sin_addr);
104 /* RFC 1700 alias for the local network */
105 if (address[0] == '0')
106 score = -25;
107 /* RFC 1700 localnet */
108 else if (strncmp(address, "127", 3) == 0)
109 score = -20;
110 /* RFC 1918 non-public address space */
111 else if (strncmp(address, "10.", 3) == 0)
112 score = -5;
113 /* RFC 1918 non-public address space */
114 else if (strncmp(address, "172", 3) == 0) {
115 /* 172.16.0.0 - 172.19.255.255, but not 172.160.0.0 - 172.169.255.255 */
116 if (address[4] == '1' && address[5] >= '6' && address[6] == '.')
117 score = -5;
118 /* 172.20.0.0 - 172.29.255.255, but not 172.200.0.0 - 172.255.255.255 nor 172.2.0.0 - 172.2.255.255 */
119 else if (address[4] == '2' && address[6] == '.')
120 score = -5;
121 /* 172.30.0.0 - 172.31.255.255 */
122 else if (address[4] == '3' && address[5] <= '1')
123 score = -5;
124 /* All other 172 addresses are public */
125 else
126 score = 0;
127 /* RFC 2544 Benchmark test range */
128 } else if (strncmp(address, "198.1", 5) == 0 && address[5] >= '8' && address[6] == '.')
129 score = -10;
130 /* RFC 1918 non-public address space */
131 else if (strncmp(address, "192.168", 7) == 0)
132 score = -5;
133 /* RFC 3330 Zeroconf network */
134 else if (strncmp(address, "169.254", 7) == 0)
135 /*!\note Better score than a test network, but not quite as good as RFC 1918
136 * address space. The reason is that some Linux distributions automatically
137 * configure a Zeroconf address before trying DHCP, so we want to prefer a
138 * DHCP lease to a Zeroconf address.
140 score = -10;
141 /* RFC 3330 Test network */
142 else if (strncmp(address, "192.0.2.", 8) == 0)
143 score = -15;
144 /* Every other address should be publically routable */
145 else
146 score = 0;
148 if (score > *best_score) {
149 *best_score = score;
150 memcpy(best_addr, &sin->sin_addr, sizeof(*best_addr));
154 static int get_local_address(struct in_addr *ourip)
156 int s, res = -1;
157 #ifdef SOLARIS
158 struct lifreq *ifr = NULL;
159 struct lifnum ifn;
160 struct lifconf ifc;
161 struct sockaddr_in *sa;
162 char *buf = NULL;
163 int bufsz, x;
164 #endif /* SOLARIS */
165 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
166 struct ifaddrs *ifap, *ifaphead;
167 int rtnerr;
168 const struct sockaddr_in *sin;
169 #endif /* BSD_OR_LINUX */
170 struct in_addr best_addr;
171 int best_score = -100;
172 memset(&best_addr, 0, sizeof(best_addr));
174 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
175 rtnerr = getifaddrs(&ifaphead);
176 if (rtnerr) {
177 perror(NULL);
178 return -1;
180 #endif /* BSD_OR_LINUX */
182 s = socket(AF_INET, SOCK_STREAM, 0);
184 if (s > 0) {
185 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
186 for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
188 if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_INET) {
189 sin = (const struct sockaddr_in *) ifap->ifa_addr;
190 score_address(sin, &best_addr, &best_score);
191 res = 0;
193 if (best_score == 0)
194 break;
197 #endif /* BSD_OR_LINUX */
199 /* There is no reason whatsoever that this shouldn't work on Linux or BSD also. */
200 #ifdef SOLARIS
201 /* Get a count of interfaces on the machine */
202 ifn.lifn_family = AF_INET;
203 ifn.lifn_flags = 0;
204 ifn.lifn_count = 0;
205 if (ioctl(s, SIOCGLIFNUM, &ifn) < 0) {
206 close(s);
207 return -1;
210 bufsz = ifn.lifn_count * sizeof(struct lifreq);
211 if (!(buf = malloc(bufsz))) {
212 close(s);
213 return -1;
215 memset(buf, 0, bufsz);
217 /* Get a list of interfaces on the machine */
218 ifc.lifc_len = bufsz;
219 ifc.lifc_buf = buf;
220 ifc.lifc_family = AF_INET;
221 ifc.lifc_flags = 0;
222 if (ioctl(s, SIOCGLIFCONF, &ifc) < 0) {
223 close(s);
224 free(buf);
225 return -1;
228 for (ifr = ifc.lifc_req, x = 0; x < ifn.lifn_count; ifr++, x++) {
229 sa = (struct sockaddr_in *)&(ifr->lifr_addr);
230 score_address(sa, &best_addr, &best_score);
231 res = 0;
233 if (best_score == 0)
234 break;
237 free(buf);
238 #endif /* SOLARIS */
240 close(s);
242 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
243 freeifaddrs(ifaphead);
244 #endif /* BSD_OR_LINUX */
246 if (res == 0 && ourip)
247 memcpy(ourip, &best_addr, sizeof(*ourip));
248 return res;
250 #endif /* HAVE_GETIFADDRS */
252 /* Free HA structure */
253 void ast_free_ha(struct ast_ha *ha)
255 struct ast_ha *hal;
256 while (ha) {
257 hal = ha;
258 ha = ha->next;
259 free(hal);
263 /* Copy HA structure */
264 static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
266 memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
267 memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
268 to->sense = from->sense;
271 /* Create duplicate of ha structure */
272 static struct ast_ha *ast_duplicate_ha(struct ast_ha *original)
274 struct ast_ha *new_ha;
276 if ((new_ha = ast_malloc(sizeof(*new_ha)))) {
277 /* Copy from original to new object */
278 ast_copy_ha(original, new_ha);
281 return new_ha;
284 /* Create duplicate HA link list */
285 /* Used in chan_sip2 templates */
286 struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
288 struct ast_ha *start = original;
289 struct ast_ha *ret = NULL;
290 struct ast_ha *link, *prev = NULL;
292 while (start) {
293 link = ast_duplicate_ha(start); /* Create copy of this object */
294 if (prev)
295 prev->next = link; /* Link previous to this object */
297 if (!ret)
298 ret = link; /* Save starting point */
300 start = start->next; /* Go to next object */
301 prev = link; /* Save pointer to this object */
303 return ret; /* Return start of list */
306 struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
308 struct ast_ha *ha;
309 char *nm = "255.255.255.255";
310 char tmp[256];
311 struct ast_ha *prev = NULL;
312 struct ast_ha *ret;
313 int x, z;
314 unsigned int y;
316 ret = path;
317 while (path) {
318 prev = path;
319 path = path->next;
321 if ((ha = ast_malloc(sizeof(*ha)))) {
322 ast_copy_string(tmp, stuff, sizeof(tmp));
323 nm = strchr(tmp, '/');
324 if (!nm) {
325 nm = "255.255.255.255";
326 } else {
327 *nm = '\0';
328 nm++;
330 if (!strchr(nm, '.')) {
331 if ((sscanf(nm, "%d", &x) == 1) && (x >= 0) && (x <= 32)) {
332 y = 0;
333 for (z = 0; z < x; z++) {
334 y >>= 1;
335 y |= 0x80000000;
337 ha->netmask.s_addr = htonl(y);
339 } else if (!inet_aton(nm, &ha->netmask)) {
340 ast_log(LOG_WARNING, "%s is not a valid netmask\n", nm);
341 free(ha);
342 return ret;
344 if (!inet_aton(tmp, &ha->netaddr)) {
345 ast_log(LOG_WARNING, "%s is not a valid IP\n", tmp);
346 free(ha);
347 return ret;
349 ha->netaddr.s_addr &= ha->netmask.s_addr;
350 if (!strncasecmp(sense, "p", 1)) {
351 ha->sense = AST_SENSE_ALLOW;
352 } else {
353 ha->sense = AST_SENSE_DENY;
355 ha->next = NULL;
356 if (prev) {
357 prev->next = ha;
358 } else {
359 ret = ha;
362 if (option_debug)
363 ast_log(LOG_DEBUG, "%s/%s appended to acl for peer\n", stuff, nm);
364 return ret;
367 int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
369 /* Start optimistic */
370 int res = AST_SENSE_ALLOW;
371 while (ha) {
372 char iabuf[INET_ADDRSTRLEN];
373 char iabuf2[INET_ADDRSTRLEN];
374 /* DEBUG */
375 ast_copy_string(iabuf, ast_inet_ntoa(sin->sin_addr), sizeof(iabuf));
376 ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
377 if (option_debug)
378 ast_log(LOG_DEBUG, "##### Testing %s with %s\n", iabuf, iabuf2);
379 /* For each rule, if this address and the netmask = the net address
380 apply the current rule */
381 if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == ha->netaddr.s_addr)
382 res = ha->sense;
383 ha = ha->next;
385 return res;
388 int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service)
390 struct hostent *hp;
391 struct ast_hostent ahp;
392 char srv[256];
393 char host[256];
394 int tportno = ntohs(sin->sin_port);
395 if (inet_aton(value, &sin->sin_addr))
396 return 0;
397 if (service) {
398 snprintf(srv, sizeof(srv), "%s.%s", service, value);
399 if (ast_get_srv(NULL, host, sizeof(host), &tportno, srv) > 0) {
400 sin->sin_port = htons(tportno);
401 value = host;
404 hp = ast_gethostbyname(value, &ahp);
405 if (hp) {
406 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
407 } else {
408 ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
409 return -1;
411 return 0;
414 struct dscp_codepoint {
415 char *name;
416 unsigned int space;
419 /* IANA registered DSCP codepoints */
421 static const struct dscp_codepoint dscp_pool1[] = {
422 { "CS0", 0x00 },
423 { "CS1", 0x08 },
424 { "CS2", 0x10 },
425 { "CS3", 0x18 },
426 { "CS4", 0x20 },
427 { "CS5", 0x28 },
428 { "CS6", 0x30 },
429 { "CS7", 0x38 },
430 { "AF11", 0x0A },
431 { "AF12", 0x0C },
432 { "AF13", 0x0E },
433 { "AF21", 0x12 },
434 { "AF22", 0x14 },
435 { "AF23", 0x16 },
436 { "AF31", 0x1A },
437 { "AF32", 0x1C },
438 { "AF33", 0x1E },
439 { "AF41", 0x22 },
440 { "AF42", 0x24 },
441 { "AF43", 0x26 },
442 { "EF", 0x2E },
445 int ast_str2tos(const char *value, unsigned int *tos)
447 int fval;
448 unsigned int x;
450 if (sscanf(value, "%i", &fval) == 1) {
451 *tos = fval & 0xFF;
452 return 0;
455 for (x = 0; x < sizeof(dscp_pool1) / sizeof(dscp_pool1[0]); x++) {
456 if (!strcasecmp(value, dscp_pool1[x].name)) {
457 *tos = dscp_pool1[x].space << 2;
458 return 0;
462 if (!strcasecmp(value, "lowdelay"))
463 *tos = IPTOS_LOWDELAY;
464 else if (!strcasecmp(value, "throughput"))
465 *tos = IPTOS_THROUGHPUT;
466 else if (!strcasecmp(value, "reliability"))
467 *tos = IPTOS_RELIABILITY;
468 else if (!strcasecmp(value, "mincost"))
469 *tos = IPTOS_MINCOST;
470 else if (!strcasecmp(value, "none"))
471 *tos = 0;
472 else
473 return -1;
475 ast_log(LOG_WARNING, "TOS value %s is deprecated. Please see doc/ip-tos.txt for more information.\n", value);
477 return 0;
480 const char *ast_tos2str(unsigned int tos)
482 unsigned int x;
484 switch (tos) {
485 case 0:
486 return "none";
487 case IPTOS_LOWDELAY:
488 return "lowdelay";
489 case IPTOS_THROUGHPUT:
490 return "throughput";
491 case IPTOS_RELIABILITY:
492 return "reliability";
493 case IPTOS_MINCOST:
494 return "mincost";
495 default:
496 for (x = 0; x < sizeof(dscp_pool1) / sizeof(dscp_pool1[0]); x++) {
497 if (dscp_pool1[x].space == (tos >> 2))
498 return dscp_pool1[x].name;
502 return "unknown";
505 int ast_get_ip(struct sockaddr_in *sin, const char *value)
507 return ast_get_ip_or_srv(sin, value, NULL);
510 /* iface is the interface (e.g. eth0); address is the return value */
511 int ast_lookup_iface(char *iface, struct in_addr *address)
513 int mysock, res = 0;
514 struct my_ifreq ifreq;
516 memset(&ifreq, 0, sizeof(ifreq));
517 ast_copy_string(ifreq.ifrn_name, iface, sizeof(ifreq.ifrn_name));
519 mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
520 res = ioctl(mysock, SIOCGIFADDR, &ifreq);
522 close(mysock);
523 if (res < 0) {
524 ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
525 memcpy((char *)address, (char *)&__ourip, sizeof(__ourip));
526 return -1;
527 } else {
528 memcpy((char *)address, (char *)&ifreq.ifru_addr.sin_addr, sizeof(ifreq.ifru_addr.sin_addr));
529 return 0;
533 int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
535 int s;
536 struct sockaddr_in sin;
537 socklen_t slen;
539 s = socket(PF_INET, SOCK_DGRAM, 0);
540 if (s < 0) {
541 ast_log(LOG_WARNING, "Cannot create socket\n");
542 return -1;
544 sin.sin_family = AF_INET;
545 sin.sin_port = 5060;
546 sin.sin_addr = *them;
547 if (connect(s, (struct sockaddr *)&sin, sizeof(sin))) {
548 ast_log(LOG_WARNING, "Cannot connect\n");
549 close(s);
550 return -1;
552 slen = sizeof(sin);
553 if (getsockname(s, (struct sockaddr *)&sin, &slen)) {
554 ast_log(LOG_WARNING, "Cannot get socket name\n");
555 close(s);
556 return -1;
558 close(s);
559 *us = sin.sin_addr;
560 return 0;
563 int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr)
565 char ourhost[MAXHOSTNAMELEN] = "";
566 struct ast_hostent ahp;
567 struct hostent *hp;
568 struct in_addr saddr;
570 /* just use the bind address if it is nonzero */
571 if (ntohl(bindaddr.sin_addr.s_addr)) {
572 memcpy(ourip, &bindaddr.sin_addr, sizeof(*ourip));
573 return 0;
575 /* try to use our hostname */
576 if (gethostname(ourhost, sizeof(ourhost) - 1)) {
577 ast_log(LOG_WARNING, "Unable to get hostname\n");
578 } else {
579 hp = ast_gethostbyname(ourhost, &ahp);
580 if (hp) {
581 memcpy(ourip, hp->h_addr, sizeof(*ourip));
582 return 0;
585 /* A.ROOT-SERVERS.NET. */
586 if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip))
587 return 0;
588 return get_local_address(ourip);