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)
- <varname>algorithm</varname> (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 - + <varname>algorithm</varname> (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") ... - - + + +
+ +
+ <varname>add_authinfo_hdr</varname> (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);