Add module support level to ast_module_info structure. Print it in CLI "module show" .
[asterisk/asterisk.git] / res / res_pjsip_acl.c
index 7cb498a..7cb56e7 100644 (file)
                <synopsis>SIP ACL module</synopsis>
                <description><para>
                        <emphasis>ACL</emphasis>
-                       </para>
-                       <para>The ACL module used by <literal>res_pjsip</literal>. This module is
+                       </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>.
+                       There are two main ways of defining your ACL with the options
+                       provided. You can use the <literal>permit</literal> and <literal>deny</literal> options
+                       which act on <emphasis>IP</emphasis> addresses, or the <literal>contactpermit</literal>
+                       and <literal>contactdeny</literal> options which act on <emphasis>Contact header</emphasis>
+                       addresses in incoming REGISTER requests. You can combine the various options to
+                       create a mixed ACL.
                        </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.
+                       Additionally, instead of defining an ACL with options, you can reference IP or
+                       Contact header ACLs from the file <filename>acl.conf</filename> by using the <literal>acl</literal>
+                       or <literal>contactacl</literal> options.
                </para></description>
                <configFile name="pjsip.conf">
                        <configObject name="acl">
                                <synopsis>Access Control List</synopsis>
                                <configOption name="acl">
-                                       <synopsis>Name of IP ACL</synopsis>
+                                       <synopsis>List of IP ACL section names in acl.conf</synopsis>
                                        <description><para>
-                                               This matches sections configured in <literal>acl.conf</literal>
+                                               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="contactacl">
-                                       <synopsis>Name of Contact ACL</synopsis>
+                               <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>
+                                               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="contactdeny">
-                                       <synopsis>List of Contact Header addresses to Deny</synopsis>
+                               <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="contactpermit">
-                                       <synopsis>List of Contact Header addresses to Permit</synopsis>
+                               <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>
                                <configOption name="deny">
-                                       <synopsis>List of IP-domains to deny access from</synopsis>
+                                       <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-domains to allow access from</synopsis>
+                                       <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="type">
-                                       <synopsis>Must be of type 'security'.</synopsis>
+                                       <synopsis>Must be of type 'acl'.</synopsis>
                                </configOption>
                        </configObject>
                </configFile>
@@ -108,7 +138,7 @@ static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr
        pjsip_sip_uri *sip_uri;
        char host[256];
 
-       if (!contact) {
+       if (!contact || contact->star) {
                return 0;
        }
        if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
@@ -153,13 +183,24 @@ static int apply_contact_acl(pjsip_rx_data *rdata, struct ast_acl_list *contact_
        return forbidden;
 }
 
+#define SIP_SORCERY_ACL_TYPE "acl"
+
+/*!
+ * \brief SIP ACL details and configuration.
+ */
+struct ast_sip_acl {
+       SORCERY_OBJECT(details);
+       struct ast_acl_list *acl;
+       struct ast_acl_list *contact_acl;
+};
+
 static int check_acls(void *obj, void *arg, int flags)
 {
-       struct ast_sip_security *security = obj;
+       struct ast_sip_acl *sip_acl = obj;
        pjsip_rx_data *rdata = arg;
 
-       if (apply_acl(rdata, security->acl) ||
-           apply_contact_acl(rdata, security->contact_acl)) {
+       if (apply_acl(rdata, sip_acl->acl) ||
+           apply_contact_acl(rdata, sip_acl->contact_acl)) {
                return CMP_MATCH | CMP_STOP;
        }
        return 0;
@@ -168,9 +209,9 @@ static int check_acls(void *obj, void *arg, int flags)
 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_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE,
                         AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
-       RAII_VAR(struct ast_sip_security *, matched_acl, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_sip_acl *, matched_acl, NULL, ao2_cleanup);
 
        if (!acls) {
                ast_log(LOG_ERROR, "Unable to retrieve ACL sorcery data\n");
@@ -187,6 +228,20 @@ static pj_bool_t acl_on_rx_msg(pjsip_rx_data *rdata)
        return PJ_FALSE;
 }
 
+static int acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+       struct ast_sip_acl *sip_acl = obj;
+       int error = 0;
+       int ignore;
+       if (!strncmp(var->name, "contact", 7)) {
+               ast_append_acl(var->name + 7, var->value, &sip_acl->contact_acl, &error, &ignore);
+       } else {
+               ast_append_acl(var->name, var->value, &sip_acl->acl, &error, &ignore);
+       }
+
+       return error;
+}
+
 static pjsip_module acl_module = {
        .name = { "ACL Module", 14 },
        /* This should run after a logger but before anything else */
@@ -194,8 +249,42 @@ static pjsip_module acl_module = {
        .on_rx_request = acl_on_rx_msg,
 };
 
+static void acl_destroy(void *obj)
+{
+       struct ast_sip_acl *sip_acl = obj;
+       sip_acl->acl = ast_free_acl_list(sip_acl->acl);
+       sip_acl->contact_acl = ast_free_acl_list(sip_acl->contact_acl);
+}
+
+static void *acl_alloc(const char *name)
+{
+       struct ast_sip_acl *sip_acl =
+               ast_sorcery_generic_alloc(sizeof(*sip_acl), acl_destroy);
+
+       return sip_acl;
+}
+
 static int load_module(void)
 {
+       ast_sorcery_apply_default(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE,
+                                 "config", "pjsip.conf,criteria=type=acl");
+
+       if (ast_sorcery_object_register(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE,
+                                       acl_alloc, NULL, NULL)) {
+
+               ast_log(LOG_ERROR, "Failed to register SIP %s object with sorcery\n",
+                       SIP_SORCERY_ACL_TYPE);
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       ast_sorcery_object_field_register(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "type", "", OPT_NOOP_T, 0, 0);
+       ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "permit", "", acl_handler, NULL, NULL, 0, 0);
+       ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "deny", "", acl_handler, NULL, NULL, 0, 0);
+       ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "acl", "", acl_handler, NULL, NULL, 0, 0);
+       ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_permit", "", acl_handler, NULL, NULL, 0, 0);
+       ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_deny", "", acl_handler, NULL, NULL, 0, 0);
+       ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE, "contact_acl", "", acl_handler, NULL, NULL, 0, 0);
+
        ast_sip_register_service(&acl_module);
        return AST_MODULE_LOAD_SUCCESS;
 }
@@ -207,6 +296,7 @@ static int unload_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP ACL Resource",
+               .support_level = AST_MODULE_SUPPORT_CORE,
                .load = load_module,
                .unload = unload_module,
                .load_pri = AST_MODPRI_APP_DEPEND,