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);