diff --git a/modules/auth/api.c b/modules/auth/api.c
old mode 100644
new mode 100755
index 9557bd7c92e..29312084c0b
--- a/modules/auth/api.c
+++ b/modules/auth/api.c
@@ -27,9 +27,12 @@
#include "../../parser/digest/digest.h"
#include "../../sr_module.h"
#include "../../ut.h"
+#include "../../data_lump_rpl.h"
#include "auth_mod.h"
#include "nonce.h"
+#include "ot_nonce.h"
#include "rfc2617_sha256.h"
+#include "challenge.h"
static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body,
auth_result_t* auth_res);
@@ -142,14 +145,60 @@ static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
return 1;
}
+/**
+ * Adds the Authentication-Info header, based on the credentials sent by a successful REGISTER.
+ * @param msg - SIP message to add the header to
+ * @returns 1 on success, 0 on error
+ */
+static int add_authinfo_resp_hdr(struct sip_msg *msg, char* next_nonce, int nonce_len, str qop, char* rspauth, str cnonce, str nc)
+{
+ str authinfo_hdr;
+ static const char authinfo_fmt[] = "Authentication-Info: "
+ "nextnonce=\"%.*s\", "
+ "qop=%.*s, "
+ "rspauth=\"%.*s\", "
+ "cnonce=\"%.*s\", "
+ "nc=%.*s\r\n";
+
+ authinfo_hdr.len = sizeof (authinfo_fmt) + nonce_len + qop.len + hash_hex_len + cnonce.len + nc.len - 20 /* format string parameters */ - 1 /* trailing \0 */;
+ authinfo_hdr.s = pkg_malloc(authinfo_hdr.len + 1);
+
+ if (!authinfo_hdr.s) {
+ LM_ERR("add_authinfo_resp_hdr: Error allocating %d bytes\n", authinfo_hdr.len);
+ goto error;
+ }
+ snprintf(authinfo_hdr.s, authinfo_hdr.len + 1, authinfo_fmt,
+ nonce_len, next_nonce,
+ qop.len, qop.s,
+ hash_hex_len, rspauth,
+ cnonce.len, cnonce.s,
+ nc.len, nc.s);
+ LM_DBG("authinfo hdr built: %.*s", authinfo_hdr.len, authinfo_hdr.s);
+ if (add_lump_rpl(msg, authinfo_hdr.s, authinfo_hdr.len, LUMP_RPL_HDR)!=0) {
+ LM_DBG("authinfo hdr added");
+ pkg_free(authinfo_hdr.s);
+ return 1;
+ }
+error:
+ if (authinfo_hdr.s) pkg_free(authinfo_hdr.s);
+ return 0;
+}
+
/*
* Purpose of this function is to do post authentication steps like
* marking authorized credentials and so on.
*/
-auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr)
+auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr, char* ha1)
{
int res = AUTHENTICATED;
auth_body_t* c;
+ dig_cred_t* d;
+ HASHHEX_SHA256 rspauth;
+#ifdef USE_OT_NONCE
+ char next_nonce[MAX_NONCE_LEN];
+ int nonce_len;
+ int cfg;
+#endif
c = (auth_body_t*)((hdr)->parsed);
@@ -167,6 +216,45 @@ auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr)
res = NOT_AUTHENTICATED;
}
}
+ else if (add_authinfo_hdr) {
+ if (unlikely(!ha1)) {
+ LM_ERR("add_authinfo_hdr is configured but the auth_* module "
+ "you are using does not provide the ha1 value to post_auth\n");
+ }
+ else {
+ d = &c->digest;
+
+ /* calculate rspauth */
+ calc_response(ha1,
+ &d->nonce,
+ &d->nc,
+ &d->cnonce,
+ &d->qop.qop_str,
+ d->qop.qop_parsed == QOP_AUTHINT,
+ 0, /* method is empty for rspauth */
+ &d->uri,
+ NULL, /* TODO should be H(entity-body) if auth-int should be supported */
+ rspauth);
+
+ /* calculate new next nonce if otn is enabled */
+#ifdef USE_OT_NONCE
+ if (otn_enabled) {
+ cfg = get_auth_checks(msg);
+ nonce_len = sizeof(next_nonce);
+ if (unlikely(calc_new_nonce(next_nonce, &nonce_len, cfg, msg) != 0)) {
+ LM_ERR("auth: calc_nonce failed (len %d, needed %d). authinfo hdr is not added.\n",
+ (int) sizeof(next_nonce), nonce_len);
+ }
+ else {
+ add_authinfo_resp_hdr(msg, next_nonce, nonce_len, d->qop.qop_str, rspauth, d->cnonce, d->nc);
+ }
+ }
+ else
+#endif
+ /* use current nonce as next nonce */
+ add_authinfo_resp_hdr(msg, d->nonce.s, d->nonce.len, d->qop.qop_str, rspauth, d->cnonce, d->nc);
+ }
+ }
return res;
}
diff --git a/modules/auth/api.h b/modules/auth/api.h
index f61292322c9..5fd1d617078 100644
--- a/modules/auth/api.h
+++ b/modules/auth/api.h
@@ -99,8 +99,8 @@ auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
* marking authorized credentials and so on.
*/
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);
+ struct hdr_field* hdr, char* ha1);
+auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr, char* ha1);
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);
diff --git a/modules/auth/auth_mod.c b/modules/auth/auth_mod.c
index ce000104809..940b314e399 100644
--- a/modules/auth/auth_mod.c
+++ b/modules/auth/auth_mod.c
@@ -137,6 +137,7 @@ static struct qp auth_qauthint = {
/* Hash algorithm used for digest authentication, MD5 if empty */
str auth_algorithm = {"", 0};
int hash_hex_len;
+int add_authinfo_hdr = 0; /* should an Authentication-Info header be added on 200 OK responses? */
calc_HA1_t calc_HA1;
calc_response_t calc_response;
@@ -204,6 +205,7 @@ static param_export_t params[] = {
{"realm_prefix", PARAM_STRING, &auth_realm_prefix.s },
{"use_domain", PARAM_INT, &auth_use_domain },
{"algorithm", PARAM_STR, &auth_algorithm },
+ {"add_authinfo_hdr", INT_PARAM, &add_authinfo_hdr },
{0, 0, 0}
};
@@ -538,7 +540,7 @@ int pv_authenticate(struct sip_msg *msg, str *realm, str *passwd,
ret = auth_check_response(&(cred->digest), method, ha1);
if(ret==AUTHENTICATED) {
ret = AUTH_OK;
- switch(post_auth(msg, h)) {
+ switch(post_auth(msg, h, ha1)) {
case AUTHENTICATED:
break;
default:
diff --git a/modules/auth/auth_mod.h b/modules/auth/auth_mod.h
index 5bf075bc920..e3f13b054ef 100644
--- a/modules/auth/auth_mod.h
+++ b/modules/auth/auth_mod.h
@@ -44,6 +44,7 @@ extern str proxy_challenge_header;
extern str www_challenge_header;
extern struct qp auth_qop;
extern str auth_algorithm;
+extern int add_authinfo_hdr; /* should an Authentication-Info header be added on 200 OK responses? */
extern int hash_hex_len;
extern calc_HA1_t calc_HA1;
diff --git a/modules/auth/challenge.c b/modules/auth/challenge.c
index 1f9c5d0624b..88cb7096e16 100644
--- a/modules/auth/challenge.c
+++ b/modules/auth/challenge.c
@@ -73,6 +73,66 @@ void strip_realm(str* _realm)
return;
}
+/**
+ * Calculate a new nonce.
+ * @param nonce Pointer to a buffer of *nonce_len. It must have enough
+ * space to hold the nonce. MAX_NONCE_LEN should be always
+ * safe.
+ * @param nonce_len A value/result parameter. Initially it contains the
+ * nonce buffer length. If the length is too small, it
+ * will be set to the needed length and the function will
+ * return error immediately. After a succesfull call it will
+ * contain the size of nonce written into the buffer,
+ * without the terminating 0.
+ * @param cfg This is the value of one of the three module parameters that
+ * control which optional checks are enabled/disabled and which
+ * parts of the message will be included in the nonce string.
+ * @param msg The message for which the nonce is computed. If
+ * auth_extra_checks is set, the MD5 of some fields of the
+ * message will be included in the generated nonce.
+ * @return 0 on success and -1 on error
+ */
+int calc_new_nonce(char* nonce, int *nonce_len, int cfg, struct sip_msg* msg)
+{
+ int t;
+#if defined USE_NC || defined USE_OT_NONCE
+ unsigned int n_id;
+ unsigned char pool;
+ unsigned char pool_flags;
+#endif
+
+ t=time(0);
+#if defined USE_NC || defined USE_OT_NONCE
+ if (nc_enabled || otn_enabled){
+ pool=nid_get_pool();
+ n_id=nid_inc(pool);
+ pool_flags=0;
+#ifdef USE_NC
+ if (nc_enabled){
+ nc_new(n_id, pool);
+ pool_flags|= NF_VALID_NC_ID;
+ }
+#endif
+#ifdef USE_OT_NONCE
+ if (otn_enabled){
+ otn_new(n_id, pool);
+ pool_flags|= NF_VALID_OT_ID;
+ }
+#endif
+ }else{
+ pool=0;
+ pool_flags=0;
+ n_id=0;
+ }
+ return calc_nonce(nonce, nonce_len, cfg, t, t + nonce_expire, n_id,
+ pool | pool_flags,
+ &secret1, &secret2, msg);
+#else /* USE_NC || USE_OT_NONCE*/
+ return calc_nonce(nonce, nonce_len, cfg, t, t + nonce_expire,
+ &secret1, &secret2, msg);
+#endif /* USE_NC || USE_OT_NONCE */
+}
+
/**
* Create and return {WWW,Proxy}-Authenticate header field
@@ -93,12 +153,6 @@ int get_challenge_hf(struct sip_msg* msg, int stale, str* realm,
char *p;
str* hfn, hf;
int nonce_len, l, cfg;
- int t;
-#if defined USE_NC || defined USE_OT_NONCE
- unsigned int n_id;
- unsigned char pool;
- unsigned char pool_flags;
-#endif
if(!ahf)
{
@@ -183,42 +237,13 @@ int get_challenge_hf(struct sip_msg* msg, int stale, str* realm,
}
else {
l=nonce_len;
- t=time(0);
-#if defined USE_NC || defined USE_OT_NONCE
- if (nc_enabled || otn_enabled){
- pool=nid_get_pool();
- n_id=nid_inc(pool);
- pool_flags=0;
-#ifdef USE_NC
- if (nc_enabled){
- nc_new(n_id, pool);
- pool_flags|= NF_VALID_NC_ID;
- }
-#endif
-#ifdef USE_OT_NONCE
- if (otn_enabled){
- otn_new(n_id, pool);
- pool_flags|= NF_VALID_OT_ID;
- }
-#endif
- }else{
- pool=0;
- pool_flags=0;
- n_id=0;
+ if (calc_new_nonce(p, &l, cfg, msg) != 0)
+ {
+ ERR("auth: calc_nonce failed (len %d, needed %d)\n",
+ nonce_len, l);
+ pkg_free(hf.s);
+ return -1;
}
- if (calc_nonce(p, &l, cfg, t, t + nonce_expire, n_id,
- pool | pool_flags,
- &secret1, &secret2, msg) != 0)
-#else /* USE_NC || USE_OT_NONCE*/
- if (calc_nonce(p, &l, cfg, t, t + nonce_expire,
- &secret1, &secret2, msg) != 0)
-#endif /* USE_NC || USE_OT_NONCE */
- {
- ERR("auth: calc_nonce failed (len %d, needed %d)\n",
- nonce_len, l);
- pkg_free(hf.s);
- return -1;
- }
p += l;
}
*p = '"'; p++;
diff --git a/modules/auth/challenge.h b/modules/auth/challenge.h
index 843fecabe9b..f2be1059279 100644
--- a/modules/auth/challenge.h
+++ b/modules/auth/challenge.h
@@ -49,4 +49,6 @@ int get_challenge_hf(struct sip_msg* msg, int stale, str* realm,
void strip_realm(str* _realm);
+int calc_new_nonce(char* nonce, int *nonce_len, int cfg, struct sip_msg* msg);
+
#endif /* CHALLENGE_H */
diff --git a/modules/auth/doc/auth_params.xml b/modules/auth/doc/auth_params.xml
index bc687f6277b..8ae077a7f4e 100644
--- a/modules/auth/doc/auth_params.xml
+++ b/modules/auth/doc/auth_params.xml
@@ -695,29 +695,55 @@ modparam("auth", "use_domain", 1)
- algorithm (string)
-
- Configure hash algorithm used for digest authentication.
- Possible values are "MD5" or "SHA-256". If left empty MD5 is used.
- If specified, the specified algorithm is used and is also but in
- the 'algorithm' field of the challenge header.
-
-
- Warning: SHA-256 hash values take twice the space of MD5 hash values.
- So a buffer overflow might occur if this option is used in combination
- with another auth_* module that does not allocate at least 65 bytes to
- store hash values.
- SHA-256 can safely be used with the module auth_db as it allocates 256 bytes
- to store HA1 values.
-
-
- use SHA-256 example
-
+ algorithm (string)
+
+ Configure hash algorithm used for digest authentication.
+ Possible values are "MD5" or "SHA-256". If left empty MD5 is used.
+ If specified, the specified algorithm is used and is also put in
+ the 'algorithm' field of the challenge header.
+
+
+ Warning: SHA-256 hash values take twice the space of MD5 hash values.
+ So a buffer overflow might occur if this option is used in combination
+ with another auth_* module that does not allocate at least 65 bytes to
+ store hash values.
+ SHA-256 can safely be used with the module auth_db as it allocates 256 bytes
+ to store HA1 values.
+
+
+ use SHA-256 example
+
...
modparam("auth", "algorithm", "SHA-256")
...
-
-
+
+
+
+
+
+ add_authinfo_hdr (boolean)
+
+ Should an Authentication-Info header be added on 200 OK responses?
+ The Authentication-Info header offers mutual authentication.
+ The server proves to the client that it knows the user's secret.
+
+
+ The header also includes the next nonce which may be used by the client
+ in a future request.
+ If one_time_nonce is enabled, a new nonce is calculated for the next nonce.
+ Otherwise the current nonce is used for the next nonce.
+
+
+ The default value is 0 (no).
+
+
+ add Authentication-Info header example
+
+...
+modparam("auth", "add_authinfo_hdr", yes)
+...
+
+
diff --git a/modules/auth/nonce.c b/modules/auth/nonce.c
index 3cd8d75877a..aba36d4db8d 100644
--- a/modules/auth/nonce.c
+++ b/modules/auth/nonce.c
@@ -178,7 +178,7 @@ inline static int calc_bin_nonce_md5(union bin_nonce* b_nonce, int cfg,
* return error immediately. After a succesfull call it will
* contain the size of nonce written into the buffer,
* without the terminating 0.
- * @param cfg This is the value of one of the tree module parameters that
+ * @param cfg This is the value of one of the three module parameters that
* control which optional checks are enabled/disabled and which
* parts of the message will be included in the nonce string.
* @param since Time when nonce was created, i.e. nonce is valid since up to
diff --git a/modules/auth/rfc2617.c b/modules/auth/rfc2617.c
index 844220c8998..56bcc992c0e 100644
--- a/modules/auth/rfc2617.c
+++ b/modules/auth/rfc2617.c
@@ -110,7 +110,9 @@ void calc_response_md5(HASHHEX _ha1, /* H(A1) */
/* calculate H(A2) */
MD5Init(&Md5Ctx);
- MD5Update(&Md5Ctx, _method->s, _method->len);
+ if (_method) {
+ MD5Update(&Md5Ctx, _method->s, _method->len);
+ }
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, _uri->s, _uri->len);
diff --git a/modules/auth/rfc2617_sha256.c b/modules/auth/rfc2617_sha256.c
index 1240d6bf2ea..7a1ab66b0da 100644
--- a/modules/auth/rfc2617_sha256.c
+++ b/modules/auth/rfc2617_sha256.c
@@ -115,7 +115,9 @@ void calc_response_sha256(HASHHEX_SHA256 _ha1, /* H(A1) */
/* calculate H(A2) */
sr_SHA256_Init(&Sha256Ctx);
- SHA256_Update(&Sha256Ctx, _method->s, _method->len);
+ if (_method) {
+ SHA256_Update(&Sha256Ctx, _method->s, _method->len);
+ }
SHA256_Update(&Sha256Ctx, ":", 1);
SHA256_Update(&Sha256Ctx, _uri->s, _uri->len);