summaryrefslogtreecommitdiff
path: root/res/res_pjsip_acl.c
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2013-07-30 18:14:50 +0000
committerMark Michelson <mmichelson@digium.com>2013-07-30 18:14:50 +0000
commit735b30ad71110c2a51404cb8686bbe3cf14b630c (patch)
tree76b1f10135c1b7f210e576be1359539de7e3476c /res/res_pjsip_acl.c
parent895c8e0d2c97cd04299f3f179e99d8a3873c06c6 (diff)
The large GULP->PJSIP renaming effort.
The general gist is to have a clear boundary between old SIP stuff and new SIP stuff by having the word "SIP" for old stuff and "PJSIP" for new stuff. Here's a brief rundown of the changes: * The word "Gulp" in dialstrings, functions, and CLI commands is now "PJSIP" * chan_gulp.c is now chan_pjsip.c * Function names in chan_gulp.c that were "gulp_*" are now "chan_pjsip_*" * All files that were "res_sip*" are now "res_pjsip*" * The "res_sip" directory is now "res_pjsip" * Files in the "res_pjsip" directory that began with "sip_*" are now "pjsip_*" * The configuration file is now "pjsip.conf" instead of "res_sip.conf" * The module info for all PJSIP-related files now uses "PJSIP" instead of "SIP" * CLI and AMI commands created by Asterisk's PJSIP modules now have "pjsip" as the starting word instead of "sip" git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395764 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_pjsip_acl.c')
-rw-r--r--res/res_pjsip_acl.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/res/res_pjsip_acl.c b/res/res_pjsip_acl.c
new file mode 100644
index 000000000..7cb498a96
--- /dev/null
+++ b/res/res_pjsip_acl.c
@@ -0,0 +1,213 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+ <depend>pjproject</depend>
+ <depend>res_pjsip</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/acl.h"
+
+/*** DOCUMENTATION
+ <configInfo name="res_pjsip_acl" language="en_US">
+ <synopsis>SIP ACL module</synopsis>
+ <description><para>
+ <emphasis>ACL</emphasis>
+ </para>
+ <para>The ACL module used by <literal>res_pjsip</literal>. This module is
+ independent of <literal>endpoints</literal> and operates on all inbound
+ SIP communication using res_pjsip.
+ </para><para>
+ It should be noted that this module can also reference ACLs from
+ <filename>acl.conf</filename>.
+ </para><para>
+ There are two main ways of creating an access list: <literal>IP-Domain</literal>
+ and <literal>Contact Header</literal>. It is possible to create a combined ACL using
+ both IP and Contact.
+ </para></description>
+ <configFile name="pjsip.conf">
+ <configObject name="acl">
+ <synopsis>Access Control List</synopsis>
+ <configOption name="acl">
+ <synopsis>Name of IP ACL</synopsis>
+ <description><para>
+ This matches sections configured in <literal>acl.conf</literal>
+ </para></description>
+ </configOption>
+ <configOption name="contactacl">
+ <synopsis>Name of Contact ACL</synopsis>
+ <description><para>
+ This matches sections configured in <literal>acl.conf</literal>
+ </para></description>
+ </configOption>
+ <configOption name="contactdeny">
+ <synopsis>List of Contact Header addresses to Deny</synopsis>
+ </configOption>
+ <configOption name="contactpermit">
+ <synopsis>List of Contact Header addresses to Permit</synopsis>
+ </configOption>
+ <configOption name="deny">
+ <synopsis>List of IP-domains to deny access from</synopsis>
+ </configOption>
+ <configOption name="permit">
+ <synopsis>List of IP-domains to allow access from</synopsis>
+ </configOption>
+ <configOption name="type">
+ <synopsis>Must be of type 'security'.</synopsis>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+ ***/
+
+static int apply_acl(pjsip_rx_data *rdata, struct ast_acl_list *acl)
+{
+ struct ast_sockaddr addr;
+
+ if (ast_acl_list_is_empty(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(acl, &addr, "SIP ACL: ") != AST_SENSE_ALLOW) {
+ ast_log(LOG_WARNING, "Incoming SIP message from %s did not pass ACL test\n", ast_sockaddr_stringify(&addr));
+ 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) {
+ return 0;
+ }
+ if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+ 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_contact_acl(pjsip_rx_data *rdata, struct ast_acl_list *contact_acl)
+{
+ 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(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(contact_acl, &contact_addrs[i], "SIP Contact ACL: ") != AST_SENSE_ALLOW) {
+ ast_log(LOG_WARNING, "Incoming SIP message from %s did not pass ACL test\n", ast_sockaddr_stringify(&contact_addrs[i]));
+ 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 int check_acls(void *obj, void *arg, int flags)
+{
+ struct ast_sip_security *security = obj;
+ pjsip_rx_data *rdata = arg;
+
+ if (apply_acl(rdata, security->acl) ||
+ apply_contact_acl(rdata, security->contact_acl)) {
+ return CMP_MATCH | CMP_STOP;
+ }
+ return 0;
+}
+
+static pj_bool_t acl_on_rx_msg(pjsip_rx_data *rdata)
+{
+ RAII_VAR(struct ao2_container *, acls, ast_sorcery_retrieve_by_fields(
+ ast_sip_get_sorcery(), SIP_SORCERY_SECURITY_TYPE,
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
+ RAII_VAR(struct ast_sip_security *, matched_acl, NULL, ao2_cleanup);
+
+ if (!acls) {
+ ast_log(LOG_ERROR, "Unable to retrieve ACL sorcery data\n");
+ return PJ_FALSE;
+ }
+
+ if ((matched_acl = ao2_callback(acls, 0, check_acls, rdata))) {
+ if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+ }
+ return PJ_TRUE;
+ }
+
+ return PJ_FALSE;
+}
+
+static pjsip_module acl_module = {
+ .name = { "ACL Module", 14 },
+ /* This should run after a logger but before anything else */
+ .priority = 1,
+ .on_rx_request = acl_on_rx_msg,
+};
+
+static int load_module(void)
+{
+ ast_sip_register_service(&acl_module);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_unregister_service(&acl_module);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP ACL Resource",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+ );