Index: utils.c =================================================================== --- utils.c (revision 215957) +++ utils.c (revision 217888) @@ -45,6 +45,7 @@ #include "asterisk/io.h" #include "asterisk/logger.h" #include "asterisk/md5.h" +#include "asterisk/sha1.h" #include "asterisk/options.h" #include "asterisk/compat.h" @@ -297,6 +298,24 @@ ptr += sprintf(ptr, "%2.2x", digest[x]); } +/*! \brief Produce 40 char SHA1 hash of value. */ +void ast_sha1_hash(char *output, char *input) +{ + struct SHA1Context sha; + char *ptr; + int x; + uint8_t Message_Digest[20]; + + SHA1Reset(&sha); + + SHA1Input(&sha, (const unsigned char *) input, strlen(input)); + + SHA1Result(&sha, Message_Digest); + ptr = output; + for (x = 0; x < 20; x++) + ptr += sprintf(ptr, "%2.2x", Message_Digest[x]); +} + int ast_base64decode(unsigned char *dst, const char *src, int max) { int cnt = 0; Index: channels/iax2-parser.h =================================================================== --- channels/iax2-parser.h (revision 215957) +++ channels/iax2-parser.h (revision 217888) @@ -71,6 +71,8 @@ unsigned short rr_delay; unsigned int rr_dropped; unsigned int rr_ooo; + unsigned char calltoken; + unsigned char *calltokendata; }; #define DIRECTION_INGRESS 1 Index: channels/chan_iax2.c =================================================================== --- channels/chan_iax2.c (revision 215957) +++ channels/chan_iax2.c (revision 217888) @@ -281,6 +281,20 @@ static int reload_config(void); static int iax2_reload(int fd, int argc, char *argv[]); +/*! + * \brief Call token validation settings. + */ +enum calltoken_peer_enum { + /*! \brief Default calltoken required unless the ip is in the ignorelist */ + CALLTOKEN_DEFAULT = 0, + /*! \brief Require call token validation. */ + CALLTOKEN_YES = 1, + /*! \brief Require call token validation after a successful registration + * using call token validation occurs. */ + CALLTOKEN_AUTO = 2, + /*! \brief Do not require call token validation. */ + CALLTOKEN_NO = 3, +}; struct iax2_user { char name[80]; @@ -303,6 +317,7 @@ struct iax2_context *contexts; struct iax2_user *next; struct ast_variable *vars; + enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */ }; struct iax2_peer { @@ -348,8 +363,10 @@ int pokefreqnotok; /*!< How often to check when the host has been determined to be down */ int historicms; /*!< How long recent average responses took */ int smoothing; /*!< Sample over how many units to determine historic ms */ + uint16_t maxcallno; /*!< Max call number limit for this peer. Set on registration */ struct ast_ha *ha; + enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */ struct iax2_peer *next; }; @@ -482,6 +499,8 @@ struct ast_codec_pref rprefs; /*! Our call number */ unsigned short callno; + /*! Our callno_entry entry */ + struct callno_entry *callno_entry; /*! Peer callno */ unsigned short peercallno; /*! Negotiated format, this is only used to remember what format was @@ -616,8 +635,18 @@ int frames_dropped; /*! received frame count: (just for stats) */ int frames_received; + /*! num bytes used for calltoken ie, even an empty ie should contain 2 */ + unsigned char calltoken_ie_len; }; +/*! table of available call numbers */ +static struct ao2_container *callno_pool; + +/*! table of available trunk call numbers */ +static struct ao2_container *callno_pool_trunk; + +static const unsigned int CALLNO_POOL_BUCKETS = 2699; + static struct ast_iax2_queue { struct iax_frame *head; struct iax_frame *tail; @@ -625,6 +654,63 @@ ast_mutex_t lock; } iaxq; +static int randomcalltokendata; + +static const time_t MAX_CALLTOKEN_DELAY = 10; + +#define MAX_PEER_BUCKETS 563 + +/*! Table containing peercnt objects for every ip address consuming a callno */ +static struct ao2_container *peercnts; + +/*! Table containing custom callno limit rules for a range of ip addresses. */ +static struct ao2_container *callno_limits; + +/*! Table containing ip addresses not requiring calltoken validation */ +static struct ao2_container *calltoken_ignores; + +static uint16_t DEFAULT_MAXCALLNO_LIMIT = 2048; + +static uint16_t DEFAULT_MAXCALLNO_LIMIT_NONVAL = 8192; + +static uint16_t global_maxcallno; + +/*! Total num of call numbers allowed to be allocated without calltoken validation */ +static uint16_t global_maxcallno_nonval; + +static uint16_t total_nonval_callno_used = 0; + +/*! peer connection private, keeps track of all the call numbers + * consumed by a single ip address */ +struct peercnt { + /*! ip address consuming call numbers */ + unsigned long addr; + /*! Number of call numbers currently used by this ip address */ + uint16_t cur; + /*! Max call numbers allowed for this ip address */ + uint16_t limit; + /*! Specifies whether limit is set by a registration or not, if so normal + * limit setting rules do not apply to this address. */ + unsigned char reg; +}; + +/*! used by both callno_limits and calltoken_ignores containers */ +struct addr_range { + /*! ip address range for custom callno limit rule */ + struct ast_ha ha; + /*! callno limit for this ip address range, only used in callno_limits container */ + uint16_t limit; + /*! delete me marker for reloads */ + unsigned char delme; +}; + +struct callno_entry { + /*! callno used for this entry */ + uint16_t callno; + /*! was this callno calltoken validated or not */ + unsigned char validated; +}; + static struct ast_user_list { struct iax2_user *users; ast_mutex_t lock; @@ -673,6 +759,7 @@ static void reg_source_db(struct iax2_peer *p); static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin); +static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin); static void destroy_peer(struct iax2_peer *peer); static int ast_cli_netstats(int fd, int limit_fmt); @@ -707,7 +794,7 @@ vsnprintf(buf, 1024, fmt, args); va_end(args); - ast_log(LOG_ERROR, buf); + ast_log(LOG_ERROR, "%s", buf); } static void jb_warning_output(const char *fmt, ...) @@ -719,7 +806,7 @@ vsnprintf(buf, 1024, fmt, args); va_end(args); - ast_log(LOG_WARNING, buf); + ast_log(LOG_WARNING, "%s", buf); } static void jb_debug_output(const char *fmt, ...) @@ -731,7 +818,7 @@ vsnprintf(buf, 1024, fmt, args); va_end(args); - ast_verbose(buf); + ast_verbose("%s", buf); } #endif @@ -739,7 +826,6 @@ /* XXX We probably should use a mutex when working with this XXX */ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS]; static ast_mutex_t iaxsl[IAX_MAX_CALLS]; -static struct timeval lastused[IAX_MAX_CALLS]; /*! * \brief Another container of iax2_pvt structures @@ -782,6 +868,9 @@ static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); static int iax2_transfer(struct ast_channel *c, const char *dest); static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan); +static struct callno_entry *get_unused_callno(int trunk, int validated); +static int replace_callno(void *obj); +static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry); static const struct ast_channel_tech iax2_tech = { .type = channeltype, @@ -905,6 +994,21 @@ return csub; } +static struct iax2_user *find_user(const char *name, int realtime) +{ + struct iax2_user *user; + ast_mutex_lock(&userl.lock); + for(user = userl.users; user; user = user->next) { + if (!strcasecmp(user->name, name)) { + break; + } + } + ast_mutex_unlock(&userl.lock); + if(!user && realtime) + user = realtime_user(name, NULL); + return user; +} + static struct iax2_peer *find_peer(const char *name, int realtime) { struct iax2_peer *peer; @@ -1036,7 +1140,6 @@ retry: ast_mutex_lock(&iaxsl[callno]); pvt = iaxs[callno]; - gettimeofday(&lastused[callno], NULL); if (pvt) owner = pvt->owner; @@ -1096,6 +1199,9 @@ iax2_destroy_helper(pvt); + sched_delay_remove(&pvt->addr, pvt->callno_entry); + pvt->callno_entry = NULL; + if (pvt->bridgetrans) ast_translator_free_path(pvt->bridgetrans); pvt->bridgetrans = NULL; @@ -1173,9 +1279,19 @@ return new; } -#define NEW_PREVENT 0 -#define NEW_ALLOW 1 -#define NEW_FORCE 2 +/* keep these defined in this order. They are used in find_callno to + * determine whether or not a new call number should be allowed. */ +enum { + /* do not allow a new call number, only search ones in use for match */ + NEW_PREVENT = 0, + /* search for match first, then allow a new one to be allocated */ + NEW_ALLOW = 1, + /* do not search for match, force a new call number */ + NEW_FORCE = 2, + /* do not search for match, force a new call number. Signifies call number + * has been calltoken validated */ + NEW_ALLOW_CALLTOKEN_VALIDATED = 3, +}; static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur, int check_dcallno) { @@ -1215,7 +1331,7 @@ { int x; int res= 0; - struct timeval now; + struct callno_entry *callno_entry; if (iaxs[callno]->oseqno) { ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n"); return -1; @@ -1224,46 +1340,642 @@ ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno); return -1; } - gettimeofday(&now, NULL); - for (x=TRUNK_CALL_START;x MIN_REUSE_TIME)) { - iaxs[x] = iaxs[callno]; - iaxs[x]->callno = x; - iaxs[callno] = NULL; - /* Update the two timers that should have been started */ - if (iaxs[x]->pingid > -1) - ast_sched_del(sched, iaxs[x]->pingid); - if (iaxs[x]->lagid > -1) - ast_sched_del(sched, iaxs[x]->lagid); - iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x); - iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x); - if (locked) - ast_mutex_unlock(&iaxsl[callno]); - res = x; - if (!locked) - ast_mutex_unlock(&iaxsl[x]); - break; - } - ast_mutex_unlock(&iaxsl[x]); - } - if (x >= IAX_MAX_CALLS - 1) { + + if (!(callno_entry = get_unused_callno(1, iaxs[callno]->callno_entry->validated))) { ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n"); return -1; } - ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x); + x = callno_entry->callno; + ast_mutex_lock(&iaxsl[x]); + + /*! + * \note We delete these before switching the slot, because if + * they fire in the meantime, they will generate a warning. + */ + iaxs[x] = iaxs[callno]; + iaxs[x]->callno = x; + /* since we copied over the pvt from a different callno, make sure the old entry is replaced + * before assigning the new one */ + if (iaxs[x]->callno_entry) { + ast_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, iaxs[x]->callno_entry); + } + iaxs[x]->callno_entry = callno_entry; + iaxs[callno] = NULL; + /* Update the two timers that should have been started */ + if (iaxs[x]->pingid > -1) + ast_sched_del(sched, iaxs[x]->pingid); + if (iaxs[x]->lagid > -1) + ast_sched_del(sched, iaxs[x]->lagid); + iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x); + iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x); + if (locked) + ast_mutex_unlock(&iaxsl[callno]); + res = x; + if (!locked) + ast_mutex_unlock(&iaxsl[x]); + + if (option_debug) + ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x); + /* We move this call from a non-trunked to a trunked call */ update_max_trunk(); update_max_nontrunk(); return res; } +static int addr_range_delme_cb(void *obj, void *arg, int flags) +{ + struct addr_range *lim = obj; + lim->delme = 1; + return 0; +} + +static int addr_range_hash_cb(const void *obj, const int flags) +{ + const struct addr_range *lim = obj; + return abs((int) lim->ha.netaddr.s_addr); +} + +static int addr_range_cmp_cb(void *obj, void *arg, int flags) +{ + struct addr_range *lim1 = obj, *lim2 = arg; + return ((lim1->ha.netaddr.s_addr == lim2->ha.netaddr.s_addr) && + (lim1->ha.netmask.s_addr == lim2->ha.netmask.s_addr)) ? + CMP_MATCH | CMP_STOP : 0; +} + +static int peercnt_hash_cb(const void *obj, const int flags) +{ + const struct peercnt *peercnt = obj; + return abs((int) peercnt->addr); +} + +static int peercnt_cmp_cb(void *obj, void *arg, int flags) +{ + struct peercnt *peercnt1 = obj, *peercnt2 = arg; + return (peercnt1->addr == peercnt2->addr) ? CMP_MATCH | CMP_STOP : 0; +} + +static int addr_range_match_address_cb(void *obj, void *arg, int flags) +{ + struct addr_range *addr_range = obj; + struct sockaddr_in *sin = arg; + + if ((sin->sin_addr.s_addr & addr_range->ha.netmask.s_addr) == addr_range->ha.netaddr.s_addr) { + return CMP_MATCH | CMP_STOP; + } + return 0; +} + +/*! + * \internal + * + * \brief compares sin to calltoken_ignores table to determine if validation is required. + */ +static int calltoken_required(struct sockaddr_in *sin, const char *name, int subclass) +{ + struct addr_range *addr_range; + struct iax2_peer *peer = NULL; + struct iax2_user *user = NULL; + /* if no username is given, check for guest accounts */ + const char *find = ast_strlen_zero(name) ? "guest" : name; + int res = 1; /* required by default */ + int optional = 0; + enum calltoken_peer_enum calltoken_required = CALLTOKEN_DEFAULT; + char iabuf[INET_ADDRSTRLEN]; + /* There are only two cases in which calltoken validation is not required. + * Case 1. sin falls within the list of address ranges specified in the calltoken optional table and + * the peer definition has not set the requirecalltoken option. + * Case 2. Username is a valid peer/user, and that peer has requirecalltoken set either auto or no. + */ + + /* ----- Case 1 ----- */ + if ((addr_range = ao2_callback(calltoken_ignores, 0, addr_range_match_address_cb, sin))) { + ao2_ref(addr_range, -1); + optional = 1; + } + + /* ----- Case 2 ----- */ + if ((subclass == IAX_COMMAND_NEW) && (user = find_user(find, 1))) { + calltoken_required = user->calltoken_required; + } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(find, 1))) { + calltoken_required = peer->calltoken_required; + } + + if (option_debug) { + ast_log(LOG_DEBUG, "Determining if address %s with username %s requires calltoken validation. Optional = %d calltoken_required = %d \n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), name, optional, calltoken_required); + } + if (((calltoken_required == CALLTOKEN_NO) || (calltoken_required == CALLTOKEN_AUTO)) || + (optional && (calltoken_required == CALLTOKEN_DEFAULT))) { + res = 0; + } + + return res; +} + +/*! + * \internal + * + * \brief set peercnt callno limit. + * + * \details + * First looks in custom definitions. If not found, global limit + * is used. Entries marked as reg already have + * a custom limit set by a registration and are not modified. + */ +static void set_peercnt_limit(struct peercnt *peercnt) +{ + uint16_t limit = global_maxcallno; + struct addr_range *addr_range; + struct sockaddr_in sin = { + .sin_addr.s_addr = peercnt->addr, + }; + char iabuf[INET_ADDRSTRLEN]; + + + if (peercnt->reg && peercnt->limit) { + return; /* this peercnt has a custom limit set by a registration */ + } + + if ((addr_range = ao2_callback(callno_limits, 0, addr_range_match_address_cb, &sin))) { + limit = addr_range->limit; + if (option_debug) { + ast_log(LOG_NOTICE, "custom addr_range %d found for %s\n", limit, ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr)); + } + ao2_ref(addr_range, -1); + } + + peercnt->limit = limit; +} + +/*! + * \internal + * \brief sets limits for all peercnts in table. done on reload to reflect changes in conf. + */ +static int set_peercnt_limit_all_cb(void *obj, void *arg, int flags) +{ + struct peercnt *peercnt = obj; + + set_peercnt_limit(peercnt); + if (option_debug) { + ast_log(LOG_NOTICE, "Reset limits for peercnts table\n"); + } + return 0; +} + +/*! + * \internal + * \brief returns match if delme is set. + */ +static int prune_addr_range_cb(void *obj, void *arg, int flags) +{ + struct addr_range *addr_range = obj; + + return addr_range->delme ? CMP_MATCH : 0; +} + +/*! + * \internal + * \brief modifies peercnt entry in peercnts table. Used to set custom limit or mark a registered ip + */ +static void peercnt_modify(unsigned char reg, uint16_t limit, struct sockaddr_in *sin) +{ + /* this function turns off and on custom callno limits set by peer registration */ + struct peercnt *peercnt; + struct peercnt tmp = { + .addr = sin->sin_addr.s_addr, + }; + char iabuf[INET_ADDRSTRLEN]; + + if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) { + peercnt->reg = reg; + if (limit) { + peercnt->limit = limit; + } else { + set_peercnt_limit(peercnt); + } + if (option_debug) { + ast_log(LOG_NOTICE, "peercnt entry %s modified limit:%d registered:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), peercnt->limit, peercnt->reg); + } + ao2_ref(peercnt, -1); /* decrement ref from find */ + } +} + +/*! + * \internal + * \brief adds an ip to the peercnts table, increments connection count if it already exists + * + * \details First searches for the address in the peercnts table. If found + * the current count is incremented. If not found a new peercnt is allocated + * and linked into the peercnts table with a call number count of 1. + */ +static int peercnt_add(struct sockaddr_in *sin) +{ + struct peercnt *peercnt; + unsigned long addr = sin->sin_addr.s_addr; + int res = 0; + struct peercnt tmp = { + .addr = addr, + }; + char iabuf[INET_ADDRSTRLEN]; + + /* Reasoning for peercnts container lock: Two identical ip addresses + * could be added by different threads at the "same time". Without the container + * lock, both threads could alloc space for the same object and attempt + * to link to table. With the lock, one would create the object and link + * to table while the other would find the already created peercnt object + * rather than creating a new one. */ + ao2_lock(peercnts); + if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) { + ao2_lock(peercnt); + } else if ((peercnt = ao2_alloc(sizeof(*peercnt), NULL))) { + ao2_lock(peercnt); + /* create and set defaults */ + peercnt->addr = addr; + set_peercnt_limit(peercnt); + /* guarantees it does not go away after unlocking table + * ao2_find automatically adds this */ + ao2_link(peercnts, peercnt); + } else { + ao2_unlock(peercnts); + return -1; + } + + /* check to see if the address has hit its callno limit. If not increment cur. */ + if (peercnt->limit > peercnt->cur) { + peercnt->cur++; + if (option_debug) { + ast_log(LOG_NOTICE, "ip callno count incremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); + } + } else { /* max num call numbers for this peer has been reached! */ + ast_log(LOG_ERROR, "maxcallnumber limit of %d for %s has been reached!\n", peercnt->limit, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); + res = -1; + } + + /* clean up locks and ref count */ + ao2_unlock(peercnt); + ao2_unlock(peercnts); + ao2_ref(peercnt, -1); /* decrement ref from find/alloc, only the container ref remains. */ + + return res; +} + +/*! + * \internal + * \brief decrements a peercnts table entry + */ +static void peercnt_remove(struct peercnt *peercnt) +{ + struct sockaddr_in sin = { + .sin_addr.s_addr = peercnt->addr, + }; + char iabuf[INET_ADDRSTRLEN]; + + if (peercnt) { + /* Container locked here since peercnt may be unlinked from list. If left unlocked, + * peercnt_add could try and grab this entry from the table and modify it at the + * "same time" this thread attemps to unlink it.*/ + ao2_lock(peercnts); + peercnt->cur--; + if (option_debug) { + ast_log(LOG_NOTICE, "ip callno count decremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr)); + } + /* if this was the last connection from the peer remove it from table */ + if (peercnt->cur == 0) { + ao2_unlink(peercnts, peercnt);/* decrements ref from table, last ref is left to scheduler */ + } + ao2_unlock(peercnts); + } +} + +/*! + * \internal + * \brief called by scheduler to decrement object + */ +static int peercnt_remove_cb(void *obj) +{ + struct peercnt *peercnt = (struct peercnt *) obj; + + peercnt_remove(peercnt); + ao2_ref(peercnt, -1); /* decrement ref from scheduler */ + + return 0; +} + +/*! + * \internal + * \brief decrements peercnts connection count, finds by addr + */ +static int peercnt_remove_by_addr(struct sockaddr_in *sin) +{ + struct peercnt *peercnt; + struct peercnt tmp = { + .addr = sin->sin_addr.s_addr, + }; + + if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) { + peercnt_remove(peercnt); + ao2_ref(peercnt, -1); /* decrement ref from find */ + } + return 0; +} + +/*! + * \internal + * \brief Create callno_limit entry based on configuration + */ +static void build_callno_limits(struct ast_variable *v) +{ + struct addr_range *addr_range = NULL; + struct addr_range tmp; + struct ast_ha *ha; + int limit; + int found; + + for (; v; v = v->next) { + limit = -1; + found = 0; + ha = ast_append_ha("permit", v->name, NULL); + + /* check for valid config information */ + if (!ha) { + ast_log(LOG_ERROR, "Call number limit for %s could not be added, Invalid address range\n.", v->name); + continue; + } else if ((sscanf(v->value, "%d", &limit) != 1) || (limit < 0)) { + ast_log(LOG_ERROR, "Call number limit for %s could not be added. Invalid limit %s\n.", v->name, v->value); + ast_free_ha(ha); + continue; + } + + ast_copy_ha(ha, &tmp.ha); + /* find or create the addr_range */ + if ((addr_range = ao2_find(callno_limits, &tmp, OBJ_POINTER))) { + ao2_lock(addr_range); + found = 1; + } else if (!(addr_range = ao2_alloc(sizeof(*addr_range), NULL))) { + ast_free_ha(ha); + return; /* out of memory */ + } + + /* copy over config data into addr_range object */ + ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible for each limit */ + ast_free_ha(ha); /* cleanup the tmp ha */ + addr_range->limit = limit; + addr_range->delme = 0; + + /* cleanup */ + if (found) { + ao2_unlock(addr_range); + } else { + ao2_link(callno_limits, addr_range); + } + ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */ + } +} + +/*! + * \internal + * \brief Create calltoken_ignores entry based on configuration + */ +static int add_calltoken_ignore(const char *addr) +{ + struct addr_range tmp; + struct addr_range *addr_range = NULL; + struct ast_ha *ha = NULL; + + if (ast_strlen_zero(addr)) { + ast_log(LOG_WARNING, "invalid calltokenoptional %s\n", addr); + return -1; + } + + ha = ast_append_ha("permit", addr, NULL); + + /* check for valid config information */ + if (!ha) { + ast_log(LOG_WARNING, "Error creating calltokenoptional entry %s\n", addr); + return -1; + } + + ast_copy_ha(ha, &tmp.ha); + /* find or create the addr_range */ + if ((addr_range = ao2_find(calltoken_ignores, &tmp, OBJ_POINTER))) { + ao2_lock(addr_range); + addr_range->delme = 0; + ao2_unlock(addr_range); + } else if ((addr_range = ao2_alloc(sizeof(*addr_range), NULL))) { + /* copy over config data into addr_range object */ + ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible */ + ao2_link(calltoken_ignores, addr_range); + } else { + ast_free_ha(ha); + return -1; + } + + ast_free_ha(ha); + ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */ + + return 0; +} + +static int iax2_show_callnumber_usage(int fd, int argc, char *argv[]) +{ + struct ao2_iterator i; + struct peercnt *peercnt; + struct sockaddr_in sin; + int found = 0; + char iabuf[INET_ADDRSTRLEN]; + + if (argc < 4 || argc > 5) + return RESULT_SHOWUSAGE; + + ast_cli(fd, "%-15s %-12s %-12s\n", "Address", "Callno Usage", "Callno Limit"); + i = ao2_iterator_init(peercnts, 0); + while ((peercnt = ao2_iterator_next(&i))) { + sin.sin_addr.s_addr = peercnt->addr; + if (argc == 5 && (!strcasecmp(argv[4], ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr)))) { + ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), peercnt->cur, peercnt->limit); + found = 1; + break; + } else { + ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), peercnt->cur, peercnt->limit); + } + ao2_ref(peercnt, -1); + } + if (argc == 4) { + ast_cli(fd, "\nNon-CallToken Validation Limit: %d\nNon-CallToken Validated: %d\n", global_maxcallno_nonval, total_nonval_callno_used); + } else if (argc == 5 && !found) { + ast_cli(fd, "No callnumber table entries for %s found\n", argv[4] ); + } + return RESULT_SUCCESS; +} + +static struct callno_entry *get_unused_callno(int trunk, int validated) +{ + struct callno_entry *callno_entry = NULL; + if ((!ao2_container_count(callno_pool) && !trunk) || (!ao2_container_count(callno_pool_trunk) && trunk)) { + ast_log(LOG_WARNING, "Out of CallNumbers\n"); + /* Minor optimization for the extreme case. */ + return NULL; + } + + /* the callno_pool container is locked here primarily to ensure thread + * safety of the total_nonval_callno_used check and increment */ + ao2_lock(callno_pool); + + /* only a certain number of nonvalidated call numbers should be allocated. + * If there ever is an attack, this separates the calltoken validating + * users from the non calltoken validating users. */ + if (!validated && (total_nonval_callno_used >= global_maxcallno_nonval)) { + ast_log(LOG_WARNING, "NON-CallToken callnumber limit is reached. Current:%d Max:%d\n", total_nonval_callno_used, global_maxcallno_nonval); + ao2_unlock(callno_pool); + return NULL; + } + + /* unlink the object from the container, taking over ownership + * of the reference the container had to the object */ + callno_entry = ao2_find((trunk ? callno_pool_trunk : callno_pool), NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE); + + if (callno_entry) { + callno_entry->validated = validated; + if (!validated) { + total_nonval_callno_used++; + } + } + + ao2_unlock(callno_pool); + return callno_entry; +} + +static int replace_callno(void *obj) +{ + struct callno_entry *callno_entry = (struct callno_entry *) obj; + + /* the callno_pool container is locked here primarily to ensure thread + * safety of the total_nonval_callno_used check and decrement */ + ao2_lock(callno_pool); + + if (!callno_entry->validated && (total_nonval_callno_used != 0)) { + total_nonval_callno_used--; + } else if (!callno_entry->validated && (total_nonval_callno_used == 0)) { + ast_log(LOG_ERROR, "Attempted to decrement total non calltoken validated callnumbers below zero... Callno is:%d \n", callno_entry->callno); + } + + if (callno_entry->callno < TRUNK_CALL_START) { + ao2_link(callno_pool, callno_entry); + } else { + ao2_link(callno_pool_trunk, callno_entry); + } + ao2_ref(callno_entry, -1); /* only container ref remains */ + + ao2_unlock(callno_pool); + return 0; +} + +static int callno_hash(const void *obj, const int flags) +{ + return abs(rand()); +} + +static int create_callno_pools(void) +{ + uint16_t i; + + if (!(callno_pool = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) { + return -1; + } + + if (!(callno_pool_trunk = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) { + return -1; + } + + /* start at 2, 0 and 1 are reserved */ + for (i = 2; i <= IAX_MAX_CALLS; i++) { + struct callno_entry *callno_entry; + + if (!(callno_entry = ao2_alloc(sizeof(*callno_entry), NULL))) { + return -1; + } + + callno_entry->callno = i; + + if (i < TRUNK_CALL_START) { + ao2_link(callno_pool, callno_entry); + } else { + ao2_link(callno_pool_trunk, callno_entry); + } + + ao2_ref(callno_entry, -1); + } + + return 0; +} + +/*! + * \internal + * \brief Schedules delayed removal of iax2_pvt call number data + * + * \note After MIN_REUSE_TIME has passed for a destroyed iax2_pvt, the callno is + * avaliable again, and the address from the previous connection must be decremented + * from the peercnts table. This function schedules these operations to take place. + */ +static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry) +{ + int i; + struct peercnt *peercnt; + struct peercnt tmp = { + .addr = sin->sin_addr.s_addr, + }; + char iabuf[INET_ADDRSTRLEN]; + + if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) { + /* refcount is incremented with ao2_find. keep that ref for the scheduler */ + if (option_debug) { + ast_log(LOG_NOTICE, "schedule decrement of callno used for %s in %d seconds\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), MIN_REUSE_TIME); + } + i = ast_sched_add(sched, MIN_REUSE_TIME * 1000, peercnt_remove_cb, peercnt); + if (i == -1) { + ao2_ref(peercnt, -1); + } + } + + ast_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, callno_entry); +} + +/*! + * \internal + * \brief returns whether or not a frame is capable of starting a new IAX2 dialog. + * + * \note For this implementation, inbound pokes should _NOT_ be capable of allocating + * a new callno. + */ +static inline int iax2_allow_new(int frametype, int subclass, int inbound) +{ + if (frametype != AST_FRAME_IAX) { + return 0; + } + switch (subclass) { + case IAX_COMMAND_NEW: + case IAX_COMMAND_REGREQ: + case IAX_COMMAND_FWDOWNL: + case IAX_COMMAND_REGREL: + return 1; + case IAX_COMMAND_POKE: + if (!inbound) { + return 1; + } + break; + } + return 0; +} + static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd, int check_dcallno) { int res = 0; int x; - struct timeval now; char iabuf[INET_ADDRSTRLEN]; + /* this call is calltoken validated as long as it is either NEW_FORCE + * or NEW_ALLOW_CALLTOKEN_VALIDATED */ + int validated = (new > NEW_ALLOW) ? 1 : 0; char host[80]; if (new <= NEW_ALLOW) { if (callno) { @@ -1308,8 +2020,7 @@ } } if ((res < 1) && (new >= NEW_ALLOW)) { - int start, found = 0; - + struct callno_entry *callno_entry; /* It may seem odd that we look through the peer list for a name for * this *incoming* call. Well, it is weird. However, users don't * have an IP address/port number that we can match against. So, @@ -1319,37 +2030,29 @@ if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer)) snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port)); - gettimeofday(&now, NULL); - start = 2 + (rand() % (TRUNK_CALL_START - 1)); - for (x = start; 1; x++) { - if (x == TRUNK_CALL_START) { - x = 1; - continue; - } + if (peercnt_add(sin)) { + /* This address has hit its callnumber limit. When the limit + * is reached, the connection is not added to the peercnts table.*/ + return 0; + } - /* Find first unused call number that hasn't been used in a while */ - ast_mutex_lock(&iaxsl[x]); - if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) { - found = 1; - break; - } - ast_mutex_unlock(&iaxsl[x]); - - if (x == start - 1) { - break; - } - } - /* We've still got lock held if we found a spot */ - if (x == start - 1 && !found) { + if (!(callno_entry = get_unused_callno(0, validated))) { + /* since we ran out of space, remove the peercnt + * entry we added earlier */ + peercnt_remove_by_addr(sin); ast_log(LOG_WARNING, "No more space\n"); return 0; } + x = callno_entry->callno; + ast_mutex_lock(&iaxsl[x]); + iaxs[x] = new_iax(sin, lockpeer, host); update_max_nontrunk(); if (iaxs[x]) { if (option_debug && iaxdebug) ast_log(LOG_DEBUG, "Creating new call structure %d\n", x); + iaxs[x]->callno_entry = callno_entry; iaxs[x]->sockfd = sockfd; iaxs[x]->addr.sin_port = sin->sin_port; iaxs[x]->addr.sin_family = sin->sin_family; @@ -1369,6 +2072,7 @@ } else { ast_log(LOG_WARNING, "Out of resources\n"); ast_mutex_unlock(&iaxsl[x]); + replace_callno(callno_entry); return 0; } ast_mutex_unlock(&iaxsl[x]); @@ -2112,6 +2816,8 @@ ast_cli(fd, " Context : %s\n", peer->context); ast_cli(fd, " Mailbox : %s\n", peer->mailbox); ast_cli(fd, " Dynamic : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes":"No"); + ast_cli(fd, " Callnum limit: %d\n", peer->maxcallno); + ast_cli(fd, " Calltoken req: %s\n", (peer->calltoken_required == CALLTOKEN_YES) ? "Yes" : ((peer->calltoken_required == CALLTOKEN_AUTO) ? "Auto" : "No")); ast_cli(fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "")); ast_cli(fd, " Expire : %d\n", peer->expire); ast_cli(fd, " ACL : %s\n", (peer->ha?"Yes":"No")); @@ -3104,16 +3810,251 @@ static int send_apathetic_reply(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int command, int ts, unsigned char seqno, - int sockfd) + int sockfd, struct iax_ie_data *ied) { - struct ast_iax2_full_hdr f = { .scallno = htons(0x8000 | callno), .dcallno = htons(dcallno), - .ts = htonl(ts), .iseqno = seqno, .oseqno = 0, .type = AST_FRAME_IAX, - .csub = compress_subclass(command) }; + struct { + struct ast_iax2_full_hdr f; + struct iax_ie_data ied; + } data; + size_t size = sizeof(struct ast_iax2_full_hdr); - return sendto(sockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin)); + if (ied) { + size += ied->pos; + memcpy(&data.ied, ied->buf, ied->pos); + } + + data.f.scallno = htons(0x8000 | callno); + data.f.dcallno = htons(dcallno); + data.f.ts = htonl(ts); + data.f.iseqno = seqno; + data.f.oseqno = 0; + data.f.type = AST_FRAME_IAX; + data.f.csub = compress_subclass(command); + + return sendto(sockfd, &data, size, 0, (struct sockaddr *)sin, sizeof(*sin)); } +static void add_empty_calltoken_ie(struct chan_iax2_pvt *pvt, struct iax_ie_data *ied) +{ + /* first make sure their are two empty bytes left in ied->buf */ + if (pvt && ied && (2 < ((int) sizeof(ied->buf) - ied->pos))) { + ied->buf[ied->pos++] = IAX_IE_CALLTOKEN; /* type */ + ied->buf[ied->pos++] = 0; /* data size, ZERO in this case */ + pvt->calltoken_ie_len = 2; + } +} + +static void resend_with_token(int callno, struct iax_frame *f, char *newtoken) +{ + struct chan_iax2_pvt *pvt = iaxs[callno]; + int frametype = f->af.frametype; + int subclass = f->af.subclass; + struct { + struct ast_iax2_full_hdr fh; + struct iax_ie_data ied; + } data = { + .ied.buf = { 0 }, + .ied.pos = 0, + }; + /* total len - header len gives us the frame's IE len */ + int ie_data_pos = f->datalen - sizeof(struct ast_iax2_full_hdr); + + if (!pvt) { + return; /* this should not be possible if called from socket_process() */ + } + + /* + * Check to make sure last frame sent is valid for call token resend + * 1. Frame should _NOT_ already have a destination callno + * 2. Frame must be a valid iax_frame subclass capable of starting dialog + * 3. Pvt must have a calltoken_ie_len which represents the number of + * bytes at the end of the frame used for the previous calltoken ie. + * 4. Pvt's calltoken_ie_len must be _LESS_ than the total IE length + * 5. Total length of f->data must be _LESS_ than size of our data struct + * because f->data must be able to fit within data. + */ + if (f->dcallno || !iax2_allow_new(frametype, subclass, 0) + || !pvt->calltoken_ie_len || (pvt->calltoken_ie_len > ie_data_pos) || + (f->datalen > sizeof(data))) { + + return; /* ignore resend, token was not valid for the dialog */ + } + + /* token is valid + * 1. Copy frame data over + * 2. Redo calltoken IE, it will always be the last ie in the frame. + * NOTE: Having the ie always be last is not protocol specified, + * it is only an implementation choice. Since we only expect the ie to + * be last for frames we have sent, this can no way be affected by + * another end point. + * 3. Remove frame from queue + * 4. Free old frame + * 5. Clear previous seqnos + * 6. Resend with CALLTOKEN ie. + */ + + /* ---1.--- */ + memcpy(&data, f->data, f->datalen); + data.ied.pos = ie_data_pos; + + /* ---2.--- */ + /* move to the beginning of the calltoken ie so we can write over it */ + data.ied.pos -= pvt->calltoken_ie_len; + iax_ie_append_str(&data.ied, IAX_IE_CALLTOKEN, newtoken); + + /* make sure to update token length incase it ever has to be stripped off again */ + pvt->calltoken_ie_len = data.ied.pos - ie_data_pos; /* new pos minus old pos tells how big token ie is */ + + /* ---3.--- */ + ast_mutex_lock(&iaxq.lock); + if (f->prev) + f->prev->next = f->next; + else + iaxq.head = f->next; + if (f->next) + f->next->prev = f->prev; + else + iaxq.tail = f->prev; + iaxq.count--; + ast_mutex_unlock(&iaxq.lock); + + /* ---4.--- */ + iax2_frame_free(f); + + /* ---5.--- */ + pvt->oseqno = 0; + pvt->rseqno = 0; + pvt->iseqno = 0; + pvt->aseqno = 0; + if (pvt->peercallno) { + remove_by_peercallno(pvt); + pvt->peercallno = 0; + } + + /* ---6.--- */ + send_command(pvt, AST_FRAME_IAX, subclass, 0, data.ied.buf, data.ied.pos, -1); +} + +static void requirecalltoken_mark_auto(const char *name, int subclass) +{ + struct iax2_user *user = NULL; + struct iax2_peer *peer = NULL; + + if (ast_strlen_zero(name)) { + return; /* no username given */ + } + + if ((subclass == IAX_COMMAND_NEW) && (user = find_user(name, 1)) && (user->calltoken_required == CALLTOKEN_AUTO)) { + user->calltoken_required = CALLTOKEN_YES; + } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(name, 1)) && (peer->calltoken_required == CALLTOKEN_AUTO)) { + peer->calltoken_required = CALLTOKEN_YES; + } +} + /*! + * \internal + * + * \brief handles calltoken logic for a received iax_frame. + * + * \note frametype must be AST_FRAME_IAX. + * + * \note + * Three different cases are possible here. + * Case 1. An empty calltoken is provided. This means the client supports + * calltokens but has not yet received one from us. In this case + * a full calltoken IE is created and sent in a calltoken fullframe. + * Case 2. A full calltoken is received and must be checked for validity. + * Case 3. No calltoken is received indicating that the client does not + * support calltokens. In this case it is up to the configuration + * to decide how this should be handled (reject or permit without calltoken) + */ +static int handle_call_token(struct ast_iax2_full_hdr *fh, struct iax_ies *ies, + struct sockaddr_in *sin, int fd) +{ +#define CALLTOKEN_HASH_FORMAT "%s%d%u%d" /* address + port + ts + randomcalldata */ +#define CALLTOKEN_IE_FORMAT "%u?%s" /* time + ? + (40 char hash) */ + char buf[256] = { 0 }; + time_t t = time(NULL); + char hash[41]; /* 40 char sha1 hash */ + int subclass = uncompress_subclass(fh->csub); + char iabuf[INET_ADDRSTRLEN]; + + /* ----- Case 1 ----- */ + if (ies->calltoken && !ies->calltokendata) { /* empty calltoken is provided, client supports calltokens */ + struct iax_ie_data ied = { + .buf = { 0 }, + .pos = 0, + }; + + /* create the hash with their address data and our timestamp */ + snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), sin->sin_port, (unsigned int) t, randomcalltokendata); + ast_sha1_hash(hash, buf); + + snprintf(buf, sizeof(buf), CALLTOKEN_IE_FORMAT, (unsigned int) t, hash); + iax_ie_append_str(&ied, IAX_IE_CALLTOKEN, buf); + send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_CALLTOKEN, ntohl(fh->ts), fh->iseqno + 1, fd, &ied); + + return 1; + + /* ----- Case 2 ----- */ + } else if (ies->calltoken && ies->calltokendata) { /* calltoken received, check to see if it is valid */ + char *rec_hash = NULL; /* the received hash, make sure it matches with ours. */ + char *rec_ts = NULL; /* received timestamp */ + unsigned int rec_time; /* received time_t */ + + /* split the timestamp from the hash data */ + rec_hash = strchr((char *) ies->calltokendata, '?'); + if (rec_hash) { + *rec_hash++ = '\0'; + rec_ts = (char *) ies->calltokendata; + } + + /* check that we have valid data before we do any comparisons */ + if (!rec_hash || !rec_ts) { + goto reject; + } else if (sscanf(rec_ts, "%u", &rec_time) != 1) { + goto reject; + } + + /* create a hash with their address and the _TOKEN'S_ timestamp */ + snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), sin->sin_port, (unsigned int) rec_time, randomcalltokendata); + ast_sha1_hash(hash, buf); + + /* compare hashes and then check timestamp delay */ + if (strcmp(hash, rec_hash)) { + ast_log(LOG_WARNING, "Address %s failed CallToken hash inspection\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); + goto reject; /* received hash does not match ours, reject */ + } else if ((t < rec_time) || ((t - rec_time) >= MAX_CALLTOKEN_DELAY)) { + ast_log(LOG_WARNING, "Too much delay in IAX2 calltoken timestamp from address %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); + goto reject; /* too much delay, reject */ + } + + /* at this point the call token is valid, returning 0 + * will allow socket_process to continue as usual */ + requirecalltoken_mark_auto(ies->username, subclass); + return 0; + + /* ----- Case 3 ----- */ + } else { /* calltokens are not supported for this client, how do we respond? */ + if (calltoken_required(sin, ies->username, subclass)) { + ast_log(LOG_ERROR, "Call rejected, CallToken Support required. If unexpected, resolve by placing address %s in the calltokenignore list or setting user %s requirecalltoken=no\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ast_strlen_zero(ies->username) ? "guest" : ies->username); + goto reject; + } + return 0; /* calltoken is not required for this addr, so permit it. */ + } + +reject: + /* received frame has failed calltoken inspection, send apathetic reject messages */ + if (subclass == IAX_COMMAND_REGREQ || subclass == IAX_COMMAND_REGREL) { + send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL); + } else { + send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL); + } + + return 1; +} + +/*! * \brief Parses an IAX dial string into its component parts. * \param data the string to be parsed * \param pds pointer to a \c struct \c parsed_dial_string to be filled in @@ -3297,6 +4238,7 @@ iaxs[callno]->sockfd = cai.sockfd; /* Transmit the string in a "NEW" request */ + add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); ast_mutex_unlock(&iaxsl[callno]); @@ -5719,8 +6661,11 @@ } /* Wake up waiters */ for (x=0;xwaiters) / sizeof(dp->waiters[0]); x++) - if (dp->waiters[x] > -1) - write(dp->waiters[x], "asdf", 4); + if (dp->waiters[x] > -1) { + if (write(dp->waiters[x], "asdf", 4) == -1) { + ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); + } + } } prev = dp; dp = dp->peer; @@ -5742,6 +6687,13 @@ ast_log(LOG_WARNING, "Invalid transfer request\n"); return -1; } + + /* since a transfer has taken place, the address will change. + * This must be accounted for in the peercnts table. Remove + * the old address and add the new one */ + peercnt_remove_by_addr(&pvt->addr); + peercnt_add(&pvt->transfer); + /* now copy over the new address */ memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr)); memset(&pvt->transfer, 0, sizeof(pvt->transfer)); /* Reset sequence numbers */ @@ -5939,6 +6891,8 @@ ast_log(LOG_DEBUG, "Expiring registration for peer '%s'\n", p->name); if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) realtime_update_peer(p->name, &p->addr, 0); + /* modify entry in peercnts table as _not_ registered */ + peercnt_modify(0, 0, &p->addr); /* Reset the address */ memset(&p->addr, 0, sizeof(p->addr)); /* Reset expire notice */ @@ -6031,6 +6985,10 @@ if (inaddrcmp(&p->addr, sin)) { if (iax2_regfunk) iax2_regfunk(p->name, 1); + + /* modify entry in peercnts table as _not_ registered */ + peercnt_modify(0, 0, &p->addr); + /* Stash the IP address from which they registered */ memcpy(&p->addr, sin, sizeof(p->addr)); snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port), p->expiry); @@ -6054,7 +7012,13 @@ /* Update the host */ /* Verify that the host is really there */ iax2_poke_peer(p, callno); - } + } + + /* modify entry in peercnts table as registered */ + if (p->maxcallno) { + peercnt_modify(1, p->maxcallno, &p->addr); + } + /* Store socket fd */ p->sockfd = fd; /* Setup the expiry */ @@ -6182,6 +7146,7 @@ res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL, NULL); if (!res) { reg->regstate = REG_STATE_AUTHSENT; + add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */ return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); } else return -1; @@ -6669,6 +7634,7 @@ void *ptr; socklen_t len = sizeof(sin); int dcallno = 0; + char decrypted = 0; struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)buf; struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)buf; struct ast_iax2_meta_hdr *meta = (struct ast_iax2_meta_hdr *)buf; @@ -6868,6 +7834,25 @@ /* Get the destination call number */ dcallno = ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS; + + + /* check to make sure this full frame isn't encrypted before we attempt + * to look inside of it. If it is encrypted, decrypt it first. Its ok if the + * callno is not found here, that just means one hasn't been allocated for + * this connection yet. */ + if ((dcallno != 1) && (fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, NEW_PREVENT, 1, fd, 1))) { + ast_mutex_lock(&iaxsl[fr->callno]); + if (iaxs[fr->callno] && ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) { + if (decrypt_frame(fr->callno, fh, &f, &res)) { + ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n"); + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } + decrypted = 1; + } + ast_mutex_unlock(&iaxsl[fr->callno]); + } + /* Retrieve the type and subclass */ f.frametype = fh->type; if (f.frametype == AST_FRAME_VIDEO) { @@ -6879,17 +7864,49 @@ /* Deal with POKE/PONG without allocating a callno */ if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) { /* Reply back with a PONG, but don't care about the result. */ - send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohs(fh->ts), fh->iseqno + 1, fd); + send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd, NULL); return 1; } else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) { /* Ignore */ return 1; } + f.datalen = res - sizeof(*fh); + if (f.datalen) { + if (f.frametype == AST_FRAME_IAX) { + if (iax_parse_ies(&ies, buf + sizeof(*fh), f.datalen)) { + ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr)); + return 1; + } + f.data = NULL; + f.datalen = 0; + } else { + f.data = buf + sizeof(*fh); + memset(&ies, 0, sizeof(ies)); + } + } else { + if (f.frametype == AST_FRAME_IAX) + f.data = NULL; + else + f.data = empty; + memset(&ies, 0, sizeof(ies)); + } - if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) || - (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) || - (f.subclass == IAX_COMMAND_REGREL))) - new = NEW_ALLOW; + if (!dcallno && iax2_allow_new(f.frametype, f.subclass, 1)) { + /* only set NEW_ALLOW if calltoken checks out */ + if (handle_call_token(fh, &ies, &sin, fd)) { + return 1; + } + + if (ies.calltoken && ies.calltokendata) { + /* if we've gotten this far, and the calltoken ie data exists, + * then calltoken validation _MUST_ have taken place. If calltoken + * data is provided, it is always validated reguardless of any + * calltokenoptional or requirecalltoken options */ + new = NEW_ALLOW_CALLTOKEN_VALIDATED; + } else { + new = NEW_ALLOW; + } + } } else { /* Don't know anything about it yet */ f.frametype = AST_FRAME_NULL; @@ -6915,7 +7932,14 @@ check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1; } - fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, 1, fd, check_dcallno); + if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, 1, fd, check_dcallno))) { + if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_NEW) { + send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL); + } else if (f.frametype == AST_FRAME_IAX && (f.subclass == IAX_COMMAND_REGREQ || f.subclass == IAX_COMMAND_REGREL)) { + send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL); + } + return 1; + } } if (fr->callno > 0) @@ -6938,17 +7962,19 @@ ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } - if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) { + if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED) && !decrypted) { if (decrypt_frame(fr->callno, fh, &f, &res)) { ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n"); ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } + decrypted = 1; + } #ifdef DEBUG_SUPPORT - else if (iaxdebug) - iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh)); + if (decrypted && iaxdebug) { + iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh)); + } #endif - } /* count this frame */ iaxs[fr->callno]->frames_received++; @@ -7042,12 +8068,6 @@ (f.frametype != AST_FRAME_IAX)) iaxs[fr->callno]->iseqno++; } - /* A full frame */ - if (res < sizeof(*fh)) { - ast_log(LOG_WARNING, "midget packet received (%d of %zd min)\n", res, sizeof(*fh)); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } /* Ensure text frames are NULL-terminated */ if (f.frametype == AST_FRAME_TEXT && buf[res - 1] != '\0') { if (res < sizeof(buf)) @@ -7055,7 +8075,6 @@ else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */ buf[res - 1] = '\0'; } - f.datalen = res - sizeof(*fh); /* Handle implicit ACKing unless this is an INVAL, and only if this is from the real peer, not the transfer peer */ @@ -7111,25 +8130,6 @@ return 1; } - if (f.datalen) { - if (f.frametype == AST_FRAME_IAX) { - if (iax_parse_ies(&ies, buf + sizeof(*fh), f.datalen)) { - ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr)); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - f.data = NULL; - f.datalen = 0; - } else - f.data = buf + sizeof(*fh); - } else { - if (f.frametype == AST_FRAME_IAX) - f.data = NULL; - else - f.data = empty; - memset(&ies, 0, sizeof(ies)); - } - /* when we receive the first full frame for a new incoming channel, it is safe to start the PBX on the channel because we have now completed a 3-way handshake with the peer */ @@ -7992,6 +8992,28 @@ else send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1); break; + case IAX_COMMAND_CALLTOKEN: + { + struct iax_frame *cur; + int found = 0; + + ast_mutex_lock(&iaxq.lock); + for (cur = iaxq.head; cur ; cur = cur->next) { + /* find the last sent frame in our frame queue for this callno. + * There are many things to take into account before resending this frame. + * All of these are taken care of in resend_with_token() */ + if (cur->callno == iaxs[fr->callno]->callno) { + found = 1; + break; + } + } + ast_mutex_unlock(&iaxq.lock); + /* find last sent frame */ + if (cur && found && ies.calltoken && ies.calltokendata) { + resend_with_token(fr->callno, cur, (char *) ies.calltokendata); + } + break; + } default: ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno); memset(&ied0, 0, sizeof(ied0)); @@ -8144,6 +9166,7 @@ memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username); iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh); + add_empty_calltoken_ie(iaxs[reg->callno], &ied); /* this _MUST_ be the last ie added */ send_command(iaxs[reg->callno],AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); reg->regstate = REG_STATE_REGSENT; return 0; @@ -8290,6 +9313,10 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall) { + struct iax_ie_data ied = { + .buf = { 0 }, + .pos = 0, + }; if (!peer->maxms || !peer->addr.sin_addr.s_addr) { /* IF we have no IP, or this isn't to be monitored, return imeediately after clearing things out */ @@ -8317,8 +9344,10 @@ /* Speed up retransmission times */ iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1; iaxs[peer->callno]->peerpoke = peer; - send_command(iaxs[peer->callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1); - + + add_empty_calltoken_ie(iaxs[peer->callno], &ied); /* this _MUST_ be the last ie added */ + send_command(iaxs[peer->callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1); + /* If the host is already unreachable then use the unreachable interval instead */ if (peer->lastms < 0) { peer->pokeexpire = ast_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer); @@ -8677,6 +9706,9 @@ peer->smoothing = 0; peer->pokefreqok = DEFAULT_FREQ_OK; peer->pokefreqnotok = DEFAULT_FREQ_NOTOK; + peer->maxcallno = 0; + peercnt_modify(0, 0, &peer->addr); + peer->calltoken_required = CALLTOKEN_DEFAULT; peer->context[0] = '\0'; peer->peercontext[0] = '\0'; ast_clear_flag(peer, IAX_HASCALLERID); @@ -8803,7 +9835,24 @@ } else ast_log(LOG_WARNING, "Set peer->pokefreqnotok to %d\n", peer->pokefreqnotok); } else if (!strcasecmp(v->name, "timezone")) { ast_copy_string(peer->zonetag, v->value, sizeof(peer->zonetag)); - }/* else if (strcasecmp(v->name,"type")) */ + } else if (!strcasecmp(v->name, "maxcallnumbers")) { + if (sscanf(v->value, "%10hu", &peer->maxcallno) != 1) { + ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno); + } else { + peercnt_modify(1, peer->maxcallno, &peer->addr); + } + } else if (!strcasecmp(v->name, "requirecalltoken")) { + /* default is required unless in optional ip list */ + if (ast_false(v->value)) { + peer->calltoken_required = CALLTOKEN_NO; + } else if (!strcasecmp(v->value, "auto")) { + peer->calltoken_required = CALLTOKEN_AUTO; + } else if (ast_true(v->value)) { + peer->calltoken_required = CALLTOKEN_YES; + } else { + ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno); + } + } /* else if (strcasecmp(v->name,"type")) */ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ v=v->next; } @@ -8871,6 +9920,7 @@ user->prefs = prefs; user->capability = iax2_capability; user->encmethods = iax2_encryption; + user->calltoken_required = CALLTOKEN_DEFAULT; ast_copy_string(user->name, name, sizeof(user->name)); ast_copy_string(user->language, language, sizeof(user->language)); ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP); @@ -8963,7 +10013,18 @@ user->maxauthreq = atoi(v->value); if (user->maxauthreq < 0) user->maxauthreq = 0; - }/* else if (strcasecmp(v->name,"type")) */ + } else if (!strcasecmp(v->name, "requirecalltoken")) { + /* default is required unless in optional ip list */ + if (ast_false(v->value)) { + user->calltoken_required = CALLTOKEN_NO; + } else if (!strcasecmp(v->value, "auto")) { + user->calltoken_required = CALLTOKEN_AUTO; + } else if (ast_true(v->value)) { + user->calltoken_required = CALLTOKEN_YES; + } else { + ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno); + } + } /* else if (strcasecmp(v->name,"type")) */ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ v = v->next; } @@ -9119,7 +10180,7 @@ /*--- set_config: Load configuration */ -static int set_config(char *config_file, int reload) +static int set_config(const char *config_file, int reload) { struct ast_config *cfg; int capability=iax2_capability; @@ -9157,6 +10218,8 @@ min_reg_expire = IAX_DEFAULT_REG_EXPIRE; max_reg_expire = IAX_DEFAULT_REG_EXPIRE; + global_maxcallno = DEFAULT_MAXCALLNO_LIMIT; + global_maxcallno_nonval = DEFAULT_MAXCALLNO_LIMIT_NONVAL; maxauthreq = 0; @@ -9322,7 +10385,19 @@ maxauthreq = atoi(v->value); if (maxauthreq < 0) maxauthreq = 0; - } /*else if (strcasecmp(v->name,"type")) */ + } else if (!strcasecmp(v->name, "maxcallnumbers")) { + if (sscanf(v->value, "%10hu", &global_maxcallno) != 1) { + ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d\n", v->value, v->lineno); + } + } else if (!strcasecmp(v->name, "maxcallnumbers_nonvalidated")) { + if (sscanf(v->value, "%10hu", &global_maxcallno_nonval) != 1) { + ast_log(LOG_WARNING, "maxcallnumbers_nonvalidated must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno); + } + } else if(!strcasecmp(v->name, "calltokenoptional")) { + if (add_calltoken_ignore(v->value)) { + ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno); + } + }/*else if (strcasecmp(v->name,"type")) */ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ v = v->next; } @@ -9357,7 +10432,9 @@ while(cat) { if (strcasecmp(cat, "general")) { utype = ast_variable_retrieve(cfg, cat, "type"); - if (utype) { + if (!strcasecmp(cat, "callnumberlimits")) { + build_callno_limits(ast_variable_browse(cfg, cat)); + } else if (utype) { if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) { user = build_user(cat, ast_variable_browse(cfg, cat), 0); if (user) { @@ -9392,7 +10469,7 @@ static int reload_config(void) { - char *config = "iax.conf"; + static const char config[] = "iax.conf"; struct iax2_registry *reg; struct iax2_peer *peer; ast_copy_string(accountcode, "", sizeof(accountcode)); @@ -9403,9 +10480,16 @@ ast_clear_flag((&globalflags), IAX_USEJITTERBUF); ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF); delete_users(); + ao2_callback(callno_limits, OBJ_NODATA, addr_range_delme_cb, NULL); + ao2_callback(calltoken_ignores, OBJ_NODATA, addr_range_delme_cb, NULL); + set_config(config,1); prune_peers(); prune_users(); + ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL); + ao2_callback(calltoken_ignores, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL); + ao2_callback(peercnts, OBJ_NODATA, set_peercnt_limit_all_cb, NULL); + ast_mutex_lock(®_lock); for (reg = registrations; reg; reg = reg->next) iax2_do_register(reg); @@ -9493,6 +10577,7 @@ if (pds.key) ast_copy_string(iaxs[callno]->outkey, pds.key, sizeof(iaxs[callno]->outkey)); /* Start the call going */ + add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); return callno; @@ -9925,6 +11010,10 @@ "Usage: iax show peer \n" " Display details on specific IAX peer\n"; +static char show_callnumber_usage[] = +"Usage: iax2 show callnumber usage \n" +" Show current entries in the ip Call Number Limit table.\n"; + static char prune_realtime_usage[] = "Usage: iax2 prune realtime [|all]\n" " Prunes object(s) from the cache\n"; @@ -10034,6 +11123,8 @@ "Show defined IAX peers", show_peers_usage }, { { "iax2", "show", "registry", NULL }, iax2_show_registry, "Show IAX registration status", show_reg_usage }, + { { "iax2", "show", "callnumber", "usage", NULL }, iax2_show_callnumber_usage, + "Show current entries in ip Call number limit table", show_callnumber_usage }, { { "iax2", "debug", NULL }, iax2_do_debug, "Enable IAX debugging", debug_usage }, { { "iax2", "trunk", "debug", NULL }, iax2_do_trunk_debug, @@ -10092,11 +11183,16 @@ ast_mutex_destroy(&iaxsl[x]); ao2_ref(iax_peercallno_pvts, -1); + ao2_ref(peercnts, -1); + ao2_ref(callno_limits, -1); + ao2_ref(calltoken_ignores, -1); + ao2_ref(callno_pool, -1); + ao2_ref(callno_pool_trunk, -1); return 0; } -int unload_module() +int unload_module(void) { ast_custom_function_unregister(&iaxpeer_function); return __unload_module(); @@ -10120,15 +11216,60 @@ pvt2->frames_received) ? CMP_MATCH : 0; } +static int load_objects(void) +{ + iax_peercallno_pvts = peercnts = callno_limits = calltoken_ignores = callno_pool = callno_pool_trunk = NULL; + if (!(iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb))) { + goto container_fail; + } else if (!(peercnts = ao2_container_alloc(MAX_PEER_BUCKETS, peercnt_hash_cb, peercnt_cmp_cb))) { + goto container_fail; + } else if (!(callno_limits = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) { + goto container_fail; + } else if (!(calltoken_ignores = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) { + goto container_fail; + } else if (create_callno_pools()) { + goto container_fail; + } + + return 0; + +container_fail: + if (iax_peercallno_pvts) { + ao2_ref(iax_peercallno_pvts, -1); + } + if (peercnts) { + ao2_ref(peercnts, -1); + } + if (callno_limits) { + ao2_ref(callno_limits, -1); + } + if (calltoken_ignores) { + ao2_ref(calltoken_ignores, -1); + } + if (callno_pool) { + ao2_ref(callno_pool, -1); + } + if (callno_pool_trunk) { + ao2_ref(callno_pool_trunk, -1); + } + return -1; +} + /*--- load_module: Load IAX2 module, load configuraiton ---*/ int load_module(void) { - char *config = "iax.conf"; + static const char config[] = "iax.conf"; int res = 0; int x; struct iax2_registry *reg; struct iax2_peer *peer; - + + if (load_objects()) { + return -1; + } + + randomcalltokendata = rand(); + ast_custom_function_register(&iaxpeer_function); iax_set_output(iax_debug_output); @@ -10207,11 +11348,6 @@ ast_netsock_release(outsock); } - iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); - if (!iax_peercallno_pvts) { - res = -1; - } - ast_mutex_lock(®_lock); for (reg = registrations; reg; reg = reg->next) iax2_do_register(reg); @@ -10228,17 +11364,17 @@ return res; } -char *description() +char *description(void) { return (char *) desc; } -int usecount() +int usecount(void) { return usecnt; } -char *key() +char *key(void) { return ASTERISK_GPL_KEY; } Index: channels/iax2.h =================================================================== --- channels/iax2.h (revision 215957) +++ channels/iax2.h (revision 217888) @@ -67,6 +67,7 @@ #define IAX_COMMAND_PROVISION 35 /* Provision device */ #define IAX_COMMAND_FWDOWNL 36 /* Download firmware */ #define IAX_COMMAND_FWDATA 37 /* Firmware Data */ +#define IAX_COMMAND_CALLTOKEN 40 /*! Call number token */ #define IAX_DEFAULT_REG_EXPIRE 60 /* By default require re-registration once per minute */ @@ -127,6 +128,7 @@ #define IAX_IE_RR_DELAY 49 /* Max playout delay for received frames (in ms) u16 */ #define IAX_IE_RR_DROPPED 50 /* Dropped frames (presumably by jitterbuf) u32 */ #define IAX_IE_RR_OOO 51 /* Frames received Out of Order u32 */ +#define IAX_IE_CALLTOKEN 54 /* Call number security token */ #define IAX_AUTH_PLAINTEXT (1 << 0) Index: channels/iax2-parser.c =================================================================== --- channels/iax2-parser.c (revision 215957) +++ channels/iax2-parser.c (revision 217888) @@ -433,7 +433,10 @@ "TRANSFR", "PROVISN", "FWDWNLD", - "FWDATA " + "FWDATA ", + "TXMEDIA", + "RTKEY ", + "CTOKEN ", }; const char *cmds[] = { "(0?)", @@ -873,6 +876,12 @@ ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2)); } break; + case IAX_IE_CALLTOKEN: + if (len) { + ies->calltokendata = (unsigned char *) data + 2; + } + ies->calltoken = 1; + break; default: snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len); outputf(tmp); Index: doc/IAX2-security.txt =================================================================== --- doc/IAX2-security.txt (revision 0) +++ doc/IAX2-security.txt (revision 217888) @@ -0,0 +1,440 @@ + IAX2 Security + + Copyright (c) 2009 - Digium, Inc. + + All Rights Reserved. + + Document Version 1.0 + + 09/03/09 + + Asterisk Development Team + + Table of Contents + + 1. Introduction 3 + + 1.1. Overview 3 + + 2. User Guide 3 + + 2.1. Configuration 3 + + 2.1.1. Quick Start 3 + + 2.1.2. Controlled Networks 4 + + 2.1.2.1. Full Upgrade 4 + + 2.1.2.2. Partial Upgrade 4 + + 2.1.3. Public Networks 4 + + 2.1.3.1. Full Upgrade 4 + + 2.1.3.2. Partial Upgrade 5 + + 2.1.3.3. Guest Access 5 + + 2.2. CLI Commands 6 + + 2.2.1. iax2 show callnumber usage 6 + + 2.2.2. iax2 show peer 6 + + 3. Protocol Modification 6 + + 3.1. Overview 6 + + 3.2. Call Token Validation 7 + + 3.3. Example Message Exchanges 8 + + 3.3.1. Call Setup 8 + + 3.3.2. Call Setup, client does not support CALLTOKEN 8 + + 3.3.3. Call Setup, client supports CALLTOKEN, server does not 9 + + 3.3.4. Call Setup from client that sends invalid token 9 + + 4. Asterisk Implementation 9 + + 4.1. CALLTOKEN IE Payload 9 + + 1. Introduction + +1.1. Overview + + A change has been made to the IAX2 protocol to help mitigate denial of + service attacks. This change is referred to as call token validation. This + change affects how messages are exchanged and is not backwards compatible + for an older client connecting to an updated server, so a number of + options have been provided to disable call token validation as needed for + compatibility purposes. + + In addition to call token validation, Asterisk can now also limit the + number of connections allowed per IP address to disallow one host from + preventing other hosts from making successful connections. These options + are referred to as call number limits. + + For additional details about the configuration options referenced in this + document, see the sample configuration file, iax.conf.sample. For + information regarding the details of the call token validation protocol + modification, see section 3 (Protocol Modification) of this document. + + 2. User Guide + +2.1. Configuration + + 2.1.1. Quick Start + + We strongly recommend that administrators leave the IAX2 security + enhancements in place where possible. However, to bypass the security + enhancements completely and have Asterisk work exactly as it did before, + the following options can be specified in the [general] section of + iax.conf: + + [general] + + ... + + calltokenoptional = 0.0.0.0/0.0.0.0 + + maxcallnumbers = 16382 + + ... + + 2.1.2. Controlled Networks + + This section discusses what needs to be done for an Asterisk server on a + network where no unsolicited traffic will reach the IAX2 service. + + 2.1.2.1. Full Upgrade + + If all IAX2 endpoints have been upgraded, then no changes to configuration + need to be made. + + 2.1.2.2. Partial Upgrade + + If only some of the IAX2 endpoints have been upgraded, then some + configuration changes will need to be made for interoperability. Since + this is for a controlled network, the easiest thing to do is to disable + call token validation completely, as described in section 2.1.1. + + 2.1.3. Public Networks + + This section discusses the use of the IAX2 security functionality on + public networks where it is possible to receive unsolicited IAX2 traffic. + + 2.1.3.1. Full Upgrade + + If all IAX2 endpoints have been upgraded to support call token validation, + then no changes need to be made. However, for enhanced security, the + administrator may adjust call number limits to further reduce the + potential impact of malicious call number consumption. The following + configuration will allow known peers to consume more call numbers than + unknown source IP addresses: + + [general] + + ; By default, restrict call number usage to a low number. + + maxcallnumbers = 16 + + ... + + [callnumberlimits] + + ; For peers with known IP addresses, call number limits can + + ; be set in this section. This limit is per IP address for + + ; addresses that fall in the specified range. + + ; / = + + 192.168.1.0/255.255.255.0 = 1024 + + ... + + [peerA] + + ; Since we won't know the IP address of a dynamic peer until + + ; they register, a max call number limit can be set in a + + ; dynamic peer configuration section. + + Type = peer + + host = dynamic + + maxcallnumbers = 1024 + + ... + + 2.1.3.2. Partial Upgrade + + If only some IAX2 endpoints have been upgraded, or the status of an IAX2 + endpoint is unknown, then call token validation must be disabled to ensure + interoperability. To reduce the potential impact of disabling call token + validation, it should only be disabled for a specific peer or user as + needed. By using the auto option, call token validation will be changed to + required as soon as we determine that the peer supports it. + + [friendA] + + requirecalltoken = auto + + ... + + Note that there are some cases where auto should not be used. For example, + if multiple peers use the same authentication details, and they have not + all upgraded to support call token validation, then the ones that do not + support it will get locked out. Once an upgraded client successfully + completes an authenticated call setup using call token validation, + Asterisk will require it from then on. In that case, it would be better to + set the requirecalltoken option to no. + + 2.1.3.3. Guest Access + + Guest access via IAX2 requires special attention. Given the number of + existing IAX2 endpoints that do not support call token validation, most + systems that allow guest access should do so without requiring call token + validation. + + [guest] + + ; Note that the name "guest" is special here. When the code + + ; tries to determine if call token validation is required, it + + ; will look for a user by the username specified in the + + ; request. Guest calls can be sent without a username. In + + ; that case, we will look for a defined user called "guest" to + + ; determine if call token validation is required or not. + + type = user + + requirecalltoken = no + + ... + + Since disabling call token validation for the guest account allows a huge + hole for malicious call number consumption, an option has been provided to + segregate the call numbers consumed by connections not using call token + validation from those that do. That way, there are resources dedicated to + the more secure connections to ensure that service is not interrupted for + them. + + [general] + + maxcallnumbers_nonvalidated = 2048 + +2.2. CLI Commands + + 2.2.1. iax2 show callnumber usage + + Usage: iax2 show callnumber usage [IP address] + + Show current IP addresses which are consuming IAX2 call numbers. + + 2.2.2. iax2 show peer + + This command will now also show the configured call number limit and + whether or not call token validation is required for this peer. + + 3. Protocol Modification + + This section discusses the modification that has been made to the IAX2 + protocol. This information would be most useful to implementors of IAX2. + +3.1. Overview + + The IAX2 protocol uses a call number to associate messages with which call + they belong to. The available amount of call numbers is finite as defined + by the protocol. Because of this, it is important to prevent attackers + from maliciously consuming call numbers. To achieve this, an enhancement + to the IAX2 protocol has been made which is referred to as call token + validation. + + Call token validation ensures that an IAX2 connection is not coming from a + spoofed IP address. In addition to using call token validation, Asterisk + will also limit how many call numbers may be consumed by a given remote IP + address. These limits have defaults that will usually not need to be + changed, but can be modified for a specific need. + + The combination of call token validation and call number limits is used to + mitigate a denial of service attack to consume all available IAX2 call + numbers. An alternative approach to securing IAX2 would be to use a + security layer on top of IAX2, such as DTLS [RFC4347] or IPsec [RFC4301]. + +3.2. Call Token Validation + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119. + + For this section, when the word "request" is used, it is referring to the + command that starts an IAX2 dialog. + + This modification adds a new IAX2 frame type, and a new information + element be defined. + + Frame Type: CALLTOKEN --- 0x28 (40) + + IE: CALLTOKEN --- 0x36 (54) + + When a request is initially sent, it SHOULD include the CALLTOKEN IE with + a zero-length payload to indicate that this client supports the CALLTOKEN + exchange. When a server receives this request, it MUST respond with the + IAX2 message CALLTOKEN. The CALLTOKEN message MUST be sent with a source + call number of 0, as a call number will not yet be allocated for this + call. + + For the sake of backwards compatibility with clients that do not support + token validation, server implementations MAY process requests that do not + indicate CALLTOKEN support in their initial request. However, this SHOULD + NOT be the default behavior, as it gives up the security benefits gained + by CALLTOKEN validation. + + After a client sends a request with an empty CALLTOKEN IE, it MUST be + prepared to receive a CALLTOKEN response, or to receive a response that + would be given in the case of a valid CALLTOKEN. This is how a client must + behave to inter operate with IAX2 server implementations that do not yet + support CALLTOKEN validation. + + When an IAX2 client receives a CALLTOKEN response, it MUST send its + initial request again. This request MUST include the CALLTOKEN IE with a + copy of the value of the CALLTOKEN IE received in the CALLTOKEN response. + The IE value is an opaque value. Clients MUST be able to accept a + CALLTOKEN payload of any length, up to the maximum length allowed in an + IAX2 IE. + + The value of the payload in the CALLTOKEN IE is an implementation detail. + It is left to the implementor to decide how sophisticated it should be. + However, it MUST be enough such that when the CALLTOKEN IE is sent back, + it can be used to verify that the source IP address and port number has + not been spoofed. + + If a server receives a request with an invalid CALLTOKEN IE value, then it + MUST drop it and not respond. + +3.3. Example Message Exchanges + + 3.3.1. Call Setup + + Client Server + + -------- -------- + + ------------- NEW -----------> + + (with empty CALLTOKEN IE) + + <---------- CALLTOKEN ------------ + + (client must ignore + + source call number + + from this message) + + ------------- NEW -----------> + + (with received token) + + <---------- AUTHREQ ---------- + + ... continue as normal ... + + 3.3.2. Call Setup, client does not support CALLTOKEN + + Client Server + + -------- -------- + + ------------- NEW -----------> + + (with no CALLTOKEN IE) + + <----------- REJECT ---------- + + (sent without allocating + + a call number) + + ------------- ACK -----------> + + 3.3.3. Call Setup, client supports CALLTOKEN, server does not + + Client Server + + -------- -------- + + ------------- NEW -----------> + + (with empty CALLTOKEN IE) + + <----------- AUTHREQ --------- + + (sent without allocating + + a call number) + + ... continue as normal ... + + 3.3.4. Call Setup from client that sends invalid token + + Client Server + + -------- -------- + + ------------- NEW -----------> + + (with invalid CALLTOKEN IE) + + ... dropped ... + + 4. Asterisk Implementation + + This section includes some additional details on the implementation of + these changes in Asterisk. + +4.1. CALLTOKEN IE Payload + + For Asterisk, we will encode the payload of the CALLTOKEN IE such that the + server is able to validate a received token without having to store any + information after transmitting the CALLTOKEN response. The CALLTOKEN IE + payload will contain: + + * A timestamp (epoch based) + + * SHA1 hash of the remote IP address and port, the timestamp, as well + some random data generated when Asterisk starts. + + When a CALLTOKEN IE is received, its validity will be determined by + recalculating the SHA1 hash. If it is a valid token, the timestamp is + checked to determine if the token is expired. The token timeout will be + hard coded at 10 seconds for now. However, it may be made configurable at + some point if it seems to be a useful addition. If the server determines + that a received token is expired, it will treat it as an invalid token and + not respond to the request. + + By using this method, we require no additional memory to be allocated for + a dialog, other than what is on the stack for processing the initial + request, until token validation is complete. + + However, one thing to note with this CALLTOKEN IE encoding is that a token + would be considered valid by Asterisk every time a client sent it until we + considered it an expired token. However, with use of the "maxcallnumbers" + option, this is not actually a problem. It just means that an attacker + could hit their call number limit a bit quicker since they would only have + to acquire a single token per timeout period, instead of a token per + request. + + 10 of 10 Property changes on: doc/IAX2-security.txt ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: UPGRADE.txt =================================================================== --- UPGRADE.txt (revision 215957) +++ UPGRADE.txt (revision 217888) @@ -1,6 +1,14 @@ Information for Upgrading From Previous Asterisk Releases ========================================================= +IAX2: + +* There have been some changes to the IAX2 protocol to address the security + concerns documented in the security advisory AST-2009-006. Please see the + IAX2 security document, doc/IAX2-security.pdf, for information regarding + backwards compatibility with versions of Asterisk that do not contain these + changes to IAX2. + Compiling: * The Asterisk 1.2 source code now uses C language features Index: Makefile =================================================================== --- Makefile (revision 215957) +++ Makefile (revision 217888) @@ -354,7 +354,7 @@ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \ netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \ - cryptostub.o astobj2.o + cryptostub.o astobj2.o sha1.o ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),) OBJS+= poll.o Index: acl.c =================================================================== --- acl.c (revision 215957) +++ acl.c (revision 217888) @@ -74,14 +74,6 @@ AST_MUTEX_DEFINE_STATIC(routeseq_lock); #endif -struct ast_ha { - /* Host access rule */ - struct in_addr netaddr; - struct in_addr netmask; - int sense; - struct ast_ha *next; -}; - /* Default IP - if not otherwise set, don't breathe garbage */ static struct in_addr __ourip = { 0x00000000 }; @@ -102,7 +94,7 @@ } /* Copy HA structure */ -static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to) +void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to) { memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr)); memcpy(&to->netmask, &from->netmask, sizeof(from->netmask)); @@ -141,7 +133,7 @@ return ret; /* Return start of list */ } -struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path) +struct ast_ha *ast_append_ha(char *sense, const char *stuff, struct ast_ha *path) { struct ast_ha *ha = malloc(sizeof(struct ast_ha)); char *nm = "255.255.255.255"; Index: include/asterisk/utils.h =================================================================== --- include/asterisk/utils.h (revision 215957) +++ include/asterisk/utils.h (revision 217888) @@ -144,6 +144,10 @@ \brief Produces MD5 hash based on input string */ void ast_md5_hash(char *output, char *input); +/* ast_sha1_hash + \brief Produces SHA1 hash based on input string */ +void ast_sha1_hash(char *output, char *input); + int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max); int ast_base64decode(unsigned char *dst, const char *src, int max); Index: include/asterisk/acl.h =================================================================== --- include/asterisk/acl.h (revision 215957) +++ include/asterisk/acl.h (revision 217888) @@ -36,10 +36,17 @@ /* Host based access control */ -struct ast_ha; +struct ast_ha { + /* Host access rule */ + struct in_addr netaddr; + struct in_addr netmask; + int sense; + struct ast_ha *next; +}; -extern void ast_free_ha(struct ast_ha *ha); -extern struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path); +void ast_free_ha(struct ast_ha *ha); +void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to); +struct ast_ha *ast_append_ha(char *sense, const char *stuff, struct ast_ha *path); extern int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin); extern int ast_get_ip(struct sockaddr_in *sin, const char *value); extern int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service); Index: include/asterisk/sha1.h =================================================================== --- include/asterisk/sha1.h (revision 0) +++ include/asterisk/sha1.h (revision 217888) @@ -0,0 +1,81 @@ +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + + +#ifndef _SHA1_H_ +#define _SHA1_H_ + + + +#if defined(__OpenBSD__) || defined( __FreeBSD__) +#include +#else +#include +#endif + +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typdef the following: + * name meaning + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer (i.e., unsigned char) + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + uint32_t Message_Block_Index; /* 8 bits actually suffice */ + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ + + +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const uint8_t *, + unsigned int); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +#endif Property changes on: include/asterisk/sha1.h ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: include/asterisk/astobj2.h =================================================================== --- include/asterisk/astobj2.h (revision 215957) +++ include/asterisk/astobj2.h (revision 217888) @@ -273,6 +273,16 @@ * This implies that it can be passed to the object's hash function * for optimized searching. */ OBJ_POINTER = (1 << 3), + /*! + * \brief Continue if a match is not found in the hashed out bucket + * + * This flag is to be used in combination with OBJ_POINTER. This tells + * the ao2_callback() core to keep searching through the rest of the + * buckets if a match is not found in the starting bucket defined by + * the hash value on the argument. + */ + OBJ_CONTINUE = (1 << 4), + }; /*! Index: sha1.c =================================================================== --- sha1.c (revision 0) +++ sha1.c (revision 217888) @@ -0,0 +1,385 @@ +/* + * + * Based on the RFC 3174 + * + * Full Copyright Statement + * + * Copyright (C) The Internet Society (2001). All Rights Reserved. + * + * This document and translations of it may be copied and furnished to + * others, and derivative works that comment on or otherwise explain it + * or assist in its implementation may be prepared, copied, published + * and distributed, in whole or in part, without restriction of any + * kind, provided that the above copyright notice and this paragraph are + * included on all such copies and derivative works. However, this + * document itself may not be modified in any way, such as by removing + * the copyright notice or references to the Internet Society or other + * Internet organizations, except as needed for the purpose of + * developing Internet standards in which case the procedures for + * copyrights defined in the Internet Standards process must be + * followed, or as required to translate it into languages other than + * English. + * + * The limited permissions granted above are perpetual and will not be + * revoked by the Internet Society or its successors or assigns. + + * This document and the information contained herein is provided on an + * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses (included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + + +#include "asterisk/sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +void SHA1PadMessage(SHA1Context *); +void SHA1ProcessMessageBlock(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset(SHA1Context *context) +{ + if (!context) { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) { + return shaNull; + } + + if (context->Corrupted) { + return context->Corrupted; + } + + if (!context->Computed) { + SHA1PadMessage(context); + for (i = 0; i < 64; ++i) { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + } + + for (i = 0; i < SHA1HashSize; ++i) { + Message_Digest[i] = context->Intermediate_Hash[i >> 2] >> 8 * ( 3 - ( i & 0x03 ) ); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length) +{ + if (!length) { + return shaSuccess; + } + + if (!context || !message_array) { + return shaNull; + } + + if (context->Computed) { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) { + return context->Corrupted; + } + + while (length-- && !context->Corrupted) { + context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) { + context->Length_High++; + if (context->Length_High == 0) { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) { + temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) { + temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while (context->Message_Block_Index < 64) { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while (context->Message_Block_Index < 56) { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } else { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while (context->Message_Block_Index < 56) { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + SHA1ProcessMessageBlock(context); +} Property changes on: sha1.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: astobj2.c =================================================================== --- astobj2.c (revision 215957) +++ astobj2.c (revision 217888) @@ -413,7 +413,7 @@ const enum search_flags flags, ao2_callback_fn cb_fn, void *arg) { - int i, last; /* search boundaries */ + int i, start, last; /* search boundaries */ void *ret = NULL; if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */ @@ -443,14 +443,16 @@ * (this only for the time being. We need to optimize this.) */ if ((flags & OBJ_POINTER)) /* we know hash can handle this case */ - i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets; + start = i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets; else /* don't know, let's scan all buckets */ i = -1; /* XXX this must be fixed later. */ /* determine the search boundaries: i..last-1 */ if (i < 0) { - i = 0; + start = i = 0; last = c->n_buckets; + } else if ((flags & OBJ_CONTINUE)) { + last = c->n_buckets; } else { last = i + 1; } @@ -505,6 +507,17 @@ } } AST_LIST_TRAVERSE_SAFE_END + + if (ret) { + /* This assumes OBJ_MULTIPLE with !OBJ_NODATA is still not implemented */ + break; + } + + if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) { + /* Move to the beginning to ensure we check every bucket */ + i = -1; + last = start; + } } ao2_unlock(c); return ret; Index: configs/iax.conf.sample =================================================================== --- configs/iax.conf.sample (revision 215957) +++ configs/iax.conf.sample (revision 217888) @@ -286,6 +286,62 @@ ; has expired based on its registration interval, used the stored ; address information regardless. (yes|no) +; +; The following two options are used to disable call token validation for the +; purposes of interoperability with IAX2 endpoints that do not yet support it. +; +; Call token validation can be set as optional for a single IP address or IP +; address range by using the 'calltokenoptional' option. 'calltokenoptional' is +; only a global option. +; +;calltokenoptional=209.16.236.73/255.255.255.0 +; +; In a peer/user/friend definition, the 'requirecalltoken' option may be used. +; By setting 'requirecalltoken=no', call token validation becomes optional for +; that peer/user. By setting 'requirecalltoken=auto', call token validation +; is optional until a call token supporting peer registers successfully using +; call token validation. This is used as an indication that from now on, we +; can require it from this peer. So, requirecalltoken is internally set to yes. +; By default, 'requirecalltoken=yes'. +; +;requirecalltoken=no +; + +; +; These options are used to limit the amount of call numbers allocated to a +; single IP address. Before changing any of these values, it is highly encouraged +; to read the user guide associated with these options first. In most cases, the +; default values for these options are sufficient. +; +; The 'maxcallnumbers' option limits the amount of call numbers allowed for each +; individual remote IP address. Once an IP address reaches it's call number +; limit, no more new connections are allowed until the previous ones close. This +; option can be used in a peer definition as well, but only takes effect for +; the IP of a dynamic peer after it completes registration. +; +;maxcallnumbers=512 +; +; The 'maxcallnumbers_nonvalidated' is used to set the combined number of call +; numbers that can be allocated for connections where call token validation +; has been disabled. Unlike the 'maxcallnumbers' option, this limit is not +; separate for each individual IP address. Any connection resulting in a +; non-call token validated call number being allocated contributes to this +; limit. For use cases, see the call token user guide. This option's +; default value of 8192 should be sufficient in most cases. +; +;maxcallnumbers_nonvalidated=1024 +; +; The [callnumberlimits] section allows custom call number limits to be set +; for specific IP addresses and IP address ranges. These limits take precedence +; over the global 'maxcallnumbers' option, but may still be overridden by a +; peer defined 'maxcallnumbers' entry. Note that these limits take effect +; for every individual address within the range, not the range as a whole. +; +;[callnumberlimits] +;10.1.1.0/255.255.255.0 = 24 +;10.1.2.0/255.255.255.0 = 32 +; + ; Guest sections for unauthenticated connection attempts. Just specify an ; empty secret, or provide no secret section. ;