2012-12-03 03:11:21 +00:00
/*
* Copyright ( C ) 2010 - 2011 Mamadou Diop .
*
* Contact : Mamadou Diop < diopmamadou ( at ) doubango [ dot ] org >
2016-02-23 21:00:35 +00:00
*
2012-12-03 03:11:21 +00:00
* This file is part of Open Source Doubango Framework .
*
* DOUBANGO 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 3 of the License , or
* ( at your option ) any later version .
2016-02-23 21:00:35 +00:00
*
2012-12-03 03:11:21 +00:00
* DOUBANGO 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 .
2016-02-23 21:00:35 +00:00
*
2012-12-03 03:11:21 +00:00
* You should have received a copy of the GNU General Public License
* along with DOUBANGO .
*
*/
/**@file tsip_transport_ipsec.c
* @ brief SIP / IPSec transport .
*
* @ author Mamadou Diop < diopmamadou ( at ) doubango [ dot ] org >
*
*/
# include "tinysip/transports/tsip_transport_ipsec.h"
# include "tinysip/transports/tsip_transport.h"
# include "tinysip/headers/tsip_header_Proxy_Require.h"
# include "tinysip/headers/tsip_header_Require.h"
# include "tinysip/headers/tsip_header_Security_Client.h"
# include "tinysip/headers/tsip_header_Security_Server.h"
# include "tsip.h"
# include "tnet_socket.h"
# include "tsk_debug.h"
TINYSIP_GEXTERN const tsk_object_def_t * tsip_ipsec_association_def_t ;
tsip_ipsec_association_t * tsip_ipsec_association_create ( const tsip_transport_t * transport )
{
2016-02-23 21:00:35 +00:00
return tsk_object_new ( tsip_ipsec_association_def_t , transport ) ;
2012-12-03 03:11:21 +00:00
}
tsip_transport_ipsec_t * tsip_transport_ipsec_create ( struct tsip_stack_s * stack , const char * host , tnet_port_t port , tnet_socket_type_t type , const char * description )
{
2016-02-23 21:00:35 +00:00
return tsk_object_new ( tsip_transport_ipsec_def_t , stack , host , port , type , description ) ;
2012-12-03 03:11:21 +00:00
}
int tsip_transport_ipsec_createTempSAs ( tsip_transport_ipsec_t * self )
{
2016-02-23 21:00:35 +00:00
int ret = - 1 ;
/* Check */
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
goto bail ;
}
/* Already have temporary SAs ? */
if ( self - > asso_temporary ) {
TSK_DEBUG_ERROR ( " IPSec transport layer already have temporary SAs " ) ;
ret = - 2 ;
goto bail ;
}
/* Create temporary association */
if ( ( self - > asso_temporary = tsip_ipsec_association_create ( TSIP_TRANSPORT ( self ) ) ) ) {
if ( self - > asso_temporary - > ctx & & self - > asso_temporary - > ctx - > state = = tipsec_state_inbound ) {
ret = 0 ;
}
else {
TSK_DEBUG_INFO ( " Failed to create new temporary SAs. " ) ;
ret = - 3 ;
goto bail ;
}
}
else {
TSK_DEBUG_INFO ( " Failed to create new temporary SAs. " ) ;
ret = - 4 ;
goto bail ;
}
2012-12-03 03:11:21 +00:00
bail :
2016-02-23 21:00:35 +00:00
if ( ret & & ret ! = - 1 ) {
TSK_OBJECT_SAFE_FREE ( self - > asso_temporary ) ;
}
return ret ;
2012-12-03 03:11:21 +00:00
}
int tsip_transport_ipsec_ensureTempSAs ( tsip_transport_ipsec_t * self , const tsip_response_t * r401_407 , int64_t expires )
{
2016-02-23 21:00:35 +00:00
int ret = - 1 ;
tsk_size_t index ;
const tsip_header_Security_Server_t * ssHdr ;
double maxQ = - 2.0 ; /* The Q value in the SIP header will be equal to -1 by default. */
int match = 0 ;
tipsec_spi_t spi_pc , spi_ps ;
tipsec_port_t port_pc , port_ps ;
tipsec_lifetime_t lifetime ;
if ( ! self | | expires < 0 ) {
goto bail ;
}
lifetime = ( tipsec_lifetime_t ) expires ;
/* Already have temporary SAs ? */
if ( ! self - > asso_temporary ) {
TSK_DEBUG_ERROR ( " Cannot ensure temporary SAs (No tempSAs) " ) ;
ret = - 2 ;
goto bail ;
}
/* Cleanup old Security-Verifies */
TSK_OBJECT_SAFE_FREE ( self - > secVerifies ) ;
/* RFC 3329 - 2.3.1 Client Initiated
When the client receives a response with a Security - Server header field , it MUST choose the security mechanism in the server ' s list
with the highest " q " value among all the mechanisms that are known to the client .
*/
for ( index = 0 ; ( ssHdr = ( const tsip_header_Security_Server_t * ) tsip_message_get_headerAt ( r401_407 , tsip_htype_Security_Server , index ) ) ; index + + ) {
tsip_header_Security_Verify_t * svHdr ;
if ( maxQ > ssHdr - > q | | ! tsk_striequals ( ssHdr - > mech , " ipsec-3gpp " ) ) {
goto copy ;
}
if ( ( TIPSEC_ALG_FROM_STR ( ssHdr - > alg ) = = self - > asso_temporary - > ctx - > alg ) & &
( TIPSEC_EALG_FROM_STR ( ssHdr - > ealg ) = = self - > asso_temporary - > ctx - > ealg ) & &
( TIPSEC_PROTOCOL_FROM_STR ( ssHdr - > prot ) = = self - > asso_temporary - > ctx - > protocol ) & &
( TIPSEC_MODE_FROM_STR ( ssHdr - > mod ) = = self - > asso_temporary - > ctx - > mode ) ) {
match = 1 ;
maxQ = ( ssHdr - > q > = maxQ ) ? ssHdr - > q : maxQ ;
spi_pc = ssHdr - > spi_c ;
spi_ps = ssHdr - > spi_s ;
port_pc = ssHdr - > port_c ;
port_ps = ssHdr - > port_s ;
}
2012-12-03 03:11:21 +00:00
copy :
2016-02-23 21:00:35 +00:00
svHdr = tsip_header_Security_Verify_create_null ( ) ;
svHdr - > mech = tsk_strdup ( ssHdr - > mech ) ;
svHdr - > alg = tsk_strdup ( ssHdr - > alg ) ;
svHdr - > prot = tsk_strdup ( ssHdr - > prot ) ;
svHdr - > mod = tsk_strdup ( ssHdr - > mod ) ;
svHdr - > ealg = tsk_strdup ( ssHdr - > ealg ) ;
svHdr - > port_c = ssHdr - > port_c ;
svHdr - > port_s = ssHdr - > port_s ;
svHdr - > spi_c = ssHdr - > spi_c ;
svHdr - > spi_s = ssHdr - > spi_s ;
svHdr - > q = ssHdr - > q ;
TSIP_HEADER_PARAMS ( svHdr ) = tsk_object_ref ( TSIP_HEADER_PARAMS ( ssHdr ) ) ;
if ( ! self - > secVerifies ) {
self - > secVerifies = tsk_list_create ( ) ;
}
tsk_list_push_back_data ( self - > secVerifies , ( void * * ) & svHdr ) ;
}
if ( ! match ) {
TSK_DEBUG_ERROR ( " Failed to match security server<->security client. " ) ;
ret = - 3 ;
goto bail ;
}
/* Set remote parameters received from 401/407 response. */
if ( ( ret = tipsec_ctx_set_remote ( self - > asso_temporary - > ctx , spi_pc , spi_ps , port_pc , port_ps , lifetime ) ) ) {
TSK_DEBUG_ERROR ( " Failed to set remote IPSec parameters [%d] " , ret ) ;
goto bail ;
}
2012-12-03 03:11:21 +00:00
bail :
2016-02-23 21:00:35 +00:00
return ret ;
2012-12-03 03:11:21 +00:00
}
int tsip_transport_ipsec_startSAs ( tsip_transport_ipsec_t * self , const tipsec_key_t * ik , const tipsec_key_t * ck )
{
2022-03-04 19:20:08 +00:00
struct sockaddr_storage to ;
2016-02-23 21:00:35 +00:00
int ret = - 1 ;
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
goto bail ;
}
if ( ! self - > asso_temporary ) {
TSK_DEBUG_ERROR ( " Failed to find temporary SAs " ) ;
ret = - 2 ;
goto bail ;
}
/* Promote tempSAs (temp => active) */
TSK_OBJECT_SAFE_FREE ( self - > asso_active ) ; /* delete old active SAs */
self - > asso_active = tsk_object_ref ( ( void * ) self - > asso_temporary ) ; /* promote */
TSK_OBJECT_SAFE_FREE ( self - > asso_temporary ) ; /* delete old temp SAs */
if ( ( ret = tipsec_ctx_set_keys ( self - > asso_active - > ctx , ik , ck ) ) = = 0 ) {
ret = tipsec_ctx_start ( self - > asso_active - > ctx ) ;
}
2012-12-03 03:11:21 +00:00
2022-03-04 19:20:08 +00:00
/* Connect Sockets: port_uc to port_ps*/
if ( ( ret = tnet_sockaddr_init ( self - > asso_active - > ip_remote , self - > asso_active - > ctx - > port_ps , TSIP_TRANSPORT ( self ) - > type , & to ) ) ) {
TSK_DEBUG_ERROR ( " Invalid HOST/PORT [%s/%u]. " , ( const char * ) self - > asso_active - > ctx - > addr_remote , self - > asso_active - > ctx - > port_ps ) ;
goto bail ;
}
if ( ( ret = tnet_sockfd_connectto ( self - > asso_active - > socket_uc - > fd , & to ) ) ) {
TSK_DEBUG_ERROR ( " Failed to connect port_uc to port_ps. " ) ;
goto bail ;
}
2012-12-03 03:11:21 +00:00
bail :
2016-02-23 21:00:35 +00:00
return ret ;
2012-12-03 03:11:21 +00:00
}
int tsip_transport_ipsec_cleanupSAs ( tsip_transport_ipsec_t * self )
{
2016-02-23 21:00:35 +00:00
int ret = - 1 ;
2012-12-03 03:11:21 +00:00
2016-02-23 21:00:35 +00:00
if ( ! self ) {
goto bail ;
}
2012-12-03 03:11:21 +00:00
2016-02-23 21:00:35 +00:00
TSK_OBJECT_SAFE_FREE ( self - > asso_temporary ) ;
TSK_OBJECT_SAFE_FREE ( self - > asso_active ) ;
2012-12-03 03:11:21 +00:00
bail :
2016-02-23 21:00:35 +00:00
return ret ;
2012-12-03 03:11:21 +00:00
}
int tsip_transport_ipsec_updateMSG ( tsip_transport_ipsec_t * self , tsip_message_t * msg )
{
2016-02-23 21:00:35 +00:00
int ret = - 1 ;
const tsip_ipsec_association_t * asso ;
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
goto bail ;
}
asso = ( self - > asso_temporary & & TSIP_REQUEST_IS_REGISTER ( msg ) ) ? self - > asso_temporary : self - > asso_active ;
if ( ! asso | | ! asso - > ctx ) {
TSK_DEBUG_ERROR ( " No IPSec association found. " ) ;
ret = - 2 ;
goto bail ;
}
if ( TSIP_MESSAGE_IS_RESPONSE ( msg ) ) {
return 0 ;
}
/* Security-Client, Require, Proxy-Require and Security Verify */
switch ( msg - > line . request . request_type ) {
case tsip_BYE :
case tsip_INVITE :
case tsip_OPTIONS :
case tsip_REGISTER :
case tsip_SUBSCRIBE :
case tsip_NOTIFY :
case tsip_REFER :
case tsip_INFO :
case tsip_UPDATE :
case tsip_MESSAGE :
case tsip_PUBLISH :
case tsip_PRACK : {
const tsk_list_item_t * item ;
TSIP_MESSAGE_ADD_HEADER ( msg , TSIP_HEADER_SECURITY_CLIENT_VA_ARGS ( " ipsec-3gpp " ,
TIPSEC_ALG_TO_STR ( asso - > ctx - > alg ) ,
TIPSEC_PROTOCOL_TO_STR ( asso - > ctx - > protocol ) ,
TIPSEC_MODE_TO_STR ( asso - > ctx - > mode ) ,
TIPSEC_EALG_TO_STR ( asso - > ctx - > ealg ) ,
asso - > ctx - > port_uc ,
asso - > ctx - > port_us ,
asso - > ctx - > spi_uc ,
asso - > ctx - > spi_us
) ) ;
/* RFC 3329 - 2.3.1 Client Initiated
All the subsequent SIP requests sent by the client to that server
SHOULD make use of the security mechanism initiated in the previous
step . These requests MUST contain a Security - Verify header field
that mirrors the server ' s list received previously in the Security -
Server header field . These requests MUST also have both a Require
and Proxy - Require header fields with the value " sec-agree " .
*/
tsk_list_foreach ( item , self - > secVerifies ) {
tsip_message_add_header ( msg , ( const tsip_header_t * ) item - > data ) ;
}
TSIP_MESSAGE_ADD_HEADER ( msg , TSIP_HEADER_REQUIRE_VA_ARGS ( " sec-agree " ) ) ;
TSIP_MESSAGE_ADD_HEADER ( msg , TSIP_HEADER_PROXY_REQUIRE_VA_ARGS ( " sec-agree " ) ) ;
break ;
}
default :
break ;
}
ret = 0 ;
/* Add Security-Server headers */
2012-12-03 03:11:21 +00:00
bail :
2016-02-23 21:00:35 +00:00
return ret ;
2012-12-03 03:11:21 +00:00
}
tnet_fd_t tsip_transport_ipsec_getFD ( tsip_transport_ipsec_t * self , int isRequest )
{
2016-02-23 21:00:35 +00:00
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return TNET_INVALID_FD ;
}
/* If no active SAs ca be found then use default connection. */
if ( ! self - > asso_active ) {
return TNET_INVALID_FD ;
// return TSIP_TRANSPORT(self)->connectedFD;
}
/* IPSec ports management
For more information : http : //betelco.blogspot.com/2008/09/ipsec-using-security-agreement-in-3gpp.html
*/
if ( TNET_SOCKET_TYPE_IS_DGRAM ( TSIP_TRANSPORT ( self ) - > type ) ) {
/*
= = = UDP = = =
port_uc - > REGISTER - > port_ps
port_ps < - 200 OK < - port_pc
*/
return self - > asso_active - > socket_uc - > fd ;
}
else {
/*
= = = TCP = = =
port_uc - > REGISTER - > port_ps
port_uc < - 200 OK < - port_ps
port_us < - NOTIFY < - port_pc
port_us - > 200 OK - > port_pc
*/
if ( isRequest ) {
return self - > asso_active - > socket_uc - > fd ;
}
else {
return self - > asso_active - > socket_us - > fd ;
}
}
return TNET_INVALID_FD ;
2012-12-03 03:11:21 +00:00
}
//========================================================
// SIP/IPSec transport object definition
//
static tsk_object_t * tsip_transport_ipsec_ctor ( tsk_object_t * self , va_list * app )
{
2016-02-23 21:00:35 +00:00
tsip_transport_ipsec_t * transport = self ;
if ( transport ) {
const struct tsip_stack_s * stack = va_arg ( * app , const struct tsip_stack_s * ) ;
const char * host = va_arg ( * app , const char * ) ;
2012-12-03 03:11:21 +00:00
# if defined(__GNUC__)
2016-02-23 21:00:35 +00:00
tnet_port_t port = ( tnet_port_t ) va_arg ( * app , unsigned ) ;
2012-12-03 03:11:21 +00:00
# else
2016-02-23 21:00:35 +00:00
tnet_port_t port = va_arg ( * app , tnet_port_t ) ;
2012-12-03 03:11:21 +00:00
# endif
2016-02-23 21:00:35 +00:00
tnet_socket_type_t type = va_arg ( * app , tnet_socket_type_t ) ;
const char * description = va_arg ( * app , const char * ) ;
/* init base */
tsip_transport_init ( TSIP_TRANSPORT ( transport ) , type , stack , host , port , description ) ;
}
return self ;
2012-12-03 03:11:21 +00:00
}
static tsk_object_t * tsip_transport_ipsec_dtor ( tsk_object_t * self )
2016-02-23 21:00:35 +00:00
{
tsip_transport_ipsec_t * transport = self ;
if ( transport ) {
/* deinit base */
tsip_transport_deinit ( TSIP_TRANSPORT ( transport ) ) ;
/* deinit self */
tsip_transport_ipsec_cleanupSAs ( transport ) ;
TSK_OBJECT_SAFE_FREE ( transport - > secVerifies ) ;
}
return self ;
2012-12-03 03:11:21 +00:00
}
static int tsip_transport_ipsec_cmp ( const tsk_object_t * obj1 , const tsk_object_t * obj2 )
{
2016-02-23 21:00:35 +00:00
const tsip_transport_ipsec_t * transport1 = obj1 ;
const tsip_transport_ipsec_t * transport2 = obj2 ;
if ( transport1 & & transport2 ) {
const char * desc1 = tsip_transport_get_description ( TSIP_TRANSPORT ( transport1 ) ) ;
const char * desc2 = tsip_transport_get_description ( TSIP_TRANSPORT ( transport2 ) ) ;
return tsk_stricmp ( desc1 , desc2 ) ;
}
return - 1 ;
2012-12-03 03:11:21 +00:00
}
2016-02-23 21:00:35 +00:00
static const tsk_object_def_t tsip_transport_ipsec_def_s = {
sizeof ( tsip_transport_ipsec_t ) ,
tsip_transport_ipsec_ctor ,
tsip_transport_ipsec_dtor ,
tsip_transport_ipsec_cmp ,
2012-12-03 03:11:21 +00:00
} ;
const tsk_object_def_t * tsip_transport_ipsec_def_t = & tsip_transport_ipsec_def_s ;
//=================================================================================================
// IPSec association object definition
//
static tsk_object_t * tsip_ipsec_association_ctor ( tsk_object_t * self , va_list * app )
{
2016-02-23 21:00:35 +00:00
tsip_ipsec_association_t * association = self ;
if ( association ) {
const tsip_transport_t * transport = va_arg ( * app , const tsip_transport_t * ) ;
/* Set transport */
association - > transport = transport ;
/* Get local IP and port. */
tsip_transport_get_ip_n_port ( transport , & association - > ip_local , & association - > port_local ) ;
/* Create IPSec context */
if ( tipsec_ctx_create (
TIPSEC_IPPROTO_FROM_STR ( transport - > protocol ) ,
TNET_SOCKET_TYPE_IS_IPV6 ( transport - > type ) ,
TIPSEC_MODE_FROM_STR ( transport - > stack - > security . ipsec . mode ) ,
TIPSEC_EALG_FROM_STR ( transport - > stack - > security . ipsec . ealg ) ,
TIPSEC_ALG_FROM_STR ( transport - > stack - > security . ipsec . alg ) ,
TIPSEC_PROTOCOL_FROM_STR ( transport - > stack - > security . ipsec . protocol ) , & association - > ctx ) ) {
TSK_DEBUG_ERROR ( " Failed to create IPSec context " ) ;
return tsk_null ;
}
/* Create Both client and Server legs */
association - > socket_us = tnet_socket_create ( association - > ip_local , TNET_SOCKET_PORT_ANY , transport - > type ) ;
association - > socket_uc = tnet_socket_create ( association - > ip_local , TNET_SOCKET_PORT_ANY , transport - > type ) ;
/* Add Both sockets to the network transport */
tsip_transport_add_socket ( transport , association - > socket_us - > fd , transport - > type , 0 , 0 ) ;
tsip_transport_add_socket ( transport , association - > socket_uc - > fd , transport - > type , 0 , 1 ) ;
/* Set local */
if ( tnet_get_peerip ( transport - > connectedFD , & association - > ip_remote ) = = 0 ) { /* Get remote IP string */
if ( tipsec_ctx_set_local ( association - > ctx , association - > ip_local , association - > ip_remote , association - > socket_uc - > port , association - > socket_us - > port ) ) {
TSK_DEBUG_ERROR ( " Failed to set IPSec local info:%s,%s,%u,%u " , association - > ip_local , association - > ip_remote , association - > socket_uc - > port , association - > socket_us - > port ) ;
return tsk_null ;
}
}
else {
// Resolve the HostName because "tipsec_ctx_set_local()" requires IP address instead of FQDN.
if ( tnet_resolve ( transport - > stack - > network . proxy_cscf [ transport - > stack - > network . transport_idx_default ] ,
transport - > stack - > network . proxy_cscf_port [ transport - > stack - > network . transport_idx_default ] ,
transport - > stack - > network . proxy_cscf_type [ transport - > stack - > network . transport_idx_default ] ,
& association - > ip_remote , tsk_null ) ) {
return tsk_null ;
}
if ( tipsec_ctx_set_local ( association - > ctx ,
association - > ip_local ,
association - > ip_remote ,
association - > socket_uc - > port ,
association - > socket_us - > port ) ) {
return tsk_null ;
}
}
}
return self ;
2012-12-03 03:11:21 +00:00
}
static tsk_object_t * tsip_ipsec_association_dtor ( tsk_object_t * self )
2016-02-23 21:00:35 +00:00
{
tsip_ipsec_association_t * association = self ;
if ( association ) {
TSK_OBJECT_SAFE_FREE ( association - > ctx ) ;
/* Remove Both sockets from the network transport and delete them. */
if ( association - > socket_uc ) {
tsip_transport_remove_socket ( association - > transport , & association - > socket_uc - > fd ) ;
TSK_OBJECT_SAFE_FREE ( association - > socket_uc ) ;
}
if ( association - > socket_us ) {
tsip_transport_remove_socket ( association - > transport , & association - > socket_us - > fd ) ;
TSK_OBJECT_SAFE_FREE ( association - > socket_us ) ;
}
}
return self ;
2012-12-03 03:11:21 +00:00
}
static int tsip_ipsec_association_cmp ( const tsk_object_t * obj1 , const tsk_object_t * obj2 )
{
2016-02-23 21:00:35 +00:00
return - 1 ;
2012-12-03 03:11:21 +00:00
}
2016-02-23 21:00:35 +00:00
static const tsk_object_def_t tsip_ipsec_association_def_s = {
sizeof ( tsip_ipsec_association_t ) ,
tsip_ipsec_association_ctor ,
tsip_ipsec_association_dtor ,
tsip_ipsec_association_cmp ,
2012-12-03 03:11:21 +00:00
} ;
const tsk_object_def_t * tsip_ipsec_association_def_t = & tsip_ipsec_association_def_s ;