2
0
Эх сурвалжийг харах

auth: new function pv_auth_check(...)

- equivalent of auth_check(...) from auth_db, but taking the password
  from a PV -- combines pv_proxy_authenticate() and
  pv_www_authenticate()
- new module parameter use_domain that controls whether the domain part
  of URIs should be used or not to make the identity checks upon
  authentication with pv_auth_check(...)
Daniel-Constantin Mierla 13 жил өмнө
parent
commit
8de2a607b9

+ 47 - 10
modules/auth/README

@@ -38,6 +38,7 @@ Daniel-Constantin Mierla
         1.3.12. nonce_auth_max_drift (integer)
         1.3.13. force_stateless_reply (boolean)
         1.3.14. realm_prefix (string)
+        1.3.15. use_domain (boolean)
 
    1.4. Functions
 
@@ -48,7 +49,8 @@ Daniel-Constantin Mierla
         1.4.5. auth_challenge(realm, flags)
         1.4.6. pv_www_authenticate(realm, passwd, flags)
         1.4.7. pv_proxy_authenticate(realm, passwd, flags)
-        1.4.8. auth_get_www_authenticate(realm, flags, pvdst)
+        1.4.8. pv_auth_check(realm, passwd, flags, checks)
+        1.4.9. auth_get_www_authenticate(realm, flags, pvdst)
 
 1.1. Overview
 
@@ -513,6 +515,16 @@ modparam("auth", "force_stateless_reply", 1)
    Example 14. realm_prefix parameter example
 modparam("auth", "realm_prefix", "sip.")
 
+1.3.15. use_domain (boolean)
+
+   If set to 1, pv_auth_check() uses domain parts of the URIs to check
+   user identity.
+
+   Example 15. force_stateless_reply example
+...
+modparam("auth", "use_domain", 1)
+...
+
 1.4. Functions
 
 1.4.1. consume_credentials()
@@ -525,7 +537,7 @@ modparam("auth", "realm_prefix", "sip.")
    little bit shorter. The function must be called after www_authorize,
    proxy_authorize, www_authenticate or proxy_authenticate.
 
