Browse code

auth: Add flag for not invalidating nc on auth failure

If flag 32 is set then we skip updating nc in pre_auth. On success we
call check_nonce once more to do the update. This can be used to chain
calls to eg. pv_auth_check to authenticate against multiple passwords.

Jon Bergli Heier authored on 03/06/2022 13:43:39
Showing 8 changed files
... ...
@@ -34,7 +34,7 @@
34 34
 #include "rfc2617_sha256.h"
35 35
 #include "challenge.h"
36 36
 
37
-static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body,
37
+static int auth_check_hdr_md5_default(struct sip_msg* msg, auth_body_t* auth_body,
38 38
 		auth_result_t* auth_res);
39 39
 
40 40
 /*
... ...
@@ -94,7 +94,7 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
94 94
 
95 95
 	/* check authorization header field's validity */
96 96
 	if (check_auth_hdr == NULL) {
97
-		check_hf = auth_check_hdr_md5;
97
+		check_hf = auth_check_hdr_md5_default;
98 98
 	} else {	/* use check function of external authentication module */
99 99
 		check_hf = check_auth_hdr;
100 100
 	}
... ...
@@ -113,8 +113,8 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
113 113
  * @result if authentication should continue (1) or not (0)
114 114
  *
115 115
  */
116
-static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
117
-		auth_result_t* auth_res)
116
+int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
117
+		auth_result_t* auth_res, int update_nonce)
118 118
 {
119 119
 	int ret;
120 120
 
... ...
@@ -125,7 +125,7 @@ static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
125 125
 		return 0;
126 126
 	}
127 127
 
