/* auth_helper.c belle-sip - SIP (RFC3261) library. 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 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 . */ #include "belle-sip/auth-helper.h" #include "belle_sip_internal.h" #include "md5.h" #include #define CHECK_IS_PRESENT(obj,header_name,name) \ if (!belle_sip_header_##header_name##_get_##name(obj)) {\ belle_sip_error("parameter ["#name"]not found for header ["#header_name"]");\ return-1;\ } static void belle_sip_auth_helper_clone_authorization(belle_sip_header_authorization_t* authorization, const belle_sip_header_www_authenticate_t* authentication) { CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,scheme,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,realm,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,nonce,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,algorithm,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,opaque,authorization,authentication) } belle_sip_header_authorization_t* belle_sip_auth_helper_create_authorization(const belle_sip_header_www_authenticate_t* authentication) { belle_sip_header_authorization_t* authorization = belle_sip_header_authorization_new(); belle_sip_auth_helper_clone_authorization(authorization,authentication); return authorization; } belle_http_header_authorization_t* belle_http_auth_helper_create_authorization(const belle_sip_header_www_authenticate_t* authentication) { belle_http_header_authorization_t* authorization = belle_http_header_authorization_new(); belle_sip_auth_helper_clone_authorization(BELLE_SIP_HEADER_AUTHORIZATION(authorization),authentication); return authorization; } belle_sip_header_proxy_authorization_t* belle_sip_auth_helper_create_proxy_authorization(const belle_sip_header_proxy_authenticate_t* proxy_authentication){ belle_sip_header_proxy_authorization_t* authorization = belle_sip_header_proxy_authorization_new(); belle_sip_auth_helper_clone_authorization(BELLE_SIP_HEADER_AUTHORIZATION(authorization),BELLE_SIP_HEADER_WWW_AUTHENTICATE(proxy_authentication)); return authorization; } int belle_sip_auth_helper_compute_ha1(const char* userid,const char* realm,const char* password, char ha1[33]) { md5_byte_t out[16]; md5_state_t state; int di; if (!userid) { belle_sip_error("belle_sip_fill_authorization_header, username not found "); return -1; } if (!password) { belle_sip_error("belle_sip_fill_authorization_header, password not found "); return -1; } if (!realm) { belle_sip_error("belle_sip_fill_authorization_header, password not found "); return -1; } belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)userid,strlen(userid)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)realm,strlen(realm)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)password,strlen(password)); belle_sip_md5_finish(&state,out); for (di = 0; di < 16; ++di) sprintf(ha1 + di * 2, "%02x", out[di]); ha1[32]='\0'; return 0; } int belle_sip_auth_helper_compute_ha2(const char* method,const char* uri, char ha2[33]) { md5_byte_t out[16]; md5_state_t state; int di; ha2[32]='\0'; /*HA2=MD5(method:uri)*/ belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)method,strlen(method)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)uri,strlen(uri)); belle_sip_md5_finish(&state,out); for (di = 0; di < 16; ++di) sprintf(ha2 + di * 2, "%02x", out[di]); return 0; } int belle_sip_auth_helper_compute_response(const char* ha1,const char* nonce, const char* ha2, char response[33]) { md5_byte_t out[16]; md5_state_t state; int di; response[32]='\0'; belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)ha1,strlen(ha1)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state ,(const md5_byte_t *)nonce ,strlen(nonce)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)ha2,strlen(ha2)); belle_sip_md5_finish(&state,out); /*copy values*/ for (di = 0; di < 16; ++di) sprintf(response + di * 2, "%02x", out[di]); return 0; } int belle_sip_auth_helper_compute_response_qop_auth(const char* ha1 , const char* nonce , unsigned int nonce_count , const char* cnonce , const char* qop , const char* ha2, char response[33]) { md5_byte_t out[16]; md5_state_t state; char nounce_count_as_string[9]; int di; response[32]='\0'; snprintf(nounce_count_as_string,sizeof(nounce_count_as_string),"%08x",nonce_count); /*response=MD5(HA1:nonce:nonce_count:cnonce:qop:HA2)*/ belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)ha1,strlen(ha1)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state ,(const md5_byte_t *)nonce ,strlen(nonce)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state ,(const md5_byte_t *)nounce_count_as_string ,strlen(nounce_count_as_string)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state ,(const md5_byte_t *)cnonce ,strlen(cnonce)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state ,(const md5_byte_t *)qop ,strlen(qop)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)ha2,strlen(ha2)); belle_sip_md5_finish(&state,out); /*copy values*/ for (di = 0; di < 16; ++di) sprintf(response + di * 2, "%02x", out[di]); return 0; } int belle_sip_auth_helper_fill_authorization(belle_sip_header_authorization_t* authorization ,const char* method ,const char* ha1) { int auth_mode=0; char* uri; char ha2[16*2 + 1]; char response[16*2 + 1]; char cnonce[9]; response[32]=ha2[32]='\0'; if (belle_sip_header_authorization_get_scheme(authorization) != NULL && strcmp("Digest",belle_sip_header_authorization_get_scheme(authorization))!=0) { belle_sip_error("belle_sip_fill_authorization_header, unsupported schema [%s]" ,belle_sip_header_authorization_get_scheme(authorization)); return -1; } if (belle_sip_header_authorization_get_qop(authorization) && !(auth_mode=strcmp("auth",belle_sip_header_authorization_get_qop(authorization))==0)) { belle_sip_error("belle_sip_fill_authorization_header, unsupported qop [%s], use auth or nothing instead" ,belle_sip_header_authorization_get_qop(authorization)); return -1; } CHECK_IS_PRESENT(authorization,authorization,realm) CHECK_IS_PRESENT(authorization,authorization,nonce) if (BELLE_SIP_IS_INSTANCE_OF(authorization,belle_http_header_authorization_t)) { /*http case*/ if (!belle_http_header_authorization_get_uri(BELLE_HTTP_HEADER_AUTHORIZATION(authorization))) { belle_sip_error("parameter uri not found for http header authorization"); return-1; } } else { CHECK_IS_PRESENT(authorization,authorization,uri) } if (auth_mode) { CHECK_IS_PRESENT(authorization,authorization,nonce_count) if (!belle_sip_header_authorization_get_cnonce(authorization)) { snprintf(cnonce,sizeof(cnonce),"%08x",(short)(long)authorization^0x5555555); /*spseudo randomly genrated cnonce*/ belle_sip_header_authorization_set_cnonce(authorization,cnonce); } } if (!method) { belle_sip_error("belle_sip_fill_authorization_header, method not found "); return -1; } if (BELLE_SIP_IS_INSTANCE_OF(authorization,belle_http_header_authorization_t)) { /*http case*/ uri=belle_generic_uri_to_string(belle_http_header_authorization_get_uri(BELLE_HTTP_HEADER_AUTHORIZATION(authorization))); } else { uri=belle_sip_uri_to_string(belle_sip_header_authorization_get_uri(authorization)); } belle_sip_auth_helper_compute_ha2(method,uri,ha2); belle_sip_free(uri); if (auth_mode) { /*response=MD5(HA1:nonce:nonce_count:cnonce:qop:HA2)*/ belle_sip_auth_helper_compute_response_qop_auth(ha1 ,belle_sip_header_authorization_get_nonce(authorization) ,belle_sip_header_authorization_get_nonce_count(authorization) ,belle_sip_header_authorization_get_cnonce(authorization) ,belle_sip_header_authorization_get_qop(authorization) ,ha2 ,response); } else { /*response=MD5(ha1:nonce:ha2)*/ belle_sip_auth_helper_compute_response(ha1,belle_sip_header_authorization_get_nonce(authorization),ha2,response); } belle_sip_header_authorization_set_response(authorization,(const char*)response); return 0; } int belle_sip_auth_helper_fill_proxy_authorization(belle_sip_header_proxy_authorization_t* proxy_authorization ,const char* method ,const char* ha1) { return belle_sip_auth_helper_fill_authorization(BELLE_SIP_HEADER_AUTHORIZATION(proxy_authorization) ,method, ha1); }