diff --git a/src/modules/auth/api.c b/src/modules/auth/api.c
index baa215f5bfe..56f102f9592 100644
--- a/src/modules/auth/api.c
+++ b/src/modules/auth/api.c
@@ -34,7 +34,7 @@
#include "rfc2617_sha256.h"
#include "challenge.h"
-static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body,
+static int auth_check_hdr_md5_default(struct sip_msg* msg, auth_body_t* auth_body,
auth_result_t* auth_res);
/*
@@ -94,7 +94,7 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
/* check authorization header field's validity */
if (check_auth_hdr == NULL) {
- check_hf = auth_check_hdr_md5;
+ check_hf = auth_check_hdr_md5_default;
} else { /* use check function of external authentication module */
check_hf = check_auth_hdr;
}
@@ -113,8 +113,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)
+int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
+ auth_result_t* auth_res, int update_nonce)
{
int ret;
@@ -125,7 +125,7 @@ static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
return 0;
}
- ret = check_nonce(auth, &secret1, &secret2, msg);
+ ret = check_nonce(auth, &secret1, &secret2, msg, update_nonce);
if (ret!=0){
if (ret==3 || ret==4){
/* failed auth_extra_checks or stale */
@@ -145,6 +145,12 @@ static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
return 1;
}
+static int auth_check_hdr_md5_default(struct sip_msg* msg, auth_body_t* auth,
+ auth_result_t* auth_res)
+{
+ return auth_check_hdr_md5(msg, auth, auth_res, 1);
+}
+
/**
* Adds the Authentication-Info header, based on the credentials sent by a successful REGISTER.
* @param msg - SIP message to add the header to
diff --git a/src/modules/auth/api.h b/src/modules/auth/api.h
index 33d131840a3..2bb030c1cae 100644
--- a/src/modules/auth/api.h
+++ b/src/modules/auth/api.h
@@ -82,6 +82,9 @@ typedef int (*check_auth_hdr_t)(struct sip_msg* msg, auth_body_t* auth_body,
int check_auth_hdr(struct sip_msg* msg, auth_body_t* auth_body,
auth_result_t* auth_res);
+int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
+ auth_result_t* auth_res, int update_nonce);
+
/*
* Purpose of this function is to find credentials with given realm,
* do sanity check, validate credential correctness and determine if
diff --git a/src/modules/auth/auth_mod.c b/src/modules/auth/auth_mod.c
index cc16643eb49..6f7639619af 100644
--- a/src/modules/auth/auth_mod.c
+++ b/src/modules/auth/auth_mod.c
@@ -476,6 +476,19 @@ int w_has_credentials(sip_msg_t *msg, char* realm, char* s2)
}
return ki_has_credentials(msg, &srealm);
}
+
+#ifdef USE_NC
+/**
+ * Calls auth_check_hdr_md5 with the update_nonce flag set to false.
+ * Used when flag 32 is set in pv_authenticate.
+ */
+static int auth_check_hdr_md5_noupdate(struct sip_msg* msg, auth_body_t* auth,
+ auth_result_t* auth_res)
+{
+ return auth_check_hdr_md5(msg, auth, auth_res, 0);
+}
+#endif
+
/**
* @brief do WWW-Digest authentication with password taken from cfg var
*/
@@ -490,11 +503,17 @@ int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
avp_value_t val;
static char ha1[256];
struct qp *qop = NULL;
+ check_auth_hdr_t check_auth_hdr = NULL;
cred = 0;
ret = AUTH_ERROR;
- switch(pre_auth(msg, realm, hftype, &h, NULL)) {
+#ifdef USE_NC
+ if (nc_enabled && (flags & 32))
+ check_auth_hdr = auth_check_hdr_md5_noupdate;
+#endif
+
+ switch(pre_auth(msg, realm, hftype, &h, check_auth_hdr)) {
case NONCE_REUSED:
LM_DBG("nonce reused");
ret = AUTH_NONCE_REUSED;
@@ -562,6 +581,16 @@ int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
ret = AUTH_ERROR;
}
+#ifdef USE_NC
+ /* On success we need to update the nonce if flag 32 is set */
+ if (nc_enabled && ret == AUTH_OK && (flags & 32)) {
+ if (check_nonce(cred, &secret1, &secret2, msg, 1) < 0) {
+ LM_ERR("check_nonce failed after post_auth");
+ ret = AUTH_ERROR;
+ }
+ }
+#endif
+
end:
if (ret < 0) {
/* check if required to add challenge header as avp */
diff --git a/src/modules/auth/doc/auth_functions.xml b/src/modules/auth/doc/auth_functions.xml
index 7d3dc639049..b9a4cb2058b 100644
--- a/src/modules/auth/doc/auth_functions.xml
+++ b/src/modules/auth/doc/auth_functions.xml
@@ -274,6 +274,10 @@ if (!auth_check("$fd", "subscriber", "1")) {
16 - build challenge header with
stale=true
+
+ 32 - don't invalidate nc on
+ authentication failure
+
diff --git a/src/modules/auth/nc.c b/src/modules/auth/nc.c
index 10e3fede9a6..93641728201 100644
--- a/src/modules/auth/nc.c
+++ b/src/modules/auth/nc.c
@@ -213,7 +213,7 @@ nid_t nc_new(nid_t id, unsigned char p)
* NC_TOO_BIG (nc value got too big and cannot be held anymore)
* NC_REPLAY (nc value is <= the current stored one)
*/
-enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc)
+enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc, int update)
{
unsigned int i;
unsigned n, r;
@@ -234,6 +234,8 @@ enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc)
crt_nc=(v>>(r*8)) & ((1U<<(sizeof(nc_t)*8))-1);
if (crt_nc>=nc)
return NC_REPLAY;
+ if (!update)
+ break;
/* set corresponding array cell byte/short to new nc */
new_v=(v & ~(((1U<<(sizeof(nc_t)*8))-1)<< (r*8)) )|
(nc << (r*8));
diff --git a/src/modules/auth/nc.h b/src/modules/auth/nc.h
index 2f73989aad8..ab9bc0053ed 100644
--- a/src/modules/auth/nc.h
+++ b/src/modules/auth/nc.h
@@ -64,7 +64,7 @@ enum nc_check_ret{
/* check if nonce-count nc w/ index i is expected/valid and record its
* value */
-enum nc_check_ret nc_check_val(nid_t i, unsigned pool, unsigned int nc);
+enum nc_check_ret nc_check_val(nid_t i, unsigned pool, unsigned int nc, int update);
/* re-init the stored nc for nonce id in pool pool_no */
nid_t nc_new(nid_t id, unsigned char pool_no);
diff --git a/src/modules/auth/nonce.c b/src/modules/auth/nonce.c
index b07515084a9..3e28df9be45 100644
--- a/src/modules/auth/nonce.c
+++ b/src/modules/auth/nonce.c
@@ -307,7 +307,7 @@ static inline int l8hex2int(char* _s, unsigned int *_r)
* 6 - nonce reused
*/
int check_nonce(auth_body_t* auth, str* secret1, str* secret2,
- struct sip_msg* msg)
+ struct sip_msg* msg, int update_nonce)
{
str * nonce;
int since, b_nonce2_len, b_nonce_len, cfg;
@@ -418,7 +418,7 @@ if (!memcmp(&b_nonce.n.md5_1[0], &b_nonce2.n.md5_1[0], 16)) {
LM_ERR("bad nc value %.*s\n", auth->digest.nc.len, auth->digest.nc.s);
return 5; /* invalid nc */
}
- switch(nc_check_val(n_id, pf & NF_POOL_NO_MASK, nc)){
+ switch(nc_check_val(n_id, pf & NF_POOL_NO_MASK, nc, update_nonce)){
case NC_OK:
/* don't perform extra checks or one-time nonce checks
* anymore, if we have nc */
diff --git a/src/modules/auth/nonce.h b/src/modules/auth/nonce.h
index e66c1a233a8..2d040513806 100644
--- a/src/modules/auth/nonce.h
+++ b/src/modules/auth/nonce.h
@@ -219,7 +219,7 @@ int calc_nonce(char* nonce, int* nonce_len, int cfg, int since, int expires,
* Check nonce value received from UA
*/
int check_nonce(auth_body_t* auth, str* secret1, str* secret2,
- struct sip_msg* msg);
+ struct sip_msg* msg, int update_nonce);