Skip to content

Commit

Permalink
SIP digest auth: Improve handling for multiple digest challenges
Browse files Browse the repository at this point in the history
Before this patch, OpenSIPS would always work with the digest
credentials of the 1st WWW/Proxy-Authenticate header field.

While RFC 3261 does not define the behavior with multiple
WWW/Proxy-Authenticate headers, in § 22.3 it is stated that:

   Note that if an authentication scheme that does not support realms is
   used in the Proxy-Authorization header field, a proxy server MUST
   attempt to parse all Proxy-Authorization header field values to
   determine whether one of them has what the proxy server considers to
   be valid credentials.

... so a proxy must _walk_ through unacceptable headers until it finds
one with "valid" credentials.  In the context of the upcoming RFC 8760,
this would also mean: finding an auth header field with an MD5 digest
algorithm, which is the only one currently supported.

TL;DR: this patch improves the "uac", "uac_registrant" and
"b2b_entities" modules so they correctly process 3 WWW-Authenticate
headers with the following algorithm preference:
    algorithm=SHA-512-256
    algorithm=SHA-256
    algorithm=MD5

... and correctly build an MD5-based response for the 3rd header field.

Issue discovered during OpenSIPIt 2020

(cherry picked from commit 27d5862)

SIP digest auth: Complete 27d5862

The b2b_entities was not patched well, as some weird switch
fallthrough-on-error behavior was introduced.

(cherry picked from commit 5942d53)
(cherry picked from commit 3274fc2)
  • Loading branch information
liviuchircu committed Nov 5, 2020
1 parent f721cc0 commit fced368
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 49 deletions.
10 changes: 4 additions & 6 deletions modules/b2b_entities/dlg.c
Expand Up @@ -2367,13 +2367,11 @@ void b2b_tm_cback(struct cell *t, b2b_table htable, struct tmcb_params *ps)
}
switch(statuscode)
{
case 401:
if (0 == parse_www_authenticate_header(msg))
auth = get_www_authenticate(msg);
case WWW_AUTH_CODE:
parse_www_authenticate_header(msg, &auth);
break;
case 407:
if (0 == parse_proxy_authenticate_header(msg))
auth = get_proxy_authenticate(msg);
case PROXY_AUTH_CODE:
parse_proxy_authenticate_header(msg, &auth);
break;
}
if(uac_auth_loaded && auth && dlg->state == B2B_NEW)
Expand Down
2 changes: 1 addition & 1 deletion modules/sipmsgops/sipmsgops.c
Expand Up @@ -1541,7 +1541,7 @@ static int sip_validate_hdrs(struct sip_msg *msg)

case HDR_PROXY_AUTHENTICATE_T:
case HDR_WWW_AUTHENTICATE_T:
CHECK_HDR_FUNC(parse_authenticate_header, hf);
CHECK_HDR_FUNC(_parse_authenticate_header, hf);
break;

case HDR_CALLID_T:
Expand Down
6 changes: 2 additions & 4 deletions modules/uac/auth.c
Expand Up @@ -286,11 +286,9 @@ int uac_auth( struct sip_msg *msg)
}

if (code==WWW_AUTH_CODE) {
if (0 == parse_www_authenticate_header(rpl))
auth = get_www_authenticate(rpl);
parse_www_authenticate_header(rpl, &auth);
} else if (code==PROXY_AUTH_CODE) {
if (0 == parse_proxy_authenticate_header(rpl))
auth = get_proxy_authenticate(rpl);
parse_proxy_authenticate_header(rpl, &auth);
}

if (auth == NULL) {
Expand Down
6 changes: 2 additions & 4 deletions modules/uac_registrant/registrant.c
Expand Up @@ -411,11 +411,9 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
}

if (statuscode==WWW_AUTH_CODE) {
if (0 == parse_www_authenticate_header(msg))
auth = get_www_authenticate(msg);
parse_www_authenticate_header(msg, &auth);
} else if (statuscode==PROXY_AUTH_CODE) {
if (0 == parse_proxy_authenticate_header(msg))
auth = get_proxy_authenticate(msg);
parse_proxy_authenticate_header(msg, &auth);
}
if (auth == NULL) {
LM_ERR("Unable to extract authentication info\n");
Expand Down
83 changes: 53 additions & 30 deletions parser/parse_authenticate.c
Expand Up @@ -88,7 +88,7 @@ int parse_authenticate_body( str *body, struct authenticate_body *auth)
{
char *p;
char *end;
int n;
int n, ret = 0;
int state;
str name;
str val;
Expand Down Expand Up @@ -229,10 +229,20 @@ int parse_authenticate_body( str *body, struct authenticate_body *auth)
case ALGORITHM_STATE:
if (val.len==3)
{
if ( LOWER4B(GET3B(val.s))==0x6d6435ff) /*MD5*/
if ( LOWER4B(GET3B(val.s))==0x6d6435ff) /* MD5 */
auth->flags |= AUTHENTICATE_MD5;
} else if ((val.len == 11 && ( /* SHA-512-256 */
LOWER4B(GET4B(val.s + 0)) == 0x7368612d &&
LOWER4B(GET4B(val.s + 4)) == 0x3531322d &&
LOWER4B(GET3B(val.s + 8)) == 0x323536ff)) ||
(val.len == 7 && ( /* SHA-256 */
LOWER4B(GET4B(val.s + 0)) == 0x7368612d &&
LOWER4B(GET3B(val.s + 4)) == 0x323536ff))) {
LM_INFO("RFC 8760 (%.*s) is only available "
"in OpenSIPS 3.2+\n", val.len, val.s);
ret = 1;
} else {
LM_ERR("unsupported algorithm \"%.*s\"\n",val.len,val.s);
LM_INFO("bad algorithm \"%.*s\"\n", val.len, val.s);
goto error;
}
break;
Expand All @@ -259,43 +269,54 @@ int parse_authenticate_body( str *body, struct authenticate_body *auth)
goto error;
}

return 0;
return ret;
parse_error:
LM_ERR("parse error in <%.*s> around %ld\n", body->len, body->s, (long)(p-body->s));
error:
return -1;
}


