Skip to content

Commit

Permalink
auth: add support for Authentication-Info header
Browse files Browse the repository at this point in the history
Add an option to include an Authentication-Info header in the response to a successful authentication.
The header can be enabled with the parameter 'add_authinfo_hdr' module parameter.
The implementation follows RFC2617.
The Authentication-Info header is added inside the function post_auth of the auth api.
The parameter HA1 had to be added to the post_auth api function which requires an
update in all auth_* modules that use the module auth api.
  • Loading branch information
AndreasHuber-CH committed May 25, 2016
1 parent b004753 commit e2ee8b5
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 68 deletions.
90 changes: 89 additions & 1 deletion modules/auth/api.c 100644 → 100755
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/auth/api.h
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion modules/auth/auth_mod.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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}
};

Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions modules/auth/auth_mod.h
Expand Up @@ -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;
Expand Down
107 changes: 66 additions & 41 deletions modules/auth/challenge.c
Expand Up @@ -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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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++;
Expand Down
2 changes: 2 additions & 0 deletions modules/auth/challenge.h
Expand Up @@ -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 */
66 changes: 46 additions & 20 deletions modules/auth/doc/auth_params.xml
Expand Up @@ -695,29 +695,55 @@ modparam("auth", "use_domain", 1)
</section>

<section id="auth.p.algorithm">
<title><varname>algorithm</varname> (string)</title>
<para>
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.
</para>
<para>
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.
</para>
<example>
<title>use SHA-256 example</title>
<programlisting>
<title><varname>algorithm</varname> (string)</title>
<para>
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.
</para>
<para>
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.
</para>
<example>
<title>use SHA-256 example</title>
<programlisting>
...
modparam("auth", "algorithm", "SHA-256")
...
</programlisting>
</example>
</programlisting>
</example>
</section>

<section id="auth.p.add_authinfo_hdr">
<title><varname>add_authinfo_hdr</varname> (boolean)</title>
<para>
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.
</para>
<para>
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.
</para>
<para>
The default value is 0 (no).
</para>
<example>
<title>add Authentication-Info header example</title>
<programlisting>
...
modparam("auth", "add_authinfo_hdr", yes)
...
</programlisting>
</example>
</section>

</section>
2 changes: 1 addition & 1 deletion modules/auth/nonce.c
Expand Up @@ -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 <valid_since> up to <expires>
Expand Down
4 changes: 3 additions & 1 deletion modules/auth/rfc2617.c
Expand Up @@ -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);

Expand Down

0 comments on commit e2ee8b5

Please sign in to comment.