-   Example 15. consume_credentials example
+   Example 16. consume_credentials example
 ...
 if (www_authenticate("realm", "subscriber")) {
     consume_credentials();
@@ -538,7 +550,7 @@ if (www_authenticate("realm", "subscriber")) {
    Proxy-Authorization header with provided realm. The parameter can be
    string with pseudo-variables.
 
-   Example 16. consume_credentials example
+   Example 17. consume_credentials example
 ...
 if (has_credentials("myrealm")) {
     ...
@@ -574,7 +586,7 @@ if (has_credentials("myrealm")) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 17. www_challenge usage
+   Example 18. www_challenge usage
 ...
 if (!www_authenticate("$td", "subscriber")) {
         www_challenge("$td", "1");
@@ -596,7 +608,7 @@ if (!www_authenticate("$td", "subscriber")) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 18. proxy_challenge usage
+   Example 19. proxy_challenge usage
 ...
 if (!proxy_authenticate("$fd", "subscriber")) {
         proxy_challenge("$fd", "1");
@@ -615,7 +627,7 @@ if (!proxy_authenticate("$fd", "subscriber")) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 19. proxy_challenge usage
+   Example 20. proxy_challenge usage
 ...
 if (!auth_check("$fd", "subscriber", "1")) {
         auth_challenge("$fd", "1");
@@ -666,7 +678,7 @@ if (!auth_check("$fd", "subscriber", "1")) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 20. pv_www_authenticate usage
+   Example 21. pv_www_authenticate usage
 ...
 if (!pv_www_authenticate("$td", "123abc", "0")) {
         www_challenge("$td", "1");
@@ -688,7 +700,7 @@ if (!pv_www_authenticate("$td", "123abc", "0")) {
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 21. pv_proxy_authenticate usage
+   Example 22. pv_proxy_authenticate usage
 ...
 $avp(password)="xyz";
 if (!pv_proxy_authenticate("$fd", "$avp(password)", "0")) {
@@ -696,7 +708,32 @@ if (!pv_proxy_authenticate("$fd", "$avp(password)", "0")) {
 };
 ...
 
-1.4.8. auth_get_www_authenticate(realm, flags, pvdst)
+1.4.8. pv_auth_check(realm, passwd, flags, checks)
+
+   The function combines the functionalities of pv_www_authenticate and
+   pv_proxy_authenticate, first being exectuted if the SIP request is a
+   REGISTER, the second for the rest.
+
+   Meaning of the first three parameters is the same as for
+   pv_www_authenticate(realm, passwd, flags).
+
+   Parameter checks can be used to control the behaviour of the function.
+   If it is 1, then the function will check to see if the authentication
+   username matches either To or From header username, a matter of whether
+   it is for a REGISTER request or not. The parameter may be a pseudo
+   variable.
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 23. pv_proxy_authenticate usage
+...
+$avp(password)="xyz";
+if (!pv_auth_check("$fd", "$avp(password)", "0", "1")) {
+        proxy_challenge("$fd", "1");
+};
+...
+
+1.4.9. auth_get_www_authenticate(realm, flags, pvdst)
 
    Build WWW-Authentication header and set the resulting value in 'pvdest'
    parameter.
@@ -706,7 +743,7 @@ if (!pv_proxy_authenticate("$fd", "$avp(password)", "0")) {
 
    This function can be used from ANY_ROUTE.
 
-   Example 22. auth_get_www_authenticate
+   Example 24. auth_get_www_authenticate
 ...
 if (auth_get_www_authenticate("$fd", "0", "$var(wauth)")) {
         xlog("www authenticate header is [$var(wauth)]\n");

+ 5 - 0
modules/auth/api.h

@@ -56,6 +56,11 @@ typedef enum auth_cfg_result {
 } auth_cfg_result_t;
 
 
+/**
+ * flags for checks in auth functions
+ */
+#define AUTH_CHECK_ID_F 1<<0
+
 /**
  * return codes to auth API functions
  */

+ 146 - 1
modules/auth/auth_mod.c

@@ -46,6 +46,9 @@
 #include "../../dprint.h"
 #include "../../mem/mem.h"
 #include "../../parser/digest/digest.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_uri.h"
 #include "../../data_lump.h"
 #include "../../data_lump_rpl.h"
 #include "../../error.h"
@@ -91,6 +94,9 @@ static int pv_proxy_authenticate(struct sip_msg* msg, char* realm,
 static int pv_www_authenticate(struct sip_msg* msg, char* realm,
 		char *passwd, char *flags);
 static int fixup_pv_auth(void **param, int param_no);
+static int pv_auth_check(sip_msg_t *msg, char *realm,
+		char *passwd, char *flags, char *checks);
+static int fixup_pv_auth_check(void **param, int param_no);
 
 static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags);
 static int www_challenge(struct sip_msg *msg, char* realm, char *flags);
@@ -113,6 +119,8 @@ int force_stateless_reply = 0; /* Always send reply statelessly */
 /*! Prefix to strip from realm */
 str auth_realm_prefix = {"", 0};
 
+static int auth_use_domain = 0;
+
 str secret1;
 str secret2;
 char* sec_rand1 = 0;
@@ -166,6 +174,8 @@ static cmd_export_t cmds[] = {
 			fixup_auth_get_www_authenticate, REQUEST_ROUTE},
     {"has_credentials",        w_has_credentials,                    1,
 			fixup_spve_null, REQUEST_ROUTE},
+    {"pv_auth_check",         (cmd_function)pv_auth_check,           4,
+			fixup_pv_auth_check, REQUEST_ROUTE},
     {"bind_auth_s",           (cmd_function)bind_auth_s, 0, 0, 0        },
     {0, 0, 0, 0, 0}
 };
@@ -192,9 +202,10 @@ static param_export_t params[] = {
 	{"one_time_nonce"  ,       PARAM_INT,    &otn_enabled           },
 	{"otn_in_flight_no",       PARAM_INT,    &otn_in_flight_no      },
 	{"otn_in_flight_order",    PARAM_INT,    &otn_in_flight_k       },
-	{"nid_pool_no",            PARAM_INT,    &nid_pool_no            },
+	{"nid_pool_no",            PARAM_INT,    &nid_pool_no           },
     {"force_stateless_reply",  PARAM_INT,    &force_stateless_reply },
 	{"realm_prefix",           PARAM_STRING, &auth_realm_prefix.s   },
+    {"use_domain",             PARAM_INT,    &auth_use_domain       },
     {0, 0, 0}
 };
 
@@ -637,6 +648,118 @@ error:
 	return AUTH_ERROR;
 }
 
+/**
+ *
+ */
+static int pv_auth_check(sip_msg_t *msg, char *realm,
+		char *passwd, char *flags, char *checks)
+{
+    int vflags = 0;
+    int vchecks = 0;
+    str srealm  = {0, 0};
+    str spasswd = {0, 0};
+	int ret;
+	hdr_field_t *hdr;
+	sip_uri_t *uri = NULL;
+	sip_uri_t *turi = NULL;
+	sip_uri_t *furi = NULL;
+
+	if(msg==NULL) {
+		LM_ERR("invalid msg parameter\n");
+		return AUTH_ERROR;
+	}
+
+	if ((msg->REQ_METHOD == METHOD_ACK) || (msg->REQ_METHOD == METHOD_CANCEL)) {
+		return AUTH_OK;
+	}
+
+	if(realm==NULL || passwd==NULL || flags==NULL || checks==NULL) {
+		LM_ERR("invalid parameters\n");
+		return AUTH_ERROR;
+	}
+
+	if (get_str_fparam(&srealm, msg, (fparam_t*)realm) < 0) {
+		LM_ERR("failed to get realm value\n");
+		return AUTH_ERROR;
+	}
+
+	if(srealm.len==0) {
+		LM_ERR("invalid realm value - empty content\n");
+		return AUTH_ERROR;
+	}
+
+	if (get_str_fparam(&spasswd, msg, (fparam_t*)passwd) < 0) {
+		LM_ERR("failed to get passwd value\n");
+		return AUTH_ERROR;
+	}
+
+	if(spasswd.len==0) {
+		LM_ERR("invalid password value - empty content\n");
+		return AUTH_ERROR;
+	}
+
+	if (get_int_fparam(&vflags, msg, (fparam_t*)flags) < 0) {
+		LM_ERR("invalid flags value\n");
+		return AUTH_ERROR;
+	}
+
+	if (get_int_fparam(&vchecks, msg, (fparam_t*)checks) < 0) {
+		LM_ERR("invalid checks value\n");
+		return AUTH_ERROR;
+	}
+	LM_DBG("realm [%.*s] flags [%d] checks [%d]\n", srealm.len, srealm.s,
+			vflags, vchecks);
+
+	if(msg->REQ_METHOD==METHOD_REGISTER)
+		ret = pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_AUTHORIZATION_T);
+	else
+		ret = pv_authenticate(msg, &srealm, &spasswd, vflags, HDR_PROXYAUTH_T);
+
+	if(ret==AUTH_OK && (vflags&AUTH_CHECK_ID_F)) {
+		hdr = (msg->proxy_auth==0)?msg->authorization:msg->proxy_auth;
+		srealm = ((auth_body_t*)(hdr->parsed))->digest.username.user;
+
+		if((furi=parse_from_uri(msg))==NULL)
+			return AUTH_ERROR;
+
+		if(msg->REQ_METHOD==METHOD_REGISTER || msg->REQ_METHOD==METHOD_PUBLISH) {
+			if((turi=parse_to_uri(msg))==NULL)
+				return AUTH_ERROR;
+			uri = turi;
+		} else {
+			uri = furi;
+		}
+		if(srealm.len!=uri->user.len
+					|| strncmp(srealm.s, uri->user.s, srealm.len)!=0)
+			return AUTH_USER_MISMATCH;
+
+		if(msg->REQ_METHOD==METHOD_REGISTER || msg->REQ_METHOD==METHOD_PUBLISH) {
+			/* check from==to */
+			if(furi->user.len!=turi->user.len
+					|| strncmp(furi->user.s, turi->user.s, furi->user.len)!=0)
+				return AUTH_USER_MISMATCH;
+			if(auth_use_domain!=0 && (furi->host.len!=turi->host.len
+					|| strncmp(furi->host.s, turi->host.s, furi->host.len)!=0))
+				return AUTH_USER_MISMATCH;
+			/* check r-uri==from for publish */
+			if(msg->REQ_METHOD==METHOD_PUBLISH) {
+				if(parse_sip_msg_uri(msg)<0)
+					return AUTH_ERROR;
+				uri = &msg->parsed_uri;
+				if(furi->user.len!=uri->user.len
+						|| strncmp(furi->user.s, uri->user.s, furi->user.len)!=0)
+					return AUTH_USER_MISMATCH;
+				if(auth_use_domain!=0 && (furi->host.len!=uri->host.len
+						|| strncmp(furi->host.s, uri->host.s, furi->host.len)!=0))
+					return AUTH_USER_MISMATCH;
+				}
+		}
+		return AUTH_OK;
+	}
+
+	return ret;
+}
+
 /**
  * @brief fixup function for pv_{www,proxy}_authenticate
  */
@@ -657,6 +780,28 @@ static int fixup_pv_auth(void **param, int param_no)
 	return 0;
 }
 
+/**
+ * @brief fixup function for pv_{www,proxy}_authenticate
+ */
+static int fixup_pv_auth_check(void **param, int param_no)
+{
+	if(strlen((char*)*param)<=0) {
+		LM_ERR("empty parameter %d not allowed\n", param_no);
+		return -1;
+	}
+
+	switch(param_no) {
+		case 1:
+		case 2:
+			return fixup_var_pve_str_12(param, 1);
+		case 3:
+		case 4:
+			return fixup_var_int_12(param, 1);
+	}
+	return 0;
+}
+
+
 
 /**
  *

+ 37 - 0
modules/auth/doc/functions.xml

@@ -326,6 +326,43 @@ if (!pv_proxy_authenticate("$fd", "$avp(password)", "0")) {
 		</example>
 	</section>
 
+	<section id="pv_auth_check">
+		<title>
+			<function moreinfo="none">pv_auth_check(realm, passwd, flags, checks)</function>
+		</title>
+		<para>
+		The function combines the functionalities of
+		<function moreinfo="none">pv_www_authenticate</function> and
+		<function moreinfo="none">pv_proxy_authenticate</function>, first being
+		exectuted if the SIP request is a REGISTER, the second for the rest.
+		</para>
+		<para>
+		Meaning of the first three parameters is the same as for
+		pv_www_authenticate(realm, passwd, flags).
+		</para>
+		<para>
+		Parameter <emphasis>checks</emphasis> can be used to control the
+		behaviour of the function. If it is 1, then the function will
+		check to see if the authentication username matches either To or
+		From header username, a matter of whether it is for a REGISTER
+		request or not.	The parameter may be a pseudo variable.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE.
+		</para>
+		<example>
+		<title>pv_proxy_authenticate usage</title>
+		<programlisting format="linespecific">
+...
+$avp(password)="xyz";
+if (!pv_auth_check("$fd", "$avp(password)", "0", "1")) {
+	proxy_challenge("$fd", "1");
+};
+...
+</programlisting>
+		</example>
+	</section>
+
 	<section id="auth_get_www_authenticate">
 		<title>
 			<function moreinfo="none">auth_get_www_authenticate(realm, flags, pvdst)</function>

+ 18 - 0
modules/auth/doc/params.xml

@@ -636,6 +636,7 @@ modparam("auth", "force_stateless_reply", 1)
 	    </programlisting>
 	</example>
 	</section>
+
 	<section id="realm_prefix">
 		<title><varname>realm_prefix</varname> (string)</title>
 		<para>
@@ -657,4 +658,21 @@ modparam("auth", "realm_prefix", "sip.")
 </programlisting>
 		</example>
 	</section>
+
+	<section id="auth.use_domain">
+	<title><varname>use_domain</varname> (boolean)</title>
+	<para>
+		If set to 1, <function>pv_auth_check()</function> uses
+		domain parts of the URIs to check user identity.
+	</para>
+	<example>
+	    <title>force_stateless_reply example</title>
+	    <programlisting>
+...
+modparam("auth", "use_domain", 1)
+...
+	    </programlisting>
+	</example>
+	</section>
+
 </section>