# HG changeset patch # User Alexandre Westfahl # Date 1278039658 -32400 # Node ID 1740bee6c82189d62e56627e8f4fb203818ac1ee # Parent 9fa49025743f9e7a744c14acb4534df639fefa0c Initial App_SIP import diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/CMakeLists.txt Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,19 @@ +# The dict_nasreq extension +PROJECT("Diameter SIP Application (RFC4740)" C) + +FIND_PACKAGE(MYSQL REQUIRED) +INCLUDE_DIRECTORIES(${MYSQL_INCLUDE_DIR}) + +# List of source files +SET( DIAM_SIP_SRC + diamsip.c + diamsip.h + libdiamsip.c + md5.c + multimediaauth.c +) + +# Compile as a module +FD_ADD_EXTENSION(app_sip ${DIAM_SIP_SRC}) + +TARGET_LINK_LIBRARIES(app_sip ${MYSQL_LIBRARY}) diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/diamsip.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/diamsip.c Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,153 @@ +#include "diamsip.h" + +static struct disp_hdl * diamsip_MAR_hdl=NULL; +static struct disp_hdl * diamsip_default_hdl=NULL; + +int diamsip_default_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act) +{ + + TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act); + + + return 0; +} + + + + +/* entry point */ +static int ds_entry() +{ + struct dict_object * app=NULL; + struct disp_when data; + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Session Initiation Protocol (SIP) Application", &app, ENOENT) ); + CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) ); + + + + //We set usefull AVPs + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Session-State", &sip_dict.Auth_Session_State, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &sip_dict.Auth_Application_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Auth-Data-Item", &sip_dict.SIP_Auth_Data_Item, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authorization", &sip_dict.SIP_Authorization, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authenticate", &sip_dict.SIP_Authenticate, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Number-Auth-Items", &sip_dict.SIP_Number_Auth_Items, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Scheme", &sip_dict.SIP_Authentication_Scheme, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Info", &sip_dict.SIP_Authentication_Info, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Server-URI", &sip_dict.SIP_Server_URI, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Method", &sip_dict.SIP_Method, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-AOR", &sip_dict.SIP_AOR, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Realm", &sip_dict.Digest_Realm, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-URI", &sip_dict.Digest_URI, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce", &sip_dict.Digest_Nonce, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-CNonce", &sip_dict.Digest_CNonce, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Method", &sip_dict.Digest_Method, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce-Count", &sip_dict.Digest_Nonce_Count, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Realm", &sip_dict.Digest_Realm, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response", &sip_dict.Digest_Response, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response-Auth", &sip_dict.Digest_Response_Auth, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Username", &sip_dict.Digest_Username, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Algorithm", &sip_dict.Digest_Algorithm, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-QoP", &sip_dict.Digest_QOP, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &sip_dict.User_Name, ENOENT) ); + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-HA1", &sip_dict.Digest_HA1, ENOENT) ); + + + //Register Application + memset(&data, 0, sizeof(data)); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Session Initiation Protocol (SIP) Application", &data.app, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Multimedia-Auth-Request", &data.command, ENOENT) ); + + //Callback for unexpected messages + CHECK_FCT( fd_disp_register( diamsip_MAR_cb, DISP_HOW_APPID, &data, &diamsip_default_hdl ) ); + + //**Command Codes + //MAR + CHECK_FCT( fd_disp_register( diamsip_MAR_cb, DISP_HOW_CC, &data, &diamsip_MAR_hdl ) ); + + //TRACE_DEBUG(INFO,"*%s*%s*%s*%s*",DB_SERVER,DB_USERNAME, DB_PASSWORD, DB_DATABASE); + //We start database connection + if(start_mysql_connection(DB_SERVER,DB_USERNAME, DB_PASSWORD, DB_DATABASE)) + return 1; + + CHECK_FCT(fd_sess_handler_create(&ds_sess_hdl, free)); + + + //listnonce=NULL; + return 0; +} + +//Cleanup callback +void fd_ext_fini(void) +{ + + if (diamsip_MAR_cb) { + (void) fd_disp_unregister(&diamsip_MAR_hdl); + CHECK_FCT_DO( fd_sess_handler_destroy(&ds_sess_hdl),return); + } + + //We close database connection + close_mysql_connection(); + + //We delete the chained list of nonces + //nonce_deletelistnonce(); + //TODO:NONCE + + TRACE_ENTRY(); + return ; +} + +EXTENSION_ENTRY("diam_sip", ds_entry); + + +/* + + + + + + +test set for digest calculate + +TRACE_DEBUG(FULL,"TEST"); + DigestCalcHA1("MD5", "12345678", "example.com", "secret", "3bada1a0","56593a80", HA1); + TRACE_DEBUG(FULL,"TEST->HA1 done: *%s*",HA1); + DigestCalcResponse(HA1, "3bada1a0", "00000001", "56593a80", "auth","INVITE", "sip:97226491335@example.com", HA2, response); + DigestCalcResponseAuth(HA1, "3bada1a0", "00000001", "56593a80", "auth","INVITE", "sip:97226491335@example.com", HA2, responseauth); + + + + + + +old digest reponse check + + struct avp_hdr * tempavphdr=NULL; + + + CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &tempavp, NULL) ); + + while(tempavp) + { + CHECK_FCT( fd_msg_avp_hdr( tempavp, &tempavphdr ) ); + + if(tempavphdr->avp_code==380) + { + found_response=0; + //We have not found it but we finished looking in this Auth-Data-Item + tempavp=NULL; + } + else if(tempavphdr->avp_code==103) + { + found_response=1; + //We found it, we can leave the loop + tempavp=NULL; + } + else + { + CHECK_FCT(fd_msg_browse ( tempavp, MSG_BRW_WALK, &tempavp, NULL) ); + } + } +*/ diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/diamsip.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/diamsip.h Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "md5.h" + + +#define NONCE_SIZE 16 +#define DIGEST_LEN 16 + +//SQL configuration +#define DB_USERNAME "diamsip" +#define DB_PASSWORD "BAVpzCUhULVHayFr" +#define DB_SERVER "pineapple.tera.ics.keio.ac.jp" +#define DB_DATABASE "diamsip" + +extern MYSQL *conn; + + + +void calc_md5(char *buffer, char * data); +void clear_digest(char * digest, char * readable_digest, int digestlength); +struct avp_hdr * walk_digest(struct avp *avp, int avp_code); +int start_mysql_connection(char *server,char *user, char *password, char *database); +void request_mysql(char *query); +void close_mysql_connection(); +/* +typedef struct noncechain noncechain; +struct noncechain +{ + int timestamp; + char * nonce; + noncechain *next; +}; + + +//Global variable which points to chained list of nonce +noncechain* listnonce; + +void nonce_add_element(char * nonce); +int nonce_check_element(char * nonce); +void nonce_deletelistnonce(); +*/ + + + +static int ds_entry(); +void fd_ext_fini(void); +int diamsip_default_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act); +int diamsip_MAR_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act); +#define SQL_GETPASSWORD "SELECT `password` FROM ds_users WHERE `username` ='%s'" +#define SQL_GETPASSWORD_LEN 52 + +#define SQL_GETSIPURI "SELECT `sip_server_uri` FROM ds_users WHERE `username` ='%s'" +#define SQL_GETSIPURI_LEN 60 + +#define SQL_SETSIPURI "UPDATE ds_users SET `sip_server_uri`='%s', `flag`=1 WHERE `username` ='%s'" +#define SQL_SETSIPURI_LEN 74 + +#define SQL_GETSIPAOR "SELECT `sip_aor` FROM `ds_sip_aor`, `ds_users` WHERE `ds_sip_aor`.`id_user` = `ds_users`.`id_user` AND `ds_users`.`username` = '%s'" +#define SQL_GETSIPAOR_LEN 131 + +#define SQL_CLEARFLAG "UPDATE ds_users SET `flag`=0 WHERE `username` ='%s'" +#define SQL_CLEARFLAG_LEN 74 + +static struct session_handler * ds_sess_hdl; +static struct session *dssess; + + + +struct ds_nonce +{ + char *nonce; +}; + +//Storage for some usefull AVPs +static struct { + struct dict_object * Auth_Session_State; + struct dict_object * Auth_Application_Id; + struct dict_object * User_Name; + struct dict_object * SIP_Auth_Data_Item; + struct dict_object * SIP_Authorization; + struct dict_object * SIP_Authenticate; + struct dict_object * SIP_Number_Auth_Items; + struct dict_object * SIP_Authentication_Scheme; + struct dict_object * SIP_Authentication_Info; + struct dict_object * SIP_Server_URI; + struct dict_object * SIP_Method; + struct dict_object * SIP_AOR; + struct dict_object * Digest_URI; + struct dict_object * Digest_Nonce; + struct dict_object * Digest_Nonce_Count; + struct dict_object * Digest_CNonce; + struct dict_object * Digest_Realm; + struct dict_object * Digest_Response; + struct dict_object * Digest_Response_Auth; + struct dict_object * Digest_Username; + struct dict_object * Digest_Method; + struct dict_object * Digest_QOP; + struct dict_object * Digest_Algorithm; + struct dict_object * Digest_HA1; +} sip_dict; diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/libdiamsip.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/libdiamsip.c Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,262 @@ +#include +#include "diamsip.h" + +/* +void calculate_nonce(u8 * nonce) +{ + + nonce="lkgbsljhsdjdsgj"; + return; +}*/ + +void clear_digest(char * digest, char * readable_digest, int digestlength) +{ + int i=0; + for(i=0;iavp_code == dictdata.avp_code) && (nextavphdr->avp_vendor == dictdata.avp_vendor) ) // always 0 if no Vendor flag + { + break; + } + + // Otherwise move to next AVP in the grouped AVP + CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) ); + + if(nextavp!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( nextavp, &nextavphdr ) ); + } + else + nextavphdr=NULL; + } + if (avp) + *avp = nextavp; + + if (avp && nextavp) { + struct dictionary * dict; + CHECK_FCT( fd_dict_getdict( what, &dict) ); + CHECK_FCT_DO( fd_msg_parse_dict( nextavp, dict, NULL ), ); + } + + if (avp || nextavp) + return 0; + else + return ENOENT; +} +struct avp_hdr *walk_digest(struct avp *avp, int avp_code) +{ + struct avp_hdr *temphdr=NULL; + CHECK_FCT_DO(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL),0 ); + + while(avp!=NULL) + { + + CHECK_FCT_DO( fd_msg_avp_hdr( avp,&temphdr ),0); + + if(temphdr->avp_code==avp_code) + { + //We found the AVP so we set avp to NULL to exit the loop + avp=NULL; + return temphdr; + + } + else if(temphdr->avp_code==380)//SIP-Authorization AVP + { + //We didn't found the AVP but we finished browsing the Authentication AVP + avp=NULL; + temphdr=NULL; + + return temphdr; + } + else + { + CHECK_FCT_DO(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL),0 ); + temphdr=NULL; + + } + } + + return temphdr; +} + +int start_mysql_connection(char *server,char *user, char *password, char *database) +{ + conn = mysql_init(NULL); + + mysql_options(conn, MYSQL_OPT_RECONNECT, "true"); + + if (!mysql_real_connect(conn, server,user, password, database, 0, NULL, 0)) + { + TRACE_DEBUG(INFO,"Unable to connect to database (%s) with login:%s",database,user); + return 1; + } + return 0; + +} + +//You must free ""result"" after using this function +void request_mysql(char *query) +{ + //We check if the connection is still up + mysql_ping(conn); + + if (mysql_query(conn, query)) + { + TRACE_DEBUG(INFO,"Query %s failed", query); + + } + +} + +void close_mysql_connection() +{ + mysql_close(conn); + +} +/* +void nonce_add_element(char * nonce) +{ + noncechain *newelt=malloc(sizeof(noncechain)); + + newelt->nonce=nonce; + + newelt->timestamp=(int)time(NULL); + newelt->next=NULL; + + if(listnonce==NULL) + { + listnonce=newelt; + } + else + { + noncechain* temp=listnonce; + + while(temp->next != NULL) + { + if(temp->timestamp < ((int)time(NULL)-300)) + { + listnonce=temp->next; + free(temp); + temp=listnonce; + } + temp = temp->next; + } + temp->next = newelt; + } + +} +void nonce_del_element(char * nonce) +{ + if(listnonce!=NULL) + { + noncechain *temp=listnonce, *tempbefore=NULL; + + if(listnonce->next==NULL && strcmp(listnonce->nonce,nonce)==0) + { + free(listnonce); + listnonce=NULL; + return; + } + while(temp->next != NULL) + { + if(strcmp(temp->nonce,nonce)==0) + { + if(tempbefore==NULL) + { + listnonce=temp->next; + free(temp); + return; + } + tempbefore->next=temp->next; + free(temp); + break; + } + tempbefore=temp; + temp = temp->next; + } + + } + +} +int nonce_check_element(char * nonce) +{ + if(listnonce==NULL) + { + //Not found + return 0; + } + else + { + noncechain* temp=listnonce; + + while(temp->next != NULL) + { + if(strcmp(temp->nonce,nonce)==0) + return 1; + else + temp = temp->next; + } + } + return 0; +} + +void nonce_deletelistnonce() +{ + if(listnonce !=NULL) + { + noncechain* temp=listnonce; + + while(listnonce->next != NULL) + { + temp = listnonce->next; + + free(listnonce); + + listnonce=temp; + } + free(listnonce); + } +} +*/ diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/md5.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/md5.c Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,421 @@ +/*********************************************************************************/ +/* freeDiameter author note: + * The content from this file comes directly from the hostap project. + * It is redistributed under the terms of the BSD license, as allowed + * by the original copyright reproduced bellow. + * In addition to this notice, only the #include directives have been modified. + */ + +/*********************************************************************************/ +#include"diamsip.h" + +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ +void CvtHex( + IN HASH Bin, + OUT HASHHEX Hex + ) +{ + unsigned short i; + unsigned char j; + + for (i = 0; i < HASHLEN; i++) { + j = (Bin[i] >> 4) & 0xf; + if (j <= 9) + Hex[i*2] = (j + '0'); + else + Hex[i*2] = (j + 'a' - 10); + j = Bin[i] & 0xf; + if (j <= 9) + Hex[i*2+1] = (j + '0'); + else + Hex[i*2+1] = (j + 'a' - 10); + } + Hex[HASHHEXLEN] = '\0'; +} + +// calculate H(A1) as per spec +void DigestCalcHA1(char * pszAlg,char * pszUserName,char * pszRealm,char * pszPassword,char * pszNonce,char * pszCNonce,HASHHEX SessionKey) +{ + MD5_CTX Md5Ctx; + HASH HA1; + + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword)); + MD5Final(HA1, &Md5Ctx); + if (strcmp(pszAlg, "md5-sess") == 0) { + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, HA1, HASHLEN); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce)); + MD5Final(HA1, &Md5Ctx); + } + CvtHex(HA1, SessionKey); +} + +// calculate request-digest as SIP Digest spec RFC5090 +void DigestCalcResponse(HASHHEX HA1,char * pszNonce,char * pszNonceCount,char * pszCNonce,char * pszQop,char * pszMethod,char * pszDigestUri,HASHHEX HEntity,HASHHEX Response) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + + // calculate H(A2) + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri)); + if (strcmp(pszQop, "auth-int") == 0) { + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); + } + MD5Final(HA2, &Md5Ctx); + CvtHex(HA2, HA2Hex); + + // calculate response + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, HA1, HASHHEXLEN); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, ":", 1); + if (*pszQop) { + MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszQop, strlen(pszQop)); + MD5Update(&Md5Ctx, ":", 1); + } + MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); + MD5Final(RespHash, &Md5Ctx); + CvtHex(RespHash, Response); +} +// calculate Digest_response_Auth as per SIP Digest spec RFC5090 +void DigestCalcResponseAuth(HASHHEX HA1,char * pszNonce,char * pszNonceCount,char * pszCNonce,char * pszQop,char * pszMethod,char * pszDigestUri,HASHHEX HEntity,HASHHEX Response) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + + // calculate H(A2) + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri)); + if (strcmp(pszQop, "auth-int") == 0) { + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); + } + MD5Final(HA2, &Md5Ctx); + CvtHex(HA2, HA2Hex); + + // calculate response + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, HA1, HASHHEXLEN); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, ":", 1); + if (*pszQop) { + MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszQop, strlen(pszQop)); + MD5Update(&Md5Ctx, ":", 1); + } + MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); + MD5Final(RespHash, &Md5Ctx); + CvtHex(RespHash, Response); +} + + + + + +static void MD5Transform(u32 buf[4], u32 const in[16]); + + + + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + size_t i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); +} + + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + os_memcpy(p, buf, len); + return; + } + os_memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + os_memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + os_memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + os_memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + os_memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + os_memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + os_memcpy(digest, ctx->buf, 16); + os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +/* ===== end - public domain MD5 implementation ===== */ + diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/md5.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/md5.h Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,59 @@ +/*********************************************************************************/ +/* freeDiameter author note: + * The content from this file comes directly from the hostap project. + * It is redistributed under the terms of the BSD license, as allowed + * by the original copyright reproduced bellow. + * The file has not been modified, except for this notice. + */ +/*********************************************************************************/ + +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +#define HASHLEN 16 +typedef char HASH[HASHLEN]; +#define HASHHEXLEN 32 +typedef char HASHHEX[HASHHEXLEN+1]; +#define IN +#define OUT + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; +typedef struct MD5Context MD5_CTX; +#define os_memcpy(d, s, n) memcpy((d), (s), (n)) +#define os_memset(s, c, n) memset(s, c, n) + + +void MD5Init(struct MD5Context *ctx); +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *ctx); + +#endif /* MD5_H */ diff -r 9fa49025743f -r 1740bee6c821 extensions/app_sip/multimediaauth.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_sip/multimediaauth.c Fri Jul 02 12:00:58 2010 +0900 @@ -0,0 +1,747 @@ +#include "diamsip.h" + + +int diamsip_MAR_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act) +{ + struct msg *ans, *qry; + struct avp *a2, *authdataitem; + struct msg_hdr * header = NULL; + struct avp_hdr * avphdr=NULL, *avpheader=NULL, *avpheader_auth=NULL,*digestheader=NULL; + union avp_value val; + int found_cnonce=0; + struct avp * tempavp=NULL,*sipAuthentication=NULL,*sipAuthenticate=NULL; + char * result; + char password[51]; + int idx=0, idx2=0, number_of_auth_items=0,i=0;; + //Flags and variables for Database + int sipurinotstored=0, authenticationpending=0, querylen=0, usernamelen=0; + char *query=NULL,*username=NULL; + + //The nonce we will store and retrieve in session + struct ds_nonce *storednonce=NULL; + + + TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act); + + if (msg == NULL) + return EINVAL; + + + /* Create answer header */ + qry = *msg; + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) ); + ans = *msg; + + + + /* Add the appropriate command code & Auth-Application-Id */ + { + + CHECK_FCT( fd_msg_hdr ( *msg, &header ) ); + header->msg_flags = CMD_FLAG_PROXIABLE; + header->msg_code = 286; + header->msg_appl = 6; + + + /* Add the Auth-Application-Id */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.Auth_Application_Id, 0, &avp ) ); + val.i32 = header->msg_appl; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add ( ans, MSG_BRW_LAST_CHILD, avp) ); + } + } + + + /* Add the Auth-Session-State AVP */ + { + + CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.Auth_Session_State, &avp) ); + CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ) ); + + CHECK_FCT( fd_msg_avp_new ( sip_dict.Auth_Session_State, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue( avp, avphdr->avp_value ) ); + CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) ); + } + + + + /* Check if method is REGISTER then User-Name must be present */ + { + + CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Method, &avp) ); + CHECK_FCT( fd_msg_avp_hdr( avp, &avpheader )); + + + char *method=NULL; + + CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.User_Name, &avp) ); + if(avp!=NULL) + { + + int not_found=1; + MYSQL_RES *res=NULL; + MYSQL_ROW row; + + CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ) ); + + //We allocate the double size of username because at worst it can be all quotes + username=malloc(avphdr->avp_value->os.len*2+1); + //We purify username not to have forbidden characters + usernamelen=mysql_real_escape_string(conn, username, avphdr->avp_value->os.data, avphdr->avp_value->os.len); + + + if((strncmp(avpheader->avp_value->os.data,"REGISTER",avpheader->avp_value->os.len)==0)) + { + not_found=1; + + //We copy username in query + querylen=SQL_GETPASSWORD_LEN + usernamelen; + query = malloc(querylen+2); + snprintf(query, querylen+1, SQL_GETPASSWORD, username); + + + + //We make the query + request_mysql(query); + res=mysql_use_result(conn); + if(res==NULL) + { + //We couldn't make the request + result="DIAMETER_UNABLE_TO_COMPLY"; + goto out; + } + + + + while ((row = mysql_fetch_row(res)) != NULL) + { + if(row[0]!="") + { + strcpy(password,row[0]); + not_found=0; + break; + } + } + mysql_free_result(res); + free(query); + + if(not_found) + { + TRACE_DEBUG(FULL,"The user %s doesn't exist!",username); + result="DIAMETER_ERROR_USER_UNKNOWN"; + free(username); + goto out; + } + + + + //Now that we know the user exist, we get the list of AOR owned by this user + querylen=SQL_GETSIPAOR_LEN + usernamelen; + query = malloc(querylen+2); + snprintf(query, querylen+1, SQL_GETSIPAOR, username); + + //We make the query + request_mysql(query); + res=mysql_use_result(conn); + if(res==NULL) + { + //We couldn't make the request + result="DIAMETER_UNABLE_TO_COMPLY"; + goto out; + } + + //We retrieve the SIP-AOR from AVP to check if the user can us it + CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_AOR, &avp) ); + CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ) ); + + not_found=1; + while ((row = mysql_fetch_row(res)) != NULL) + { + if(strncmp(avphdr->avp_value->os.data,row[0],avphdr->avp_value->os.len)==0) + { + not_found=0; + break; + } + } + mysql_free_result(res); + free(query); + + if(not_found) + { + TRACE_DEBUG(FULL,"The user %s can't use this SIP-AOR!",username); + result="DIAMETER_ERROR_IDENTITIES_DONT_MATCH"; + free(username); + goto out; + } + + } + + CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Server_URI, &avp) ); + CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )); + + if(avphdr!=NULL) + { + char *sipuri=NULL; + int sipurilen=0; + + //We allocate the double size of SIP-URI because at worst it can be all quotes + sipuri=malloc(avphdr->avp_value->os.len*2+1); + //We purify SIP-URI not to have forbidden characters + sipurilen=mysql_real_escape_string(conn, sipuri, avphdr->avp_value->os.data, avphdr->avp_value->os.len); + + + //We get the SIP-URI assignated to the user + querylen=SQL_GETSIPURI_LEN + usernamelen; + query = malloc(querylen+2); + snprintf(query, querylen+1, SQL_GETSIPURI, username); + + //We make the query + request_mysql(query); + res=mysql_use_result(conn); + if(res==NULL) + { + //We couldn't make the request + result="DIAMETER_UNABLE_TO_COMPLY"; + goto out; + } + not_found=1; + while ((row = mysql_fetch_row(res)) != NULL) + { + if(strncmp(avphdr->avp_value->os.data,row[0],avphdr->avp_value->os.len)==0) + { + not_found=0; + break; + } + } + mysql_free_result(res); + free(query); + + if(not_found) + { + //We update the SIP_URI for the user and we flag "authentication in progress" + querylen=SQL_SETSIPURI_LEN + usernamelen + sipurilen; + query = malloc(querylen+2); + snprintf(query, querylen+1, SQL_SETSIPURI, sipuri, username); + + //We make the query + request_mysql(query); + + free(query); + authenticationpending=1; + } + free(sipuri); + + } + else + sipurinotstored=1; + + } + else + { + result="DIAMETER_USER_NAME_REQUIRED"; + goto out; + } + + + free(method); + + } + + + //TODO: remove loop for authdataitem because RFC say only one (wait for answer from Garcia) + // How many Auth Data Items? + CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Number_Auth_Items, &avp) ); + CHECK_FCT( fd_msg_avp_hdr( avp, &avpheader ) ); + + + if(avp!=NULL) + { + CHECK_FCT(fd_msg_search_avp ( qry, sip_dict.SIP_Auth_Data_Item, &avp)); + CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ) ); + + if(avp!=NULL) + { + //First is Authentication Scheme + CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ) ); + + //Digest-Authentication? + if(avphdr->avp_value->i32==0) + { + + for(idx=0;idxavp_value->i32;idx++) + { + //We look for SIP Auth items + CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL) ); + + if(avp!=NULL) + { + + CHECK_FCT( fd_msg_avp_hdr( avp,&avphdr )); + + if(avphdr->avp_code==380) //We only create Auth-Data-Item to answer Auth-Data-Item + { + /* Add the Auth-Data-Item AVP */ + CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Auth_Data_Item, 0, &authdataitem ) ); + + /* Add the Authentication Scheme AVP */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authentication_Scheme, 0, &a2 ) ); + val.i32=0; //We only know Digest Authentication + CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) ); + CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, a2 ) ); + } + + //We need to know if there is a Cnonce attribute (only in the second MAR request) + + //CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL) ); + + + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_CNonce, &a2 )); + + if(a2!=NULL) + found_cnonce=1; + else + found_cnonce=0; + + if(!found_cnonce) + { + /* + We are in the case of first access request so we need to challenge the user. + */ + TRACE_DEBUG(FULL,"First Authorization in progress..."); + + /* Create a new session */ //this create a new session Id !!! + //CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "diamsip", 7), goto out ); + + + /* Create the SIP-Authenticate AVP */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authenticate, 0, &sipAuthenticate ) ); + } + + /* Add the Digest QOP AVP */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_QOP, 0, &a2 ) ); + val.os.data="auth"; + val.os.len=strlen(val.os.data); + CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) ); + CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) ); + } + /* Add the Digest Nonce AVP */ + { + uint8_t buffer[NONCE_SIZE]; + char nonce[NONCE_SIZE * 2 + 1]; + + + gcry_create_nonce ((uint8_t *)buffer, sizeof(buffer)); + + for(i=0;inonce=malloc(NONCE_SIZE*2+1)); + memcpy(storednonce->nonce,(char *)nonce,NONCE_SIZE*2+1); + CHECK_FCT( fd_sess_state_store ( ds_sess_hdl, sess, &storednonce )); + + val.os.data=nonce; + val.os.len=NONCE_SIZE * 2; + + CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) ); + CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) ); + } + /* Add the Digest Algorithm AVP */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Algorithm, 0, &a2 ) ); + val.os.data="MD5"; + val.os.len=strlen(val.os.data); + CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) ); + CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) ); + + } + /* Add the Digest Realm AVP */ + { + tempavp=avp; + + avpheader_auth=walk_digest(tempavp, 104); + if(avpheader_auth!=NULL) + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Realm, 0, &a2 ) ); + CHECK_FCT( fd_msg_avp_setvalue( a2, avpheader_auth->avp_value ) ); + CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) ); + + } + } + + + //We add SIP Authenticate to Auth Data Item + CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, sipAuthenticate ) ); + //We add Auth Data Item to Answer + CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, authdataitem ) ); + + number_of_auth_items++; + if(sipurinotstored) + result="DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED"; + else + result="DIAMETER_MULTI_ROUND_AUTH"; + found_cnonce=0; + } + else + { + /* + We are in the case of access request after challenge so we need to check credentials. + */ + TRACE_DEBUG(FULL,"Authentication after challenge"); + + /* Search the session, retrieve its data */ + { + //int new=0; + + //TRACE_DEBUG(FULL,"new: *%d*",new); + //ASSERT( new == 0 ); + CHECK_FCT( fd_sess_state_retrieve( ds_sess_hdl, sess, &storednonce )); + if(storednonce ==NULL) + { + result="DIAMETER_UNABLE_TO_COMPLY"; + + if(username!=NULL) + free(username); + goto out; + } + } + + /* Create the SIP-Authentication-Info AVP */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authentication_Info, 0, &sipAuthentication ) ); + } + + + + /* Add the Digest response Auth AVP */ + { + //uint8_t bufferresp[DIGEST_LEN]; + //char response[DIGEST_LEN*2+1]; + int i=0; + + //We extract all the data we need + tempavp=avp; + + char * digest_username=NULL, *digest_uri=NULL, *digest_response=NULL, *digest_realm=NULL, *digest_nonce=NULL, *digest_method=NULL, *digest_qop=NULL, *digest_algorithm=NULL, *digest_cnonce=NULL, *digest_noncecount=NULL; + + + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Nonce, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + + digest_nonce = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_nonce, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_nonce[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_nonce); + TRACE_DEBUG(FULL,"Stored Nonce:*%s*",storednonce->nonce); + + if(strcmp(digest_nonce,storednonce->nonce)!=0) + { + free(digest_nonce); + free(storednonce->nonce); + free(storednonce); + result="DIAMETER_UNABLE_TO_COMPLY"; + + if(username!=NULL) + free(username); + goto out; + } + + } + + } + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Response, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_response = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_response, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_response[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_response); + } + + } + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Realm, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_realm = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_realm, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_realm[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_realm); + } + } + + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Method, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_method = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_method, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_method[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_method); + } + } + else + digest_method=""; + + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_URI, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_uri = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_uri, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_uri[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_uri); + } + } + + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_QOP, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_qop = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_qop, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_qop[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_qop); + } + } + else + digest_qop=NULL; + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Algorithm, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_algorithm = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_algorithm, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_algorithm[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_algorithm); + } + } + else + digest_algorithm=NULL; + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_CNonce, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_cnonce = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_cnonce, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_cnonce[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_cnonce); + } + } + else + digest_cnonce=""; + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Nonce_Count, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_noncecount = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_noncecount, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_noncecount[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_noncecount); + } + } + else + digest_noncecount=""; + CHECK_FCT(fd_avp_search_avp (avp, sip_dict.Digest_Username, &a2 )); + if(a2!=NULL) + { + CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader ) ); + if(digestheader!=NULL) + { + digest_username = malloc(digestheader->avp_value->os.len + 1); + memcpy(digest_username, digestheader->avp_value->os.data, + digestheader->avp_value->os.len); + digest_username[digestheader->avp_value->os.len]='\0'; + TRACE_DEBUG(FULL,"Element:*%s*",digest_username); + } + } + //TODO: replace by authentication function + + HASHHEX HA1; + HASHHEX HA2 = ""; + HASHHEX response, responseauth; + + + DigestCalcHA1(digest_algorithm, digest_username, digest_realm, password, digest_nonce,digest_cnonce, HA1); + + DigestCalcResponse(HA1, digest_nonce, digest_noncecount, digest_cnonce, digest_qop,digest_method, digest_uri, HA2, response); + + + // We check that the Digest-Response is the same (UA, Diameter) + if(strcmp(response,digest_response)!=0) + { + TRACE_DEBUG(FULL,"Response calculated by Diameter server:%s",response); + TRACE_DEBUG(FULL,"Response calculated by UA:%s",digest_response); + TRACE_DEBUG(INFO,"Digest-Response does not match!"); + result="DIAMETER_UNABLE_TO_COMPLY"; + free(digest_algorithm); + free(digest_cnonce); + free(digest_nonce); + free(digest_noncecount); + free(digest_method); + free(digest_username); + free(digest_uri); + free(digest_qop); + free(digest_response); + free(digest_realm); + free(storednonce->nonce); + free(storednonce); + if(username!=NULL) + free(username); + goto out; + + } + //We calculate Digest_Response_Auth + DigestCalcResponseAuth(HA1, digest_nonce, digest_noncecount, digest_cnonce, digest_qop,digest_method, digest_uri, HA2, responseauth); + + + if(strcmp(digest_qop,"auth-int")==0) + { + //Digest-HA1 MUST be used instead of Digest-Response-Auth if Digest-Qop is 'auth-int'. + CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_HA1, 0, &a2 ) ); + val.os.data=HA1; + val.os.len=HASHHEXLEN+1; + CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) ); + CHECK_FCT( fd_msg_avp_add( sipAuthentication, MSG_BRW_LAST_CHILD, a2 ) ); + } + else + { + //Digest-Response-Auth MUST be used instead of Digest-HA1 if Digest-Qop is 'auth'. + CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Response_Auth, 0, &a2 ) ); + val.os.data=responseauth; + val.os.len=DIGEST_LEN*2; + CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) ); + CHECK_FCT( fd_msg_avp_add( sipAuthentication, MSG_BRW_LAST_CHILD, a2 ) ); + } + free(digest_algorithm); + free(digest_cnonce); + free(digest_nonce); + free(digest_noncecount); + free(digest_method); + free(digest_username); + free(digest_uri); + free(digest_qop); + free(digest_response); + free(digest_realm); + free(storednonce->nonce); + free(storednonce); + + number_of_auth_items++; + } + + + //We add SIP Authentication-Info to Auth Data Item + CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, sipAuthentication ) ); + //We add Auth Data Item to Answer + CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, authdataitem ) ); + + + if(username!=NULL && authenticationpending) + { + //We clear the flag "authentication pending" + querylen=SQL_CLEARFLAG_LEN + usernamelen; + query = malloc(querylen+2); + snprintf(query, querylen+1, SQL_CLEARFLAG, username); + + //We make the query + request_mysql(query); + + free(query); + } + + if(sipurinotstored) + result="DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED"; + else + result="DIAMETER_SUCCESS"; + found_cnonce=0; + } + } + } + else + TRACE_DEBUG(INFO,"No auth data items!"); + } + /*Add SIP_Number_Auth_Items AVP */ + { + CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Number_Auth_Items, 0, &avp ) ); + val.i32 = number_of_auth_items; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add ( ans, MSG_BRW_LAST_CHILD, avp) ); + } + + + } + else + { + TRACE_DEBUG(INFO,"We only support DIGEST for now, unable to comply"); + result="DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED"; + if(username!=NULL) + free(username); + goto out; + } + } + } + else + { + //TODO: remove this because Number_Auth_Items is not compulsory + TRACE_DEBUG(FULL,"Number-Auth-Items is not included."); + result="DIAMETER_UNABLE_TO_COMPLY"; + if(username!=NULL) + free(username); + goto out; + } + + if(username!=NULL) + free(username); + + +out: + /* Set the Origin-Host, Origin-Realm, Result-Code AVPs */ + CHECK_FCT( fd_msg_rescode_set( ans, result, NULL, NULL, 1 ) ); + + + /* Send the answer */ + CHECK_FCT( fd_msg_send( msg, NULL, NULL ) ); + + + return 0; +}