######## DoS prevention module ######## # Default "order" is "deny,allow". # So if there is no data from DB request will be permitted by default. # modparam("htable", "htable", "acl=>initval=-1;autoexpire=7200") #!trydef ACL_MESSAGE_DENY "Rejected by ACL" #!trydef ACL_CODE_DENY "603" #!trydef ACL_ORDER_ALLOW_DENY "allow,deny" #!trydef ACL_IP_ADDR_ANY "0.0.0.0/0" #!trydef IP_REGEX "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}" ## Route for ACL functionality route[ACL_CHECK] { # If packet came from platform or from 4 class MERA, do not check it if (isflagset(FLAG_INTERNALLY_SOURCED) || isflagset(FLAG_TRUSTED_SOURCE) ) { xlog("L_DEBUG", "$ci|ACL|Trusted source IP($si) ignoring\n"); return; } if (isflagset(FLAG_IS_REPLY)) { $var(sip-packet) = $rs; } else { $var(sip-packet) = $rm; } # FIX for BYE method with IP instead of REALM in From, take REALM from To header if ($fd =~ IP_REGEX) { xlog("L_WARNING","$ci|ACL-realm|Fix for $var(sip-packet) with IP in from URI: use to-domain\n"); $var(realm) = $td; } else { $var(realm) = $fd; } $var(acl-realm-request) = "false"; $var(acl-device-request) = "false"; $var(realm-decision) = $sht(acl=>$var(realm)/$si); if ($var(realm-decision) == -1) { # we do not have cached decision $var(acl-realm-request) = "true"; } else if ($var(realm-decision) == 1 ){ # We have cached decision, let's use it xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); } else { if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(realm)\n"); exit; } if (not_empty("$fU")) { if ($fd =~ IP_REGEX) { xlog("L_WARNING","$ci|ACL-device|Fix for $var(sip-packet) with IP in from URI: use to-domain\n"); $var(device) = $fU + "@" + $td; } else { $var(device) = $fU + "@" + $fd; } $var(device-decision) = $sht(acl=>$var(device)/$si); if ($var(device-decision) == -1) { # we do not have cached decision $var(acl-device-request) = "true"; } else if ($var(device-decision) == 1 ){ # We have cached decision, let's use it xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); } else { if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); exit; } } if ($var(acl-realm-request) == "true" || $var(acl-device-request) == "true") { if (not_empty("$fU")) $var(query) = "{'Event-Category': 'acl', 'Event-Name': 'query', 'Entity': '" + $var(device) + "', 'With-Realm': " + $var(acl-realm-request) + "}"; else $var(query) = "{'Event-Category': 'acl', 'Event-Name': 'query', 'Entity': '" + $var(realm) + "'}"; xlog("L_DBG", "$ci|ACL log|Query: $var(query)\n"); sl_send_reply("100", "Attempting K query"); if (kazoo_query("frontier", "sbc_config", $var(query), "$var(acl-response)")) { xlog("L_DBG", "$ci|ACL log|Response: $var(acl-response)\n"); kazoo_json($var(acl-response), "Realm.Order", "$var(acl-realm-order)"); kazoo_json($var(acl-response), "Realm.CIDR", "$var(acl-realm-cidr)"); kazoo_json($var(acl-response), "Device.Order", "$var(acl-device-order)"); kazoo_json($var(acl-response), "Device.CIDR", "$var(acl-device-cidr)"); kazoo_json($var(acl-response), "Device.User-Agent", "$var(acl-device-ua)"); } else { xlog("L_ERROR","$ci|ACL log|DB is unreachable\n"); $sht(acl=>$var(device)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); return; } route(ACL_CHECK_REALM); if (not_empty("$fU")) { route(ACL_CHECK_DEVICE); } } } # Check ORDER setting for REALM route[ACL_CHECK_REALM] { if (not_empty("$var(acl-realm-order)")) { if ($var(acl-realm-order) == ACL_ORDER_ALLOW_DENY) { route(ACL_CHECK_REALM_ALLOW); } else { route(ACL_CHECK_REALM_DENY); } } else { xlog("L_INFO","$ci|ACL-realm|undefined Order in response for $var(realm)\n"); $sht(acl=>$var(realm)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); } } route[ACL_CHECK_REALM_ALLOW] { if (not_empty("$var(acl-realm-cidr)")) { $var(i) = 0; kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; while(not_empty("$var(record)")) { xlog("L_INFO", "$ci|ACL-realm|checking if $si is in $var(record)\n"); if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { $sht(acl=>$var(realm)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); return; } $var(i) = $var(i) + 1; kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; } } else { xlog("L_INFO", "$ci|ACL-realm|undefined CIDR in response for $var(realm)\n"); } # Remember in CACHE and DENY $sht(acl=>$var(realm)/$si) = 0; if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(realm)\n"); exit; } route[ACL_CHECK_REALM_DENY] { $var(size) = $(kzR{kz.json,Realm.CIDR.length}); if (not_empty("$var(acl-realm-cidr)")) { $var(i) = 0; kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; while(not_empty("$var(record)")) { xlog("L_INFO", "$ci|ACL-realm|checking if $si is in $var(record)\n"); if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { $sht(acl=>$var(realm)/$si) = 0; if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(realm)\n"); exit; } $var(i) = $var(i) + 1; kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; } } else { xlog("L_INFO", "$ci|ACL-realm|undefined CIDR in response for $var(realm)\n"); } # Remember in CACHE and ALLOW $sht(acl=>$var(realm)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); return; } # Check ORDER setting for DEVICE route[ACL_CHECK_DEVICE] { if (not_empty("$var(acl-device-order)")) { if ($var(acl-device-order) == ACL_ORDER_ALLOW_DENY) { route(ACL_CHECK_DEVICE_ALLOW); } else { route(ACL_CHECK_DEVICE_DENY); } } else { xlog("L_INFO","$ci|ACL-device|undefined Order in response for $var(device)\n"); $sht(acl=>$var(device)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); } } route[ACL_CHECK_DEVICE_ALLOW] { if (!not_empty("$var(acl-device-ua)") || (not_empty("$var(acl-device-ua)") && $ua =~ $var(acl-device-ua))) { if (not_empty("$var(acl-device-cidr)")) { $var(i) = 0; kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; while(not_empty("$var(record)")) { xlog("L_INFO", "$ci|ACL-realm|checking if $si is in $var(record)\n"); if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { $sht(acl=>$var(device)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); return; } $var(i) = $var(i) + 1; kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; } } else { xlog("L_INFO", "$ci|ACL-realm|undefined CIDR in response for $var(device)\n"); } } # Remember in CACHE and DENY $sht(acl=>$var(device)/$si) = 0; if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); exit; } route[ACL_CHECK_DEVICE_DENY] { if (not_empty("$var(acl-device-ua)") && !($ua =~ $var(acl-device-ua))) { $sht(acl=>$var(device)/$si) = 0; if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); exit; } if (not_empty("$var(acl-device-cidr)")) { $var(i) = 0; kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; while(not_empty("$var(record)")) { xlog("L_INFO", "$ci|ACL-device|checking if $si is in $var(record)\n"); if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { $sht(acl=>$var(device)/$si) = 0; if (!isflagset(FLAG_IS_REPLY)) { sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); } xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); exit; } $var(i) = $var(i) + 1; kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; } } else { xlog("L_INFO", "$ci|ACL-device|undefined CIDR in response for $var(device)\n"); } # Remember in CACHE and ALLOW $sht(acl=>$var(device)/$si) = 1; xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); return; } event_route[kazoo:consumer-event-acl-acl-flush] { if( $(kzE{kz.json,Device}) != "" ) { $var(device_regexp) = $(kzE{kz.json,Device}) + "@" + $(kzE{kz.json,Realm}) + "/.*" ; xlog("L_INFO","|ACL-Flush| Flush ACL for Device. Regexp: $var(device_regexp)\n"); sht_rm_name_re("acl=>$var(device_regexp)"); } else { $var(realm_regexp) = "^" + $(kzE{kz.json,Realm}) + "/.*" ; xlog("L_INFO","|ACL-Flush| Flush ACL for Realm. Regexp: $var(realm_regexp)\n"); sht_rm_name_re("acl=>$var(realm_regexp)"); } }