int parse_authenticate_header(struct hdr_field *authenticate)
int parse_authenticate_header(struct hdr_field *authenticate,
struct authenticate_body **picked_auth)
{
void **parsed;
struct authenticate_body *auth_body;
void **parsed;
struct authenticate_body *auth_body;
int rc;

parsed = &(authenticate->parsed);
parsed = &(authenticate->parsed);
*picked_auth = NULL;

while(*parsed == NULL)
{
auth_body = pkg_malloc(sizeof(struct authenticate_body));
if (auth_body == NULL)
while(*parsed == NULL)
{
LM_ERR("oom\n");
return -1;
}
auth_body = pkg_malloc(sizeof(struct authenticate_body));
if (auth_body == NULL)
{
LM_ERR("oom\n");
*picked_auth = NULL;
return -1;
}

if (0 != parse_authenticate_body(&authenticate->body, auth_body))
return -1;
*parsed = auth_body;
rc = parse_authenticate_body(&authenticate->body, auth_body);
if (rc < 0) {
*picked_auth = NULL;
return -1;
}

authenticate = authenticate->sibling;
if (authenticate)
parsed = &(authenticate->parsed);
else
break;
}
return 0;
}
if (rc == 0 && !*picked_auth)
*picked_auth = auth_body;

*parsed = auth_body;

authenticate = authenticate->sibling;
if (authenticate)
parsed = &(authenticate->parsed);
else
break;
}

return picked_auth ? 0 : -1;
}

/*
* This method is used to parse WWW-Authenticate header.
Expand All @@ -304,14 +325,15 @@ int parse_authenticate_header(struct hdr_field *authenticate)
* returns 0 on success,
* -1 on failure.
*/
int parse_www_authenticate_header( struct sip_msg *msg )
int parse_www_authenticate_header(struct sip_msg *msg,
struct authenticate_body **picked_auth)
{
if ( !msg->www_authenticate &&
(parse_headers(msg, HDR_WWW_AUTHENTICATE_F,0)==-1 || !msg->www_authenticate)) {
return -1;
}

return parse_authenticate_header(msg->www_authenticate);
return parse_authenticate_header(msg->www_authenticate, picked_auth);
}


Expand All @@ -322,14 +344,15 @@ int parse_www_authenticate_header( struct sip_msg *msg )
* returns 0 on success,
* -1 on failure.
*/
int parse_proxy_authenticate_header( struct sip_msg *msg )
int parse_proxy_authenticate_header(struct sip_msg *msg,
struct authenticate_body **picked_auth)
{
if ( !msg->proxy_authenticate &&
(parse_headers(msg, HDR_PROXY_AUTHENTICATE_F,0)==-1 || !msg->proxy_authenticate)) {
return -1;
}

return parse_authenticate_header(msg->proxy_authenticate);
return parse_authenticate_header(msg->proxy_authenticate, picked_auth);
}


Expand Down
17 changes: 13 additions & 4 deletions parser/parse_authenticate.h
Expand Up @@ -44,16 +44,25 @@ struct authenticate_body {
str qop;
};

/* casting macro for accessing www/proxy authenticate body */
/* casting macro for accessing the topmost www/proxy authenticate body */
#define get_www_authenticate(p_msg) ((struct authenticate_body*)(p_msg)->www_authenticate->parsed)
#define get_proxy_authenticate(p_msg) ((struct authenticate_body*)(p_msg)->proxy_authenticate->parsed)

/*
* WWW/Proxy-Authenticate header field parser
*/
int parse_proxy_authenticate_header( struct sip_msg *msg );
int parse_www_authenticate_header( struct sip_msg *msg );
int parse_authenticate_header(struct hdr_field *authenticate);
int parse_proxy_authenticate_header(struct sip_msg *msg,
struct authenticate_body **picked_auth);
int parse_www_authenticate_header(struct sip_msg *msg,
struct authenticate_body **picked_auth);
int parse_authenticate_header(struct hdr_field *authenticate,
struct authenticate_body **picked_auth);
static inline int _parse_authenticate_header(struct hdr_field *authenticate)
{
struct authenticate_body *_;
return parse_authenticate_header(authenticate, &_);
}


void free_authenticate(struct authenticate_body *authenticate_b);

Expand Down

0 comments on commit fced368

Please sign in to comment.