128
-	ret = check_nonce(auth, &secret1, &secret2, msg);
128
+	ret = check_nonce(auth, &secret1, &secret2, msg, update_nonce);
129 129
 	if (ret!=0){
130 130
 		if (ret==3 || ret==4){
131 131
 			/* failed auth_extra_checks or stale */
... ...
@@ -145,6 +145,12 @@ static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
145 145
 	return 1;
146 146
 }
147 147
 
148
+static int auth_check_hdr_md5_default(struct sip_msg* msg, auth_body_t* auth,
149
+		auth_result_t* auth_res)
150
+{
151
+	return auth_check_hdr_md5(msg, auth, auth_res, 1);
152
+}
153
+
148 154
 /**
149 155
  * Adds the Authentication-Info header, based on the credentials sent by a successful REGISTER.
150 156
  * @param msg - SIP message to add the header to
... ...
@@ -82,6 +82,9 @@ typedef int (*check_auth_hdr_t)(struct sip_msg* msg, auth_body_t* auth_body,
82 82
 int check_auth_hdr(struct sip_msg* msg, auth_body_t* auth_body,
83 83
 		auth_result_t* auth_res);
84 84
 
85
+int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
86
+		auth_result_t* auth_res, int update_nonce);
87
+
85 88
 /*
86 89
  * Purpose of this function is to find credentials with given realm,
87 90
  * do sanity check, validate credential correctness and determine if
... ...
@@ -476,6 +476,19 @@ int w_has_credentials(sip_msg_t *msg, char* realm, char* s2)
476 476
 	}
477 477
 	return ki_has_credentials(msg, &srealm);
478 478
 }
479
+
480
+#ifdef USE_NC
481
+/**
482
+ * Calls auth_check_hdr_md5 with the update_nonce flag set to false.
483
+ * Used when flag 32 is set in pv_authenticate.
484
+ */
485
+static int auth_check_hdr_md5_noupdate(struct sip_msg* msg, auth_body_t* auth,
486
+		auth_result_t* auth_res)
487
+{
488
+	return auth_check_hdr_md5(msg, auth, auth_res, 0);
489
+}
490
+#endif
491
+
479 492
 /**
480 493
  * @brief do WWW-Digest authentication with password taken from cfg var
481 494
  */
... ...
@@ -490,11 +503,17 @@ int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
490 503
 	avp_value_t val;
491 504
 	static char ha1[256];
492 505
 	struct qp *qop = NULL;
506
+	check_auth_hdr_t check_auth_hdr = NULL;
493 507
 
494 508
 	cred = 0;
495 509
 	ret = AUTH_ERROR;
496 510
 
497
-	switch(pre_auth(msg, realm, hftype, &h, NULL)) {
511
+#ifdef USE_NC
512
+	if (nc_enabled && (flags & 32))
513
+		check_auth_hdr = auth_check_hdr_md5_noupdate;
514
+#endif
515
+
516
+	switch(pre_auth(msg, realm, hftype, &h, check_auth_hdr)) {
498 517
 		case NONCE_REUSED:
499 518
 			LM_DBG("nonce reused");
500 519
 			ret = AUTH_NONCE_REUSED;
... ...
@@ -562,6 +581,16 @@ int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
562 581
 			ret = AUTH_ERROR;
563 582
 	}
564 583
 
584
+#ifdef USE_NC
585
+	/* On success we need to update the nonce if flag 32 is set */
586
+	if (nc_enabled && ret == AUTH_OK && (flags & 32)) {
587
+		if (check_nonce(cred, &secret1, &secret2, msg, 1) < 0) {
588
+			LM_ERR("check_nonce failed after post_auth");
589
+			ret = AUTH_ERROR;
590
+		}
591
+	}
592
+#endif
593
+
565 594
 end:
566 595
 	if (ret < 0) {
567 596
 		/* check if required to add challenge header as avp */
... ...
@@ -274,6 +274,10 @@ if (!auth_check("$fd", "subscriber", "1")) {
274 274
 				<para><emphasis>16</emphasis> - build challenge header with
275 275
 					stale=true</para>
276 276
 			</listitem>
277
+			<listitem>
278
+				<para><emphasis>32</emphasis> - don't invalidate nc on
279
+					authentication failure</para>
280
+			</listitem>
277 281
 
278 282
 			</itemizedlist>
279 283
 		</listitem>
... ...
@@ -213,7 +213,7 @@ nid_t nc_new(nid_t id, unsigned char p)
213 213
  * NC_TOO_BIG       (nc value got too big and cannot be held anymore)
214 214
  * NC_REPLAY        (nc value is <= the current stored one)
215 215
  */
216
-enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc)
216
+enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc, int update)
217 217
 {
218 218
 	unsigned int i;
219 219
 	unsigned n, r;
... ...
@@ -234,6 +234,8 @@ enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc)
234 234
 		crt_nc=(v>>(r*8)) & ((1U<<(sizeof(nc_t)*8))-1);
235 235
 		if (crt_nc>=nc)
236 236
 			return NC_REPLAY;
237
+		if (!update)
238
+			break;
237 239
 		/* set corresponding array cell byte/short to new nc */
238 240
 		new_v=(v & ~(((1U<<(sizeof(nc_t)*8))-1)<< (r*8)) )|
239 241
 				(nc << (r*8));
... ...
@@ -64,7 +64,7 @@ enum nc_check_ret{
64 64
 
65 65
 /* check if nonce-count nc w/ index i is expected/valid and record its
66 66
  * value */
67
-enum nc_check_ret nc_check_val(nid_t i, unsigned pool, unsigned int nc);
67
+enum nc_check_ret nc_check_val(nid_t i, unsigned pool, unsigned int nc, int update);
68 68
 
69 69
 /* re-init the stored nc for nonce id in pool pool_no */
70 70
 nid_t nc_new(nid_t id, unsigned char pool_no);
... ...
@@ -307,7 +307,7 @@ static inline int l8hex2int(char* _s, unsigned int *_r)
307 307
  *          6 - nonce reused
308 308
  */
309 309
 int check_nonce(auth_body_t* auth, str* secret1, str* secret2,
310
-		struct sip_msg* msg)
310
+		struct sip_msg* msg, int update_nonce)
311 311
 {
312 312
 	str * nonce;
313 313
 	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)) {
418 418
 			LM_ERR("bad nc value %.*s\n", auth->digest.nc.len, auth->digest.nc.s);
419 419
 			return 5; /* invalid nc */
420 420
 		}
421
-		switch(nc_check_val(n_id, pf & NF_POOL_NO_MASK, nc)){
421
+		switch(nc_check_val(n_id, pf & NF_POOL_NO_MASK, nc, update_nonce)){
422 422
 			case NC_OK:
423 423
 				/* don't perform extra checks or one-time nonce checks
424 424
 				 * anymore, if we have nc */
... ...
@@ -219,7 +219,7 @@ int calc_nonce(char* nonce, int* nonce_len, int cfg, int since, int expires,
219 219
  * Check nonce value received from UA
220 220
  */
221 221
 int check_nonce(auth_body_t* auth, str* secret1, str* secret2,
222
-					struct sip_msg* msg);
222
+					struct sip_msg* msg, int update_nonce);
223 223
 
224 224
 
225 225