Commit fa46c5a5 authored by Gautier Pelloux-Prayer's avatar Gautier Pelloux-Prayer
Browse files

Reuse previous nonce if outbound proxy realm is set to avoid reauthentication

parent b3012e59
......@@ -62,11 +62,12 @@ BELLESIP_EXPORT void belle_sip_provider_clean_channels(belle_sip_provider_t *p);
* @param request to be updated
* @param resp response to take authentication values from, might be NULL
* @param from_uri optional - an uri to use instead of the from of the request, which can be anonymous.
* @param auth_infos A newly allocated belle_sip_auth_info_t object is added to this list. These object contains useful information like realm and username.
* @param auth_infos optional - A newly allocated belle_sip_auth_info_t object is added to this list. These object contains useful information like realm and username.
* @param realm optional - If an outbound proxy realm is used, nounce can be reused from previous request to avoid re-authentication.
* @returns 0 in case of success,
*
**/
BELLESIP_EXPORT int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request,belle_sip_response_t *resp, belle_sip_uri_t *from_uri, belle_sip_list_t** auth_infos);
BELLESIP_EXPORT int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request,belle_sip_response_t *resp, belle_sip_uri_t *from_uri, belle_sip_list_t** auth_infos, const char* realm);
/**
* Can be used to simulate network recv error, for tests.
......@@ -110,7 +111,7 @@ BELLESIP_EXPORT int belle_sip_provider_is_rport_enabled(belle_sip_provider_t *pr
/**
* Enable discovery of NAT's public address and port during SIP exchanges.
* When activated, automatic contacts ( see belle_sip_header_contact_set_automatic() )
* When activated, automatic contacts ( see belle_sip_header_contact_set_automatic() )
* will use discovered public IP address and port (if any) instead of local ones.
* NAT public address and port are discovered using received and rport parameters in via header of responses.
* As a result, disabling rport ( see belle_sip_provider_enable_rport() ) will also break this feature.
......
......@@ -72,6 +72,16 @@ BELLESIP_EXPORT int belle_sip_refresher_get_retry_after(const belle_sip_refreshe
*/
BELLESIP_EXPORT void belle_sip_refresher_set_retry_after(belle_sip_refresher_t* refresher, int delay_ms);
/**
* returns realm of the outbound proxy used for authentication, if any
*/
BELLESIP_EXPORT const char* belle_sip_refresher_get_realm(const belle_sip_refresher_t* refresher);
/**
* Realm of the outbound proxy used for authentication, if any
*/
BELLESIP_EXPORT void belle_sip_refresher_set_realm(belle_sip_refresher_t* refresher, const char* realm);
/**
* get current client transaction
* @param refresher object
......
......@@ -78,9 +78,10 @@ BELLESIP_EXPORT belle_sip_refresher_t* belle_sip_client_transaction_create_refre
* <br>This function, update cseq, put route set and try to fill authorization headers. Initial request is not cloned.
* @param transaction . must be in state completed
* @param auth_infos if auth infos cannot be added for an authenticate header,
* @param realm optional - If an outbound proxy realm is used, digestion authentication can be optimized.
* a newly allocated belle_sip_auth_info_t object is added to this list. These object contains useful information like realm and username. May be NULL
* */
BELLESIP_EXPORT belle_sip_request_t* belle_sip_client_transaction_create_authenticated_request(belle_sip_client_transaction_t *t,belle_sip_list_t** auth_infos);
BELLESIP_EXPORT belle_sip_request_t* belle_sip_client_transaction_create_authenticated_request(belle_sip_client_transaction_t *t,belle_sip_list_t** auth_infos,const char* realm);
#define BELLE_SIP_TRANSACTION(t) BELLE_SIP_CAST(t,belle_sip_transaction_t)
#define BELLE_SIP_SERVER_TRANSACTION(t) BELLE_SIP_CAST(t,belle_sip_server_transaction_t)
......
/*
belle-sip - SIP (RFC3261) library.
Copyright (C) 2010 Belledonne Communications SARL
Copyright (C) 2010 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "belle_sip_internal.h"
......@@ -28,6 +28,7 @@ typedef struct authorization_context {
const char* nonce;
const char* qop;
const char* opaque;
const char* user_id;
int nonce_count;
int is_proxy;
}authorization_context_t;
......@@ -37,6 +38,7 @@ GET_SET_STRING(authorization_context,nonce)
GET_SET_STRING(authorization_context,qop)
GET_SET_STRING(authorization_context,scheme)
GET_SET_STRING(authorization_context,opaque)
GET_SET_STRING(authorization_context,user_id)
GET_SET_INT(authorization_context,nonce_count,int)
static authorization_context_t* belle_sip_authorization_create(belle_sip_header_call_id_t* call_id) {
authorization_context_t* result = malloc(sizeof(authorization_context_t));
......@@ -51,6 +53,7 @@ static void belle_sip_authorization_destroy(authorization_context_t* object) {
DESTROY_STRING(object,nonce);
DESTROY_STRING(object,qop);
DESTROY_STRING(object,opaque);
DESTROY_STRING(object,user_id);
belle_sip_object_unref(object->callid);
belle_sip_free(object);
}
......@@ -136,11 +139,17 @@ static void belle_sip_provider_dispatch_request(belle_sip_provider_t* prov, bell
}
}
static belle_sip_list_t* belle_sip_provider_get_auth_context_by_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id);
static belle_sip_list_t* belle_sip_provider_get_auth_context_by_realm_or_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_uri_t *from_uri,const char* realm);
static void belle_sip_provider_dispatch_response(belle_sip_provider_t* prov, belle_sip_response_t *msg){
static int belle_sip_auth_context_find_by_nonce(const void* elem, const void* nonce_value){
authorization_context_t * a = (authorization_context_t*)elem;
return strcmp(a->nonce, (const char*)nonce_value);
}
static void belle_sip_provider_dispatch_response(belle_sip_provider_t* p, belle_sip_response_t *msg){
belle_sip_client_transaction_t *t;
t=belle_sip_provider_find_matching_client_transaction(prov,msg);
t=belle_sip_provider_find_matching_client_transaction(p,msg);
/*good opportunity to cleanup auth context if answer = 401|407|403*/
......@@ -148,13 +157,20 @@ static void belle_sip_provider_dispatch_response(belle_sip_provider_t* prov, bel
case 401:
case 403:
case 407: {
belle_sip_header_call_id_t* call_id=call_id = belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t);
belle_sip_list_t* iterator;
belle_sip_list_t* head=belle_sip_provider_get_auth_context_by_call_id(prov,call_id);
for (iterator=head;iterator!=NULL;iterator=iterator->next){
prov->auth_contexts=belle_sip_list_remove(prov->auth_contexts,iterator->data);
const char* nonce = NULL;
belle_sip_message_t* req = BELLE_SIP_MESSAGE(belle_sip_transaction_get_request((belle_sip_transaction_t*)t));
belle_sip_header_authorization_t* authorization=BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type(req, belle_sip_header_proxy_authorization_t));
if (authorization==NULL) authorization=belle_sip_message_get_header_by_type(req, belle_sip_header_authorization_t);
if (authorization!=NULL){
nonce = belle_sip_header_authorization_get_nonce(authorization);
if (nonce != NULL){
belle_sip_list_t * auth_context_with_nonce = NULL;
while ((auth_context_with_nonce = belle_sip_list_find_custom(p->auth_contexts, belle_sip_auth_context_find_by_nonce, nonce)) != NULL){
belle_sip_authorization_destroy(auth_context_with_nonce->data);
p->auth_contexts = belle_sip_list_delete_link(p->auth_contexts, auth_context_with_nonce);
}
}
}
belle_sip_list_free_with_data(head,(void (*)(void *))belle_sip_authorization_destroy);
}
}
/*
......@@ -169,11 +185,11 @@ static void belle_sip_provider_dispatch_response(belle_sip_provider_t* prov, bel
belle_sip_object_unref(t);
}else{
belle_sip_response_event_t event;
event.source=(belle_sip_object_t*)prov;
event.source=(belle_sip_object_t*)p;
event.client_transaction=NULL;
event.dialog=NULL;
event.response=msg;
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_response_event,&event);
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(p->listeners,process_response_event,&event);
}
}
......@@ -224,18 +240,18 @@ static void compute_hash_from_invariants(belle_sip_message_t *msg, char *branchi
belle_sip_header_via_t *prev_via=NULL;
const belle_sip_list_t *vias=belle_sip_message_get_headers(msg,"via");
int is_request=belle_sip_message_is_request(msg);
if (vias){
via=(belle_sip_header_via_t*)vias->data;
if (vias->next){
prev_via=(belle_sip_header_via_t*)vias->next->data;
}
}
if (is_request){
requri=belle_sip_request_get_uri(BELLE_SIP_REQUEST(msg));
}
belle_sip_md5_init(&ctx);
if (initial)
belle_sip_md5_append(&ctx,(uint8_t*)initial,strlen(initial));
......@@ -271,7 +287,7 @@ static void compute_hash_from_invariants(belle_sip_message_t *msg, char *branchi
* RFC2543 10.1.2:
* "Responses are mapped to requests by the matching To, From, Call-ID,
* CSeq headers and the branch parameter of the first Via header."
*
*
* to-tag must not be used because an ACK will contain one while original INVITE will not.
* Cseq's method is changed for CANCEL so we must not use it as well.
**/
......@@ -287,16 +303,16 @@ static char *compute_rfc2543_branch(belle_sip_request_t *req, char *branchid, si
char *from_str=belle_sip_object_to_string(from);
belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(req,belle_sip_header_to_t);
char *to_str=belle_sip_object_to_string(belle_sip_header_address_get_uri((belle_sip_header_address_t*)to));
belle_sip_md5_init(&ctx);
belle_sip_md5_append(&ctx,(uint8_t*)from_str,strlen(from_str));
belle_sip_md5_append(&ctx,(uint8_t*)to_str,strlen(to_str));
belle_sip_md5_append(&ctx,(uint8_t*)callid,strlen(callid));
belle_sip_md5_append(&ctx,(uint8_t*)&cseq,sizeof(cseq));
belle_sip_free(from_str);
belle_sip_free(to_str);
if (v_branch)
belle_sip_md5_append(&ctx,(uint8_t*)v_branch,strlen(v_branch));
......@@ -313,7 +329,7 @@ static void fix_outgoing_via(belle_sip_provider_t *p, belle_sip_channel_t *chan,
belle_sip_header_via_set_port(via,chan->local_port);
belle_sip_header_via_set_protocol(via,"SIP/2.0");
belle_sip_header_via_set_transport(via,belle_sip_channel_get_transport_name(chan));
if (belle_sip_header_via_get_branch(via)==NULL){
/*branch id should not be set random here (stateless forwarding): but rather a hash of message invariants*/
char branchid[24];
......@@ -375,7 +391,7 @@ static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_chan
for (contacts=belle_sip_message_get_headers(msg,"Contact");contacts!=NULL;contacts=contacts->next){
const char *transport;
contact=(belle_sip_header_contact_t*)contacts->data;
if (belle_sip_header_contact_is_wildcard(contact)) continue;
/* fix the contact if in automatic mode or null uri (for backward compatibility)*/
if (!(contact_uri = belle_sip_header_address_get_uri((belle_sip_header_address_t*)contact))) {
......@@ -386,7 +402,7 @@ static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_chan
belle_sip_header_contact_set_automatic(contact,TRUE);
}
if (!belle_sip_header_contact_get_automatic(contact)) continue;
if (ip==NULL){
if (prov->nat_helper){
ip=chan->public_ip ? chan->public_ip : chan->local_ip;
......@@ -397,7 +413,7 @@ static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_chan
port=chan->local_port;
}
}
belle_sip_uri_set_host(contact_uri,ip);
transport=belle_sip_channel_get_transport_name_lower_case(chan);
if (strcmp(transport,"udp")==0){
......@@ -417,10 +433,10 @@ static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_chan
* According to RFC3261, content-length is mandatory for stream based transport, but optional for datagram transport.
* However some servers (opensips) are confused when they receive a SIP/UDP packet without Content-Length (they shouldn't).
*/
if (!content_length
if (!content_length
#ifndef BELLE_SIP_FORCE_CONTENT_LENGTH
&& strcasecmp("udp",belle_sip_channel_get_transport_name(chan))!=0
#endif
#endif
) {
content_length = belle_sip_header_content_length_create(0);
belle_sip_message_add_header(msg,(belle_sip_header_t*)content_length);
......@@ -436,7 +452,7 @@ BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_provider_t,belle_sip_channel_liste
BELLE_SIP_IMPLEMENT_INTERFACE_END
BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_sip_provider_t,belle_sip_channel_listener_t);
BELLE_SIP_INSTANCIATE_VPTR(belle_sip_provider_t,belle_sip_object_t,belle_sip_provider_uninit,NULL,NULL,FALSE);
belle_sip_provider_t *belle_sip_provider_new(belle_sip_stack_t *s, belle_sip_listening_point_t *lp){
......@@ -487,7 +503,7 @@ static belle_sip_channel_t* _belle_sip_provider_find_channel_using_routable(bell
* In order to do that, we go through all the channels and ask them their routable uri, and see if it matches the uri passed in argument.
* This creates a lot of temporary objects and iterates through a potentially long list of routables.
* Some more efficient solutions could be:
* 1- insert a magic cookie parameter in each routable created by the provider, so that recognition is immediate.
* 1- insert a magic cookie parameter in each routable created by the provider, so that recognition is immediate.
* Drawback: use of non-standard, possibly conflicting parameter.
* 2- check the listening point's uri first (but need to match the ip address to any local ip if it is INADDR_ANY), then use belle_sip_listening_point_get_channel()
* to see if a channel is matching.
......@@ -532,7 +548,7 @@ const belle_sip_list_t *belle_sip_provider_get_listening_points(belle_sip_provid
void belle_sip_provider_add_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l, int prepend){
if (prepend)
p->internal_listeners=belle_sip_list_prepend(p->internal_listeners,l);
else
else
p->internal_listeners=belle_sip_list_append(p->internal_listeners,l);
}
......@@ -561,7 +577,7 @@ belle_sip_dialog_t * belle_sip_provider_create_dialog(belle_sip_provider_t *prov
belle_sip_dialog_t * belle_sip_provider_create_dialog_internal(belle_sip_provider_t *prov, belle_sip_transaction_t *t,unsigned int check_last_resp){
belle_sip_dialog_t *dialog=NULL;
if (check_last_resp && t->last_response){
int code=belle_sip_response_get_status_code(t->last_response);
if (code>=200 && code<300){
......@@ -580,7 +596,7 @@ belle_sip_dialog_t * belle_sip_provider_create_dialog_internal(belle_sip_provide
/*find a dialog given the call id, from-tag and to-tag*/
belle_sip_dialog_t* belle_sip_provider_find_dialog(const belle_sip_provider_t *prov, const char* call_id, const char* from_tag, const char* to_tag) {
belle_sip_list_t* iterator;
for(iterator=prov->dialogs;iterator!=NULL;iterator=iterator->next) {
belle_sip_dialog_t* dialog=(belle_sip_dialog_t*)iterator->data;
if (belle_sip_dialog_get_state(dialog) != BELLE_SIP_DIALOG_NULL && strcmp(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(dialog)),call_id)==0) {
......@@ -612,20 +628,20 @@ belle_sip_dialog_t *belle_sip_provider_find_dialog_from_message(belle_sip_provid
const char *to_tag;
const char *call_id_value;
const char *local_tag,*remote_tag;
if (belle_sip_message_is_request(msg)){
belle_sip_request_t *req=BELLE_SIP_REQUEST(msg);
if (req->dialog)
return req->dialog;
}
to=belle_sip_message_get_header_by_type(msg,belle_sip_header_to_t);
if (to==NULL || (to_tag=belle_sip_header_to_get_tag(to))==NULL){
/* a request without to tag cannot be part of a dialog */
return NULL;
}
call_id=belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t);
from=belle_sip_message_get_header_by_type(msg,belle_sip_header_from_t);
......@@ -708,7 +724,7 @@ belle_sip_server_transaction_t *belle_sip_provider_create_server_transaction(bel
}else if (strcmp(belle_sip_request_get_method(req),"ACK")==0){
belle_sip_error("Creating a server transaction for an ACK is not a good idea, probably");
return NULL;
}else
}else
t=(belle_sip_server_transaction_t*)belle_sip_nist_new(prov,req);
belle_sip_transaction_set_dialog((belle_sip_transaction_t*)t,belle_sip_provider_find_dialog_from_message(prov,(belle_sip_message_t*)req,TRUE));
belle_sip_provider_add_server_transaction(prov,t);
......@@ -723,7 +739,7 @@ belle_sip_channel_t * belle_sip_provider_get_channel(belle_sip_provider_t *p, co
belle_sip_list_t *l;
belle_sip_listening_point_t *candidate=NULL,*lp;
belle_sip_channel_t *chan;
if (hop->transport!=NULL) {
for(l=p->lps;l!=NULL;l=l->next){
lp=(belle_sip_listening_point_t*)l->data;
......@@ -750,7 +766,7 @@ void belle_sip_provider_release_channel(belle_sip_provider_t *p, belle_sip_chann
void belle_sip_provider_clean_channels(belle_sip_provider_t *p){
belle_sip_list_t *l;
belle_sip_listening_point_t *lp;
for(l=p->lps;l!=NULL;l=l->next){
lp=(belle_sip_listening_point_t*)l->data;
belle_sip_listening_point_clean_channels(lp);
......@@ -790,7 +806,7 @@ void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_respons
void belle_sip_provider_set_transaction_terminated(belle_sip_provider_t *p, belle_sip_transaction_t *t){
belle_sip_transaction_terminated_event_t ev;
BELLE_SIP_OBJECT_VPTR(t,belle_sip_transaction_t)->on_terminate(t);
ev.source=t->provider;
ev.transaction=t;
......@@ -820,8 +836,8 @@ static int client_transaction_match(const void *p_tr, const void *p_matcher){
return -1;
}
belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction(belle_sip_provider_t *prov,
belle_sip_response_t *resp){
belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction(belle_sip_provider_t *prov,
belle_sip_response_t *resp){
struct client_transaction_matcher matcher;
belle_sip_header_via_t *via=(belle_sip_header_via_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"via");
belle_sip_header_cseq_t *cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"cseq");
......@@ -845,7 +861,7 @@ belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transac
return ret;
}
void belle_sip_provider_remove_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t){
void belle_sip_provider_remove_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t){
belle_sip_list_t* elem=belle_sip_list_find(prov->client_transactions,t);
if (elem) {
prov->client_transactions=belle_sip_list_delete_link(prov->client_transactions,elem);
......@@ -885,12 +901,12 @@ belle_sip_transaction_t * belle_sip_provider_find_matching_transaction(belle_sip
belle_sip_list_t *elem=NULL;
const char *branch;
char token[BELLE_SIP_BRANCH_ID_LENGTH];
matcher.method=belle_sip_request_get_method(req);
matcher.is_ack_or_cancel=(strcmp(matcher.method,"ACK")==0 || strcmp(matcher.method,"CANCEL")==0);
if (via!=NULL && (branch=belle_sip_header_via_get_branch(via))!=NULL &&
if (via!=NULL && (branch=belle_sip_header_via_get_branch(via))!=NULL &&
strncmp(branch,BELLE_SIP_BRANCH_MAGIC_COOKIE,strlen(BELLE_SIP_BRANCH_MAGIC_COOKIE))==0){
matcher.branchid=branch;
}else{
......@@ -901,7 +917,7 @@ belle_sip_transaction_t * belle_sip_provider_find_matching_transaction(belle_sip
}
elem=belle_sip_list_find_custom(transactions,transaction_match,&matcher);
if (elem){
ret=(belle_sip_transaction_t*)elem->data;
belle_sip_message("Found transaction [%p] matching request.",ret);
......@@ -917,13 +933,13 @@ belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transac
return ret?BELLE_SIP_CLIENT_TRANSACTION(ret):NULL;
}
void belle_sip_provider_remove_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t){
void belle_sip_provider_remove_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t){
prov->server_transactions=belle_sip_list_remove(prov->server_transactions,t);
belle_sip_object_unref(t);
}
static void authorization_context_fill_from_auth(authorization_context_t* auth_context,belle_sip_header_www_authenticate_t* authenticate) {
static void authorization_context_fill_from_auth(authorization_context_t* auth_context,belle_sip_header_www_authenticate_t* authenticate,belle_sip_uri_t *from_uri) {
authorization_context_set_realm(auth_context,belle_sip_header_www_authenticate_get_realm(authenticate));
if (auth_context->nonce && strcmp(belle_sip_header_www_authenticate_get_nonce(authenticate),auth_context->nonce)!=0) {
/*new nonce, resetting nounce_count*/
......@@ -933,45 +949,91 @@ static void authorization_context_fill_from_auth(authorization_context_t* auth_c
authorization_context_set_qop(auth_context,belle_sip_header_www_authenticate_get_qop_first(authenticate));
authorization_context_set_scheme(auth_context,belle_sip_header_www_authenticate_get_scheme(authenticate));
authorization_context_set_opaque(auth_context,belle_sip_header_www_authenticate_get_opaque(authenticate));
authorization_context_set_user_id(auth_context, from_uri?belle_sip_uri_get_user(from_uri):NULL);
if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(authenticate,belle_sip_header_proxy_authenticate_t)) {
auth_context->is_proxy=1;
}
}
static belle_sip_list_t* belle_sip_provider_get_auth_context_by_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id) {
static belle_sip_list_t* belle_sip_provider_get_auth_context_by_realm_or_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_uri_t *from_uri,const char* realm) {
belle_sip_list_t* auth_context_lst=NULL;
belle_sip_list_t* result=NULL;
authorization_context_t* auth_context;
for (auth_context_lst=p->auth_contexts;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
auth_context=(authorization_context_t*)auth_context_lst->data;
if (belle_sip_header_call_id_equals(auth_context->callid,call_id) ) {
result=belle_sip_list_append(result,auth_context_lst->data);
}
}
/* According to the RFC3261 22.3, if the outbound proxy realm is set, we could reuse its nonce value:
* "If a UA receives a Proxy-Authenticate header field value in a 401/407
* response to a request with a particular Call-ID, it should
* incorporate credentials for that realm in all subsequent requests
* that contain the same Call-ID. These credentials MUST NOT be cached
* across dialogs; however, if a UA is configured with the realm of its
* local outbound proxy, when one exists, then the UA MAY cache
* credentials for that realm across dialogs."
*/
if (result == NULL){
const char * from_user=from_uri?belle_sip_uri_get_user(from_uri):NULL;
belle_sip_debug("belle_sip_provider_auth: no auth_context registered with [call_id=%s], looking for realm..."
, call_id?belle_sip_header_call_id_get_call_id(call_id):"(null)");
for (auth_context_lst=p->auth_contexts;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
auth_context=(authorization_context_t*)auth_context_lst->data;
belle_sip_debug("belle_sip_provider_auth: \t[realm=%s] [user_id=%s] [call_id=%s]",
auth_context->realm?auth_context->realm:"(null)",
auth_context->user_id?auth_context->user_id:"(null)",
auth_context->callid?belle_sip_header_call_id_get_call_id(auth_context->callid):"(null)"
);
/* We also verify that user matches in case of multi-account to avoid use nonce from another account. For a
* single user, from_uri user id and realm user id COULD be different but we assume here that this is not the case
* in order to avoid adding another field in auth_context struct.
**/
if ((realm && strcmp(auth_context->realm,realm)==0)
&& (from_user && auth_context->user_id && strcmp(auth_context->user_id,from_user)==0)) {
result=belle_sip_list_append(result,auth_context_lst->data);
belle_sip_debug("belle_sip_provider_auth: found a MATCHING realm auth_context!");
}
}
}
return result;
}
static void belle_sip_provider_update_or_create_auth_context(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_header_www_authenticate_t* authenticate) {
belle_sip_list_t* auth_context_lst = belle_sip_provider_get_auth_context_by_call_id(p,call_id);
authorization_context_t* auth_context;
for (;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
auth_context= (authorization_context_t*)auth_context_lst->data;
if (strcmp(auth_context->realm,belle_sip_header_www_authenticate_get_realm(authenticate))==0) {
authorization_context_fill_from_auth(auth_context,authenticate);
if (auth_context_lst) belle_sip_free(auth_context_lst);
return; /*only one realm is supposed to be found for now*/
}
}
/*no auth context found, creating one*/
auth_context=belle_sip_authorization_create(call_id);
authorization_context_fill_from_auth(auth_context,authenticate);
p->auth_contexts=belle_sip_list_append(p->auth_contexts,auth_context);
if (auth_context_lst) belle_sip_free(auth_context_lst);
return;
static void belle_sip_provider_update_or_create_auth_context(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_header_www_authenticate_t* authenticate,belle_sip_uri_t *from_uri,const char* realm) {
belle_sip_list_t* auth_context_lst = NULL;
authorization_context_t* auth_context;
for (auth_context_lst=belle_sip_provider_get_auth_context_by_realm_or_call_id(p,call_id,from_uri,realm);auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
auth_context=(authorization_context_t*)auth_context_lst->data;
if (strcmp(auth_context->realm,belle_sip_header_www_authenticate_get_realm(authenticate))==0) {
authorization_context_fill_from_auth(auth_context,authenticate,from_uri);
if (auth_context_lst) belle_sip_free(auth_context_lst);
return; /*only one realm is supposed to be found for now*/
}
}
/*no auth context found, creating one*/
auth_context=belle_sip_authorization_create(call_id);
belle_sip_debug("belle_sip_provider_auth: no matching auth context, creating one for [realm=%s][user_id=%s][call_id=%s]"
, realm?realm:"(null)"
, from_uri?belle_sip_uri_get_user(from_uri):"(null)"
, call_id?belle_sip_header_call_id_get_call_id(call_id):"(null)");
authorization_context_fill_from_auth(auth_context,authenticate,from_uri);
p->auth_contexts=belle_sip_list_append(p->auth_contexts,auth_context);
if (auth_context_lst) belle_sip_free(auth_context_lst);
return;
}
int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request, belle_sip_response_t *resp,
belle_sip_uri_t *from_uri, belle_sip_list_t** auth_infos) {
belle_sip_uri_t *from_uri, belle_sip_list_t** auth_infos, const char* realm) {
belle_sip_header_call_id_t* call_id;
belle_sip_list_t* auth_context_iterator;
belle_sip_list_t* authenticate_lst;
......@@ -991,32 +1053,39 @@ int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_requ
return-1;
}
request_method=belle_sip_request_get_method(request);
/*22 Usage of HTTP Authentication
22.1 Framework
While a server can legitimately challenge most SIP requests, there
are two requests defined by this document that require special
handling for authentication: ACK and CANCEL.
Under an authentication scheme that uses responses to carry values
used to compute nonces (such as Digest), some problems come up for
any requests that take no response, including ACK. For this reason,
any credentials in the INVITE that were accepted by a server MUST be
accepted by that server for the ACK. UACs creating an ACK message
will duplicate all of the Authorization and Proxy-Authorization
header field values that appeared in the INVITE to which the ACK
corresponds. Servers MUST NOT attempt to challenge an ACK.
Although the CANCEL method does take a response (a 2xx), servers MUST
NOT attempt to challenge CANCEL requests since these requests cannot
be resubmitted. Generally, a CANCEL request SHOULD be accepted by a
server if it comes from the same hop that sent the request being
canceled (provided that some sort of transport or network layer
security association, as described in Section 26.2.1, is in place).
*/
/*22 Usage of HTTP Authentication
22.1 Framework
While a server can legitimately challenge most SIP requests, there
are two requests defined by this document that require special
handling for authentication: ACK and CANCEL.
Under an authentication scheme that uses responses to carry values
used to compute nonces (such as Digest), some problems come up for
any requests that take no response, including ACK. For this reason,
any credentials in the INVITE that were accepted by a server MUST be
accepted by that server for the ACK. UACs creating an ACK message
will duplicate all of the Authorization and Proxy-Authorization
header field values that appeared in the INVITE to which the ACK
corresponds. Servers MUST NOT attempt to challenge an ACK.
Although the CANCEL method does take a response (a 2xx), servers MUST
NOT attempt to challenge CANCEL requests since these requests cannot
be resubmitted. Generally, a CANCEL request SHOULD be accepted by a
server if it comes from the same hop that sent the request being
canceled (provided that some sort of transport or network layer
security association, as described in Section 26.2.1, is in place).
*/
if (strcmp("CANCEL",request_method)==0 || strcmp("ACK",request_method)==0) {
belle_sip_debug("no authorization header needed for method [%s]",request_method);
return 0;
}
if (from_uri==NULL){
from = belle_sip_message_get_header_by_type(request,belle_sip_header_from_t);
from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from);
}
/*get authenticates value from response*/
if (resp) {
belle_sip_list_t *it;
......@@ -1028,18 +1097,21 @@ int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_requ
/*update auth contexts with authenticate headers from response*/
for (it=authenticate_lst;it!=NULL;it=it->next) {
authenticate=BELLE_SIP_HEADER_WWW_AUTHENTICATE(it->data);
belle_sip_provider_update_or_create_auth_context(p,call_id,authenticate);
belle_sip_provider_update_or_create_auth_context(p,call_id,authenticate,from_uri,realm);
}
belle_sip_list_free(authenticate_lst);
}
/*put authorization header if passwd found*/
call_id = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_call_id_t);
if (from_uri==NULL){
from = belle_sip_message_get_header_by_type(request,belle_sip_header_from_t);
from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from);
}
head=belle_sip_provider_get_auth_context_by_call_id(p,call_id);
belle_sip_debug("belle_sip_provider_auth: looking an auth context for [method=%s][realm=%s][user_id=%s][call_id=%s]"
, request_method
, realm?realm:"(null)"
, from_uri?belle_sip_uri_get_user(from_uri):"(null)"
, call_id?belle_sip_header_call_id_get_call_id(call_id):"(null)"
);
head=belle_sip_provider_get_auth_context_by_realm_or_call_id(p,call_id,from_uri,realm);
/*we assume there no existing auth headers*/