[Asterisk-code-review] res pjsip: Endpoint IP Access Controls (asterisk[13])
Alexei Gradinari
asteriskteam at digium.com
Thu Apr 7 13:25:09 CDT 2016
Alexei Gradinari has uploaded a new change for review.
https://gerrit.asterisk.org/2549
Change subject: res_pjsip: Endpoint IP Access Controls
......................................................................
res_pjsip: Endpoint IP Access Controls
With the old SIP module we can use IP access controls per peer.
PJSIP module missing this feature.
This patch added next configuration Endpoint options:
"acl" - list of IP ACL section names in acl.conf
"deny" - List of IP addresses to deny access from
"permit" - List of IP addresses to permit access from
"contact_acl" - List of Contact ACL section names in acl.conf
"contact_deny" - List of Contact header addresses to deny
"contact_permit" - List of Contact header addresses to permit
Change-Id: I9d192c254e685fbc1c8706d4b1187cdaaf72b9be
---
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip/pjsip_distributor.c
4 files changed, 203 insertions(+), 0 deletions(-)
git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/49/2549/1
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index fbb9bdc..85167c9 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -719,6 +719,10 @@
unsigned int usereqphone;
/*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */
unsigned int rpid_immediate;
+ /* Access control list */
+ struct ast_acl_list *acl;
+ /* Restrict what IPs are allowed in the Contact header (for registration) */
+ struct ast_acl_list *contact_acl;
};
/*!
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 2b0e777..4c17b69 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -829,6 +829,56 @@
channel is hung up. By default this option is set to 0, which means do not check.
</para></description>
</configOption>
+ <configOption name="acl">
+ <synopsis>List of IP ACL section names in acl.conf</synopsis>
+ <description><para>
+ This matches sections configured in <literal>acl.conf</literal>. The value is
+ defined as a list of comma-delimited section names.
+ </para></description>
+ </configOption>
+ <configOption name="deny">
+ <synopsis>List of IP addresses to deny access from</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
+ <configOption name="permit">
+ <synopsis>List of IP addresses to permit access from</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
+ <configOption name="contact_acl">
+ <synopsis>List of Contact ACL section names in acl.conf</synopsis>
+ <description><para>
+ This matches sections configured in <literal>acl.conf</literal>. The value is
+ defined as a list of comma-delimited section names.
+ </para></description>
+ </configOption>
+ <configOption name="contact_deny">
+ <synopsis>List of Contact header addresses to deny</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
+ <configOption name="contact_permit">
+ <synopsis>List of Contact header addresses to permit</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 0658833..befabf7 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -265,6 +265,65 @@
.deleted = endpoint_deleted_observer,
};
+static int endpoint_acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct ast_sip_endpoint *endpoint = obj;
+ int error = 0;
+ int ignore;
+
+ if (ast_strlen_zero(var->value)) return 0;
+
+ if (!strncmp(var->name, "contact_", 8)) {
+ ast_append_acl(var->name + 8, var->value, &endpoint->contact_acl, &error, &ignore);
+ } else {
+ ast_append_acl(var->name, var->value, &endpoint->acl, &error, &ignore);
+ }
+
+ return error;
+}
+
+static int acl_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+ struct ast_acl_list *acl_list;
+ struct ast_acl *first_acl;
+
+ if (endpoint && !ast_acl_list_is_empty(acl_list=endpoint->acl)) {
+ AST_LIST_LOCK(acl_list);
+ first_acl = AST_LIST_FIRST(acl_list);
+ if (ast_strlen_zero(first_acl->name)) {
+ *buf = "deny/permit";
+ } else {
+ *buf = first_acl->name;
+ }
+ AST_LIST_UNLOCK(acl_list);
+ }
+
+ *buf = ast_strdup(*buf);
+ return 0;
+}
+
+static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+ struct ast_acl_list *acl_list;
+ struct ast_acl *first_acl;
+
+ if (endpoint && !ast_acl_list_is_empty(acl_list=endpoint->contact_acl)) {
+ AST_LIST_LOCK(acl_list);
+ first_acl = AST_LIST_FIRST(acl_list);
+ if (ast_strlen_zero(first_acl->name)) {
+ *buf = "deny/permit";
+ } else {
+ *buf = first_acl->name;
+ }
+ AST_LIST_UNLOCK(acl_list);
+ }
+
+ *buf = ast_strdup(*buf);
+ return 0;
+}
+
static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct ast_sip_endpoint *endpoint = obj;
@@ -1724,6 +1783,12 @@
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, message_context));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accountcode", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, accountcode));
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "deny", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "permit", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "acl", "", endpoint_acl_handler, acl_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_deny", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0);
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 834ca10..3e33367 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -21,6 +21,7 @@
#include <pjsip.h>
#include "asterisk/res_pjsip.h"
+#include "asterisk/acl.h"
#include "include/res_pjsip_private.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/threadpool.h"
@@ -404,12 +405,94 @@
return PJ_FALSE;
}
+static int apply_endpoint_acl(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
+{
+ struct ast_sockaddr addr;
+
+ if (ast_acl_list_is_empty(endpoint->acl)) {
+ return 0;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
+ ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
+
+ if (ast_apply_acl(endpoint->acl, &addr, "SIP ACL: ") != AST_SENSE_ALLOW) {
+ log_failed_request(rdata, "Not match Endpoint ACL");
+ ast_sip_report_failed_acl(endpoint, rdata, "not_match_endpoint_acl");
+ return 1;
+ }
+ return 0;
+}
+
+static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs)
+{
+ pjsip_sip_uri *sip_uri;
+ char host[256];
+
+ if (!contact || contact->star) {
+ *addrs = NULL;
+ return 0;
+ }
+ if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+ *addrs = NULL;
+ return 0;
+ }
+ sip_uri = pjsip_uri_get_uri(contact->uri);
+ ast_copy_pj_str(host, &sip_uri->host, sizeof(host));
+ return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC);
+}
+
+static int apply_endpoint_contact_acl(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
+{
+ int num_contact_addrs;
+ int forbidden = 0;
+ struct ast_sockaddr *contact_addrs;
+ int i;
+ pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
+
+ if (ast_acl_list_is_empty(endpoint->contact_acl)) {
+ return 0;
+ }
+
+ while ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
+ num_contact_addrs = extract_contact_addr(contact, &contact_addrs);
+ if (num_contact_addrs <= 0) {
+ continue;
+ }
+ for (i = 0; i < num_contact_addrs; ++i) {
+ if (ast_apply_acl(endpoint->contact_acl, &contact_addrs[i], "SIP Contact ACL: ") != AST_SENSE_ALLOW) {
+ log_failed_request(rdata, "Not match Endpoint Contact ACL");
+ ast_sip_report_failed_acl(endpoint, rdata, "not_match_endpoint_contact_acl");
+ forbidden = 1;
+ break;
+ }
+ }
+ ast_free(contact_addrs);
+ if (forbidden) {
+ /* No use checking other contacts if we already have failed ACL check */
+ break;
+ }
+ }
+
+ return forbidden;
+}
+
static pj_bool_t authenticate(pjsip_rx_data *rdata)
{
RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;
ast_assert(endpoint != NULL);
+
+ if (endpoint!=artificial_endpoint) {
+ if (apply_endpoint_acl(rdata, endpoint) || apply_endpoint_contact_acl(rdata, endpoint)) {
+ if (!is_ack) {
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+ }
+ return PJ_TRUE;
+ }
+ }
if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) {
pjsip_tx_data *tdata;
@@ -436,6 +519,7 @@
}
}
+
return PJ_FALSE;
}
--
To view, visit https://gerrit.asterisk.org/2549
To unsubscribe, visit https://gerrit.asterisk.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9d192c254e685fbc1c8706d4b1187cdaaf72b9be
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Owner: Alexei Gradinari <alex2grad at gmail.com>
More information about the asterisk-code-review
mailing list