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
  • Loading branch information
liviuchircu committed Sep 15, 2020
1 parent 6a559d5 commit 27d5862
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 47 deletions.
6 changes: 2 additions & 4 deletions modules/b2b_entities/dlg.c
Expand Up @@ -2774,12 +2774,10 @@ 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);
if (0 == parse_www_authenticate_header(msg, &auth))
break;
case 407:
if (0 == parse_proxy_authenticate_header(msg))
auth = get_proxy_authenticate(msg);
if (0 == 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 @@ -1328,7 +1328,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 @@ -288,11 +288,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 @@ -478,11 +478,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 @@ -142,7 +142,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 @@ -284,10 +284,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 @@ -314,43 +324,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 @@ -359,14 +380,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 @@ -377,14 +399,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, &_);
}


int parse_qop_value(str *val, struct authenticate_body *auth);

Expand Down

0 comments on commit 27d5862

Please sign in to comment.