瀏覽代碼

auth(s): new cfg functions

- pv_www_authenticate(real, passwd, flags)
- pv_proxy_authenticate(real, passwd, flags)
	- perform Digest authentication taking password from a parameter
	  (can be cfg var)
	- username is taken from Auth header
	- realm and passwd must have values
		- $td (To URI domain) can be used for REGISTER
		- $fd (From URI domain) can be used for other SIP requests
	- flags - bitmask of
		- 1 - the content of password param is HA1/HA1b
		- 2 - build challenge header (no qop) and add it in an avp
		- 4 - build challenge header (qop=auth) and add it in an avp
		- 8 - build challenge header (qop=auth-int) and add it in an avp
	- when challenge header is built, append_to_reply() and sl reply
	  functions can be used to send appropriate SIP reply to challenge
	  for authentication
- www_challenge(realm, flags)
- proxy_challenge(realm, flags)
	- send Digest authentication challenge (401 or 407 replies)
	- realm must have value
		- $td (To URI domain) can be used for REGISTER
		- $fd (From URI domain) can be used for other SIP requests
	- flags - bitmask of
		- 1 - build challenge header (qop=auth) and add it in an avp
		- 2 - build challenge header (qop=auth-int) and add it in an avp
		- 4 - do not send '500 Internal Server Error' reply
		  automatically in failure cases (error code is returned to config)
Daniel-Constantin Mierla 15 年之前
父節點
當前提交
b3f0a3f0b2
共有 6 個文件被更改,包括 469 次插入51 次删除
  1. 59 18
      modules_s/auth/api.c
  2. 11 4
      modules_s/auth/api.h
  3. 329 6
      modules_s/auth/auth_mod.c
  4. 1 1
      modules_s/auth/auth_mod.h
  5. 62 20
      modules_s/auth/challenge.c
  6. 7 2
      modules_s/auth/challenge.h

+ 59 - 18
modules_s/auth/api.c

@@ -42,7 +42,8 @@
 #include "auth_mod.h"
 #include "nonce.h"
 
-static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body, auth_result_t* auth_res);
+static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body,
+		auth_result_t* auth_res);
 
 /*
  * Purpose of this function is to find credentials with given realm,
@@ -53,28 +54,23 @@ static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body, auth_
  * @param check_hdr  pointer to the function checking Authorization header field
  */
 auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
-						struct hdr_field**  hdr, check_auth_hdr_t check_auth_hdr)
+						struct hdr_field**  hdr,
+						check_auth_hdr_t check_auth_hdr)
 {
 	int ret;
 	auth_body_t* c;
 	check_auth_hdr_t check_hf;
 	auth_result_t    auth_rv;
 
-	static str prack = STR_STATIC_INIT("PRACK");
-
 	     /* ACK and CANCEL must be always authenticated, there is
 	      * no way how to challenge ACK and CANCEL cannot be
 	      * challenged because it must have the same CSeq as
-	      * the request to be canceled
+	      * the request to be canceled.
+	      * PRACK is also not authenticated
 	      */
 
-	if ((msg->REQ_METHOD == METHOD_ACK) ||  (msg->REQ_METHOD == METHOD_CANCEL)) return AUTHENTICATED;
-	     /* PRACK is also not authenticated */
-	if ((msg->REQ_METHOD == METHOD_OTHER)) {
-		if (msg->first_line.u.request.method.len == prack.len &&
-		    !memcmp(msg->first_line.u.request.method.s, prack.s, prack.len))
-			return AUTHENTICATED;
-	}
+	if (msg->REQ_METHOD & (METHOD_ACK|METHOD_CANCEL|METHOD_PRACK))
+		return AUTHENTICATED;
 
 	     /* Try to find credentials with corresponding realm
 	      * in the message, parse them and return pointer to
@@ -85,7 +81,8 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
 		LOG(L_ERR, "auth:pre_auth: Error while looking for credentials\n");
 		return ERROR;
 	} else if (ret > 0) {
-		DBG("auth:pre_auth: Credentials with realm '%.*s' not found\n", realm->len, ZSW(realm->s));
+		DBG("auth:pre_auth: Credentials with realm '%.*s' not found\n",
+				realm->len, ZSW(realm->s));
 		return NOT_AUTHENTICATED;
 	}
 
@@ -93,13 +90,14 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
 	c = (auth_body_t*)((*hdr)->parsed);
 
 	    /* digest headers are in c->digest */
-	DBG("auth: digest-algo: %.*s parsed value: %d\n", c->digest.alg.alg_str.len, c->digest.alg.alg_str.s, c->digest.alg.alg_parsed);
+	DBG("auth: digest-algo: %.*s parsed value: %d\n",
+			c->digest.alg.alg_str.len, c->digest.alg.alg_str.s,
+			c->digest.alg.alg_parsed);
 
 	    /* check authorization header field's validity */
 	if (check_auth_hdr == NULL) {
 		check_hf = auth_check_hdr_md5;
-	}
-	else {	/* use check function of external authentication module */
+	} else {	/* use check function of external authentication module */
 		check_hf = check_auth_hdr;
 	}
 	/* use the right function */
@@ -117,7 +115,8 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
  * @result if authentication should continue (1) or not (0)
  * 
  */
-static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth, auth_result_t* auth_res)
+static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
+		auth_result_t* auth_res)
 {
 	int ret;
 	
@@ -177,6 +176,47 @@ auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr)
 	return res;
 }
 
+/*
+ * Calculate the response and compare with the given response string
+ * Authorization is successful if this two strings are same
+ */
+int auth_check_response(dig_cred_t* cred, str* method, char* ha1)
+{
+	HASHHEX resp, hent;
+
+	/*
+	 * First, we have to verify that the response received has
+	 * the same length as responses created by us
+	 */
+	if (cred->response.len != 32) {
+		DBG("check_response: Receive response len != 32\n");
+		return 1;
+	}
+
+	/*
+	 * Now, calculate our response from parameters received
+	 * from the user agent
+	 */
+	calc_response(ha1, &(cred->nonce),
+				  &(cred->nc), &(cred->cnonce),
+				  &(cred->qop.qop_str), cred->qop.qop_parsed == QOP_AUTHINT,
+				  method, &(cred->uri), hent, resp);
+
+	DBG("check_response: Our result = \'%s\'\n", resp);
+
+	/*
+	 * And simply compare the strings, the user is
+	 * authorized if they match
+	 */
+	if (!memcmp(resp, cred->response.s, 32)) {
+		DBG("check_response: Authorization is OK\n");
+		return 0;
+	} else {
+		DBG("check_response: Authorization failed\n");
+		return 2;
+	}
+}
+
 
 int bind_auth_s(auth_api_s_t* api)
 {
@@ -188,8 +228,9 @@ int bind_auth_s(auth_api_s_t* api)
 	api->pre_auth = pre_auth;
 	api->post_auth = post_auth;
 	api->build_challenge = build_challenge_hf;
-	api->qop = &qop;
+	api->qop = &auth_qop;
 	api->calc_HA1 = calc_HA1;
 	api->calc_response = calc_response;
+	api->check_response = auth_check_response;
 	return 0;
 }

+ 11 - 4
modules_s/auth/api.h

@@ -53,8 +53,10 @@ typedef enum auth_result {
 	                        * of sequnce numbers with mobile station. */
 } auth_result_t;
 
-typedef int (*check_auth_hdr_t)(struct sip_msg* msg, auth_body_t* auth_body, auth_result_t* auth_res);
-int check_auth_hdr(struct sip_msg* msg, auth_body_t* auth_body, auth_result_t* auth_res);
+typedef int (*check_auth_hdr_t)(struct sip_msg* msg, auth_body_t* auth_body,
+		auth_result_t* auth_res);
+int check_auth_hdr(struct sip_msg* msg, auth_body_t* auth_body,
+		auth_result_t* auth_res);
 
 /*
  * Purpose of this function is to find credentials with given realm,
@@ -63,7 +65,8 @@ int check_auth_hdr(struct sip_msg* msg, auth_body_t* auth_body, auth_result_t* a
  * ACK and CANCEL
  */
 typedef auth_result_t (*pre_auth_t)(struct sip_msg* msg, str* realm,
-				    hdr_types_t hftype, struct hdr_field** hdr, check_auth_hdr_t check_auth_hdr);
+				    hdr_types_t hftype, struct hdr_field** hdr,
+					check_auth_hdr_t check_auth_hdr);
 auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
 		       struct hdr_field** hdr, check_auth_hdr_t check_auth_hdr);
 
@@ -72,9 +75,12 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
  * Purpose of this function is to do post authentication steps like
  * marking authorized credentials and so on.
  */
-typedef auth_result_t (*post_auth_t)(struct sip_msg* msg, struct hdr_field* hdr);
+typedef auth_result_t (*post_auth_t)(struct sip_msg* msg,
+		struct hdr_field* hdr);
 auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr);
 
+typedef int (*check_response_t)(dig_cred_t* cred, str* method, char* ha1);
+int auth_check_response(dig_cred_t* cred, str* method, char* ha1);
 
 /*
  * Auth module API
@@ -86,6 +92,7 @@ typedef struct auth_api_s {
     struct qp* qop;                       /* qop module parameter */
 	calc_HA1_t      calc_HA1;
 	calc_response_t calc_response;
+	check_response_t check_response;
 } auth_api_s_t;
 
 typedef int (*bind_auth_s_t)(auth_api_s_t* api);

+ 329 - 6
modules_s/auth/auth_mod.c

@@ -47,6 +47,7 @@
 #include "../../mem/mem.h"
 #include "../../parser/digest/digest.h"
 #include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
 #include "../../error.h"
 #include "../../ut.h"
 #include "../../modules/sl/sl.h"
@@ -56,6 +57,7 @@
 #include "nid.h"
 #include "nc.h"
 #include "ot_nonce.h"
+#include "rfc2617.h"
 
 MODULE_VERSION
 
@@ -77,6 +79,16 @@ static int mod_init(void);
  */
 int consume_credentials(struct sip_msg* msg, char* s1, char* s2);
 
+static int pv_proxy_authenticate(struct sip_msg* msg, char* realm,
+		char *passwd, char *flags);
+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 proxy_challenge(struct sip_msg *msg, char* realm, char *flags);
+static int www_challenge(struct sip_msg *msg, char* realm, char *flags);
+static int fixup_auth_challenge(void **param, int param_no);
+
 
 /*
  * Module parameter variables
@@ -97,17 +109,42 @@ avp_ident_t challenge_avpid;
 str proxy_challenge_header = STR_STATIC_INIT("Proxy-Authenticate");
 str www_challenge_header = STR_STATIC_INIT("WWW-Authenticate");
 
-struct qp qop = {
+struct qp auth_qop = {
     STR_STATIC_INIT("auth"),
     QOP_AUTH
 };
 
+static struct qp auth_qauth = {
+    STR_STATIC_INIT("auth"),
+    QOP_AUTH
+};
+
+static struct qp auth_qauthint = {
+    STR_STATIC_INIT("auth-int"),
+    QOP_AUTHINT
+};
+
+/*! SL API structure */
+sl_api_t slb;
 
 /*
  * Exported functions
  */
 static cmd_export_t cmds[] = {
-    {"consume_credentials", consume_credentials,     0, 0, REQUEST_ROUTE},
+    {"consume_credentials",    consume_credentials,                  0,
+			0, REQUEST_ROUTE},
+    {"www_challenge",          (cmd_function)www_challenge,          2,
+			fixup_auth_challenge, REQUEST_ROUTE},
+    {"proxy_challenge",        (cmd_function)proxy_challenge,        2,
+			fixup_auth_challenge, REQUEST_ROUTE},
+    {"pv_www_authorize",       (cmd_function)pv_www_authenticate,    3,
+			fixup_pv_auth, REQUEST_ROUTE},
+    {"pv_www_authenticate",    (cmd_function)pv_www_authenticate,    3,
+			fixup_pv_auth, REQUEST_ROUTE},
+    {"pv_proxy_authorize",     (cmd_function)pv_proxy_authenticate,  3,
+			fixup_pv_auth, REQUEST_ROUTE},
+    {"pv_proxy_authenticate",  (cmd_function)pv_proxy_authenticate,  3,
+			fixup_pv_auth, REQUEST_ROUTE},
     {"bind_auth_s",           (cmd_function)bind_auth_s, 0, 0, 0        },
     {0, 0, 0, 0, 0}
 };
@@ -124,7 +161,7 @@ static param_export_t params[] = {
     {"challenge_attr",         PARAM_STR,    &challenge_attr        },
     {"proxy_challenge_header", PARAM_STR,    &proxy_challenge_header},
     {"www_challenge_header",   PARAM_STR,    &www_challenge_header  },
-    {"qop",                    PARAM_STR,    &qop.qop_str           },
+    {"qop",                    PARAM_STR,    &auth_qop.qop_str      },
 	{"auth_checks_register",   PARAM_INT,    &auth_checks_reg       },
 	{"auth_checks_no_dlg",     PARAM_INT,    &auth_checks_ood       },
 	{"auth_checks_in_dlg",     PARAM_INT,    &auth_checks_ind       },
@@ -202,6 +239,12 @@ static int mod_init(void)
     
     DBG("auth module - initializing\n");
     
+	/* bind the SL API */
+	if (sl_load_api(&slb)!=0) {
+		LM_ERR("cannot bind to SL API\n");
+		return -1;
+	}
+
 	/* If the parameter was not used */
     if (sec_param == 0) {
 		/* Generate secret using random generator */
@@ -237,12 +280,13 @@ static int mod_init(void)
     attr.len = challenge_attr.len - 1;
     
     if (parse_avp_ident(&attr, &challenge_avpid) < 0) {
-		ERR("auth: Error while parsing value of challenge_attr module parameter\n");
+		ERR("auth: Error while parsing value of challenge_attr module"
+				" parameter\n");
 		return -1;
     }
 	
-    parse_qop(&qop);
-	switch(qop.qop_parsed){
+    parse_qop(&auth_qop);
+	switch(auth_qop.qop_parsed){
 		case QOP_OTHER:
 			ERR("auth: Unsupported qop parameter value\n");
 			return -1;
@@ -337,3 +381,282 @@ int consume_credentials(struct sip_msg* msg, char* s1, char* s2)
     return 1;
 }
 
+/**
+ * @brief do WWW-Digest authentication with password taken from cfg var
+ */
+static int pv_authenticate(struct sip_msg *msg, char *p1, char *p2,
+		char *p3, int hftype)
+{
+    int flags = 0;
+    str realm  = {0, 0};
+    str passwd = {0, 0};
+	struct hdr_field* h;
+	auth_body_t* cred;
+	int ret;
+    str hf = {0, 0};
+    avp_value_t val;
+	static char ha1[256];
+	struct qp *qop = NULL;
+
+	cred = 0;
+	ret = -1;
+
+	if (get_str_fparam(&realm, msg, (fparam_t*)p1) < 0) {
+		LM_ERR("failed to get realm value\n");
+		goto error;
+	}
+
+	if(realm.len==0) {
+		LM_ERR("invalid realm value - empty content\n");
+		goto error;
+	}
+
+	if (get_str_fparam(&passwd, msg, (fparam_t*)p2) < 0) {
+		LM_ERR("failed to get passwd value\n");
+		goto error;
+	}
+
+	if(passwd.len==0) {
+		LM_ERR("invalid password value - empty content\n");
+		goto error;
+	}
+
+	if (get_int_fparam(&flags, msg, (fparam_t*)p3) < 0) {
+		LM_ERR("invalid flags value\n");
+		goto error;
+	}
+
+	switch(pre_auth(msg, &realm, hftype, &h, NULL)) {
+		case ERROR:
+		case BAD_CREDENTIALS:
+			LM_DBG("error or bad credentials\n");
+			ret = -3;
+			goto end;
+		case CREATE_CHALLENGE:
+			LM_ERR("CREATE_CHALLENGE is not a valid state\n");
+			ret = -2;
+			goto end;
+		case DO_RESYNCHRONIZATION:
+			LM_ERR("DO_RESYNCHRONIZATION is not a valid state\n");
+			ret = -2;
+			goto end;
+		case NOT_AUTHENTICATED:
+			LM_DBG("not authenticated\n");
+			ret = -1;
+			goto end;
+		case DO_AUTHENTICATION:
+			break;
+		case AUTHENTICATED:
+			ret = 1;
+			goto end;
+	}
+
+	cred = (auth_body_t*)h->parsed;
+
+	/* compute HA1 if needed */
+	if ((flags&1)==0) {
+		/* Plaintext password is stored in PV, calculate HA1 */
+		calc_HA1(HA_MD5, &cred->digest.username.whole, &realm,
+				&passwd, 0, 0, ha1);
+		LM_DBG("HA1 string calculated: %s\n", ha1);
+	} else {
+		memcpy(ha1, passwd.s, passwd.len);
+		ha1[passwd.len] = '\0';
+	}
+
+	/* Recalculate response, it must be same to authorize successfully */
+	if (!auth_check_response(&(cred->digest),
+				&msg->first_line.u.request.method, ha1)) {
+		switch(post_auth(msg, h)) {
+			case ERROR:
+			case BAD_CREDENTIALS:
+				ret = -2;
+				break;
+			case NOT_AUTHENTICATED:
+				ret = -1;
+				break;
+			case AUTHENTICATED:
+				ret = 1;
+				break;
+			default:
+				ret = -1;
+				break;
+		}
+	} else {
+		ret = -1;
+	}
+
+ end:
+	if (ret < 0) {
+		/* check if required to add challenge header as avp */
+		if(!(flags&14))
+			return ret;
+		if(flags&8) {
+			qop = &auth_qauthint;
+		} else if(flags&4) {
+			qop = &auth_qauth;
+		}
+		if (get_challenge_hf(msg, (cred ? cred->stale : 0),
+				&realm, NULL, NULL, qop, hftype, &hf) < 0) {
+			ERR("Error while creating challenge\n");
+			ret = -2;
+		} else {
+			val.s = hf;
+			if(add_avp(challenge_avpid.flags | AVP_VAL_STR,
+							challenge_avpid.name, val) < 0) {
+				LM_ERR("Error while creating attribute with challenge\n");
+				ret = -2;
+			}
+			pkg_free(hf.s);
+		}
+	}
+	return ret;
+
+error:
+	return -1;
+}
+
+/**
+ *
+ */
+static int pv_proxy_authenticate(struct sip_msg *msg, char* realm,
+		char *passwd, char *flags)
+{
+	return pv_authenticate(msg, realm, passwd, flags, HDR_PROXYAUTH_T);
+}
+
+/**
+ *
+ */
+static int pv_www_authenticate(struct sip_msg *msg, char* realm,
+		char *passwd, char *flags)
+{
+	return pv_authenticate(msg, realm, passwd, flags, HDR_AUTHORIZATION_T);
+}
+
+/**
+ * @brief fixup function for pv_{www,proxy}_authenticate
+ */
+static int fixup_pv_auth(void **param, int param_no)
+{
+	switch(param_no) {
+		case 1:
+		case 2:
+			return fixup_var_str_12(param, 1);
+		case 3:
+			return fixup_var_int_12(param, 1);
+	}
+	return 0;
+}
+
+
+/**
+ *
+ */
+static int auth_send_reply(struct sip_msg *msg, int code, char *reason,
+					char *hdr, int hdr_len)
+{
+	/* Add new headers if there are any */
+	if ((hdr!=NULL) && (hdr_len>0)) {
+		if (add_lump_rpl(msg, hdr, hdr_len, LUMP_RPL_HDR)==0) {
+			LM_ERR("failed to append hdr to reply\n");
+			return -1;
+		}
+	}
+
+	return slb.zreply(msg, code, reason);
+}
+
+/**
+ *
+ */
+static int auth_challenge(struct sip_msg *msg, char *p1, char *p2, int hftype)
+{
+    int flags = 0;
+    str realm  = {0, 0};
+	int ret;
+    str hf = {0, 0};
+	struct qp *qop = NULL;
+
+	ret = -1;
+
+	if (get_str_fparam(&realm, msg, (fparam_t*)p1) < 0) {
+		LM_ERR("failed to get realm value\n");
+		goto error;
+	}
+
+	if(realm.len==0) {
+		LM_ERR("invalid realm value - empty content\n");
+		goto error;
+	}
+
+	if (get_int_fparam(&flags, msg, (fparam_t*)p2) < 0) {
+		LM_ERR("invalid flags value\n");
+		goto error;
+	}
+	
+	if(flags&2) {
+		qop = &auth_qauthint;
+	} else if(flags&1) {
+		qop = &auth_qauth;
+	}
+	if (get_challenge_hf(msg, 0, &realm, NULL, NULL, qop, hftype, &hf) < 0) {
+		ERR("Error while creating challenge\n");
+		ret = -2;
+		goto error;
+	}
+	
+	ret = 1;
+	switch(hftype) {
+		case HDR_AUTHORIZATION_T:
+			if(auth_send_reply(msg, 401, "Unauthorized",
+						hf.s, hf.len) <0 )
+				ret = -3;
+		break;
+		case HDR_PROXYAUTH_T:
+			if(auth_send_reply(msg, 407, "Proxy Authentication Required",
+						hf.s, hf.len) <0 )
+				ret = -3;
+		break;
+	}
+	if(hf.s) pkg_free(hf.s);
+	return ret;
+
+error:
+	if(hf.s) pkg_free(hf.s);
+	if(!(flags&4)) {
+		if(auth_send_reply(msg, 500, "Internal Server Error", 0, 0) <0 )
+			ret = -4;
+	}
+	return ret;
+}
+
+/**
+ *
+ */
+static int proxy_challenge(struct sip_msg *msg, char* realm, char *flags)
+{
+	return auth_challenge(msg, realm, flags, HDR_PROXYAUTH_T);
+}
+
+/**
+ *
+ */
+static int www_challenge(struct sip_msg *msg, char* realm, char *flags)
+{
+	return auth_challenge(msg, realm, flags, HDR_AUTHORIZATION_T);
+}
+
+/**
+ * @brief fixup function for {www,proxy}_challenge
+ */
+static int fixup_auth_challenge(void **param, int param_no)
+{
+	switch(param_no) {
+		case 1:
+			return fixup_var_str_12(param, 1);
+		case 2:
+			return fixup_var_int_12(param, 1);
+	}
+	return 0;
+}

+ 1 - 1
modules_s/auth/auth_mod.h

@@ -54,6 +54,6 @@ extern sl_api_t sl;
 extern avp_ident_t challenge_avpid;
 extern str proxy_challenge_header;
 extern str www_challenge_header;
-extern struct qp qop;
+extern struct qp auth_qop;
 
 #endif /* AUTH_MOD_H */

+ 62 - 20
modules_s/auth/challenge.c

@@ -67,29 +67,36 @@
 
 
 /**
- * Create {WWW,Proxy}-Authenticate header field
+ * Create and return {WWW,Proxy}-Authenticate header field
  * @param nonce nonce value
  * @param algorithm algorithm value
+ * @param qop qop value
  * @return -1 on error, 0 on success
  * 
- * The result is stored in an attribute.
+ * The result is stored in param ahf.
  * If nonce is not null that it is used, instead of call calc_nonce.
  * If algorithm is not null that it is used irrespective of _PRINT_MD5
  * 
  * Major usage of nonce and algorithm params is AKA authentication. 
  */
-int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, str* algorithm, int hftype)
+int get_challenge_hf(struct sip_msg* msg, int stale, str* realm,
+		str* nonce, str* algorithm, struct qp* qop, int hftype, str *ahf)
 {
     char *p;
     str* hfn, hf;
-    avp_value_t val;
     int nonce_len, l, cfg;
 	int t;
 #if defined USE_NC || defined USE_OT_NONCE
 	unsigned int n_id;
 	unsigned char pool;
 #endif
-    
+
+	if(!ahf)
+	{
+		LM_ERR("invalid output parameter\n");
+		return -1;
+	}
+
     if (realm) {
         DEBUG("build_challenge_hf: realm='%.*s'\n", realm->len, realm->s);
     }
@@ -97,9 +104,14 @@ int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, s
         DEBUG("build_challenge_hf: nonce='%.*s'\n", nonce->len, nonce->s);
     }
     if (algorithm) {
-        DEBUG("build_challenge_hf: algorithm='%.*s'\n", algorithm->len, algorithm->s);
+        DEBUG("build_challenge_hf: algorithm='%.*s'\n", algorithm->len,
+				algorithm->s);
     }
-    
+    if (qop && qop->qop_parsed != QOP_UNSPEC) {
+        DEBUG("build_challenge_hf: qop='%.*s'\n", qop->qop_str.len,
+				qop->qop_str.s);
+    }
+
     if (hftype == HDR_PROXYAUTH_T) {
 		hfn = &proxy_challenge_header;
     } else {
@@ -134,8 +146,8 @@ int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, s
 		;
     }
     
-    if (qop.qop_parsed != QOP_UNSPEC) {
-		hf.len += QOP_PARAM_START_LEN + qop.qop_str.len + QOP_PARAM_END_LEN;
+    if (qop && qop->qop_parsed != QOP_UNSPEC) {
+		hf.len += QOP_PARAM_START_LEN + qop->qop_str.len + QOP_PARAM_END_LEN;
     }
 	hf.len += CRLF_LEN;
     p = hf.s = pkg_malloc(hf.len);
@@ -190,11 +202,11 @@ int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, s
     }
     *p = '"'; p++;
 
-    if (qop.qop_parsed != QOP_UNSPEC) {
+    if (qop && qop->qop_parsed != QOP_UNSPEC) {
 		memcpy(p, QOP_PARAM_START, QOP_PARAM_START_LEN);
 		p += QOP_PARAM_START_LEN;
-		memcpy(p, qop.qop_str.s, qop.qop_str.len);
-		p += qop.qop_str.len;
+		memcpy(p, qop->qop_str.s, qop->qop_str.len);
+		p += qop->qop_str.len;
 		memcpy(p, QOP_PARAM_END, QOP_PARAM_END_LEN);
 		p += QOP_PARAM_END_LEN;
     }
@@ -203,8 +215,10 @@ int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, s
 		p += STALE_PARAM_LEN;
     }
 	if (algorithm) {
-		memcpy(p, DIGEST_ALGORITHM, DIGEST_ALGORITHM_LEN); p += DIGEST_ALGORITHM_LEN;
-		memcpy(p, algorithm->s, algorithm->len); p += algorithm->len;
+		memcpy(p, DIGEST_ALGORITHM, DIGEST_ALGORITHM_LEN);
+		p += DIGEST_ALGORITHM_LEN;
+		memcpy(p, algorithm->s, algorithm->len);
+		p += algorithm->len;
 	}
 	else {
 #ifdef _PRINT_MD5
@@ -216,14 +230,42 @@ int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, s
 							 nonce */
     
     DBG("auth: '%.*s'\n", hf.len, ZSW(hf.s));
-	
-    val.s = hf;
-    if (add_avp(challenge_avpid.flags | AVP_VAL_STR, challenge_avpid.name, val) < 0) {
-		ERR("auth: Error while creating attribute\n");
+	*ahf = hf;
+    return 0;
+}
+
+/**
+ * Create {WWW,Proxy}-Authenticate header field
+ * @param nonce nonce value
+ * @param algorithm algorithm value
+ * @return -1 on error, 0 on success
+ *
+ * The result is stored in an attribute.
+ * If nonce is not null that it is used, instead of call calc_nonce.
+ * If algorithm is not null that it is used irrespective of _PRINT_MD5
+ * The value of 'qop' module parameter is used.
+ *
+ * Major usage of nonce and algorithm params is AKA authentication.
+ */
+int build_challenge_hf(struct sip_msg* msg, int stale, str* realm,
+		str* nonce, str* algorithm, int hftype)
+{
+    str hf = {0, 0};
+    avp_value_t val;
+	int ret;
+
+	ret = get_challenge_hf(msg, stale, realm, nonce, algorithm, &auth_qop,
+				hftype, &hf);
+	if(ret < 0)
+		return ret;
+
+	val.s = hf;
+    if(add_avp(challenge_avpid.flags | AVP_VAL_STR, challenge_avpid.name, val)
+			< 0) {
+		ERR("auth: Error while creating attribute with challenge\n");
 		pkg_free(hf.s);
 		return -1;
     }
 	pkg_free(hf.s);
-
-    return 0;
+	return 0;
 }

+ 7 - 2
modules_s/auth/challenge.h

@@ -45,7 +45,12 @@
  * 
  * Major usage of nonce and algorithm params is AKA authentication. 
  */
-typedef int (*build_challenge_hf_t)(struct sip_msg* msg, int stale, str* realm, str* nonce, str* algorithm, int hftype);
-int build_challenge_hf(struct sip_msg* msg, int stale, str* realm, str* nonce, str* algorithm, int hftype);
+typedef int (*build_challenge_hf_t)(struct sip_msg* msg, int stale,
+		str* realm, str* nonce, str* algorithm, int hftype);
+int build_challenge_hf(struct sip_msg* msg, int stale, str* realm,
+		str* nonce, str* algorithm, int hftype);
+
+int get_challenge_hf(struct sip_msg* msg, int stale, str* realm,
+		str* nonce, str* algorithm, struct qp* qop, int hftype, str *ahf);
 
 #endif /* CHALLENGE_H */