[mod_sofia] fix md5 digest infoleak

[mod_sofia] refactor IP checks with sip-dig

[mod_sofia] add sipp-based unit-tests (use spawn_instead_of_system)
This commit is contained in:
Dragos Oancea 2021-05-13 13:49:09 +00:00 committed by Andrey Volk
parent 8a40ba20bd
commit c41aa83b17
17 changed files with 2503 additions and 52 deletions

View File

@ -3,7 +3,7 @@ include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_sofia
noinst_LTLIBRARIES = libsofiamod.la
libsofiamod_la_SOURCES = mod_sofia.c sofia.c sofia_json_api.c sofia_glue.c sofia_presence.c sofia_reg.c sofia_media.c sip-dig.c rtp.c mod_sofia.h
libsofiamod_la_SOURCES = mod_sofia.c sofia.c sofia_json_api.c sofia_glue.c sofia_presence.c sofia_reg.c sofia_media.c sip-dig.c rtp.c mod_sofia.h sip-dig.h
libsofiamod_la_LDFLAGS = -static
libsofiamod_la_CFLAGS = $(AM_CFLAGS) -I. $(SOFIA_SIP_CFLAGS) $(STIRSHAKEN_CFLAGS)
if HAVE_STIRSHAKEN
@ -15,7 +15,7 @@ mod_sofia_la_SOURCES =
mod_sofia_la_LIBADD = $(switch_builddir)/libfreeswitch.la libsofiamod.la
mod_sofia_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(SOFIA_SIP_LIBS) $(STIRSHAKEN_LIBS)
noinst_PROGRAMS = test/test_sofia_funcs test/test_nuafail
noinst_PROGRAMS = test/test_sofia_funcs test/test_nuafail test/sipp-based-tests
test_test_sofia_funcs_SOURCES = test/test_sofia_funcs.c
test_test_sofia_funcs_CFLAGS = $(AM_CFLAGS) $(SOFIA_SIP_CFLAGS) $(STIRSHAKEN_CFLAGS) -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
@ -33,11 +33,17 @@ endif
test_test_nuafail_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) $(STIRSHAKEN_LIBS)
test_test_nuafail_LDADD = libsofiamod.la $(SOFIA_SIP_LIBS)
TESTS = test/test_sofia_funcs.sh test/test_nuafail
test_sipp_based_tests_SOURCES = test/sipp-based-tests.c
test_sipp_based_tests_CFLAGS = $(AM_CFLAGS) $(SOFIA_SIP_CFLAGS) -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
test_sipp_based_tests_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
test_sipp_based_tests_LDADD = libsofiamod.la $(SOFIA_SIP_LIBS)
TESTS = test/test_sofia_funcs.sh test/test_nuafail test/test_run_sipp.sh
if ISMAC
mod_sofia_la_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_test_sofia_funcs_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_test_nuafail_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_sipp_based_tests_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
endif

View File

@ -91,6 +91,7 @@ typedef struct private_object private_object_t;
#define MY_EVENT_REINVITE "sofia::reinvite"
#define MY_EVENT_GATEWAY_ADD "sofia::gateway_add"
#define MY_EVENT_GATEWAY_DEL "sofia::gateway_delete"
#define MY_EVENT_GATEWAY_INVALID_DIGEST_REQ "sofia::gateway_invalid_digest_req"
#define MY_EVENT_RECOVERY "sofia::recovery_recv"
#define MY_EVENT_RECOVERY_SEND "sofia::recovery_send"
#define MY_EVENT_RECOVERY_RECOVERED "sofia::recovery_recovered"
@ -570,6 +571,7 @@ struct sofia_gateway {
sofia_cid_type_t cid_type;
char register_network_ip[80];
int register_network_port;
char *gw_auth_acl;
};
typedef enum {

View File

@ -143,6 +143,8 @@
#include "sofia-sip/su_alloc.h"
#include "sofia-sip/su_string.h"
#include "sofia-sip/hostdomain.h"
#include "sip-dig.h"
#include "mod_sofia.h"
char const name[] = "sip-dig";
@ -151,52 +153,6 @@ char const name[] = "sip-dig";
#include <string.h>
#include <stdio.h>
enum { N_TPORT = 16 };
struct transport { char const *name, *service, *srv; };
struct dig {
sres_resolver_t *sres;
unsigned preference, ip4, ip6, sips, print;
struct transport tports[N_TPORT + 1];
};
int dig_naptr(struct dig *dig, char const *host, double weight, switch_stream_handle_t *stream);
int dig_all_srvs(struct dig *dig, char const *tport, char const *host,
double weight, switch_stream_handle_t *stream);
int dig_srv(struct dig *dig, char const *tport, char const *host,
double weight, switch_stream_handle_t *stream);
int dig_srv_at(struct dig *dig,
char const *tport, sres_record_t **answers,
double weight, int pweight,
int priority, switch_stream_handle_t *stream);
int dig_addr(struct dig *dig,
char const *tport, char const *host, char const *port,
double weight, switch_stream_handle_t *stream);
void print_addr_results(struct transport const *tports,
char const *tport, char const *tport2,
sres_record_t **answers, int type, int af,
char const *port,
double weight, int preference, switch_stream_handle_t *stream);
void print_result(char const *addr, char const *port, char const *tport,
double weight,
unsigned preference,
switch_stream_handle_t *stream);
int prepare_transport(struct dig *dig, char const *tport);
int count_transports(struct dig *dig,
char const *tp1,
char const *tp2);
void _usage(int exitcode, switch_stream_handle_t *stream)
{
stream->write_function(stream, "%s", "usage: sofia_dig [OPTIONS] [@dnsserver] uri\n");
@ -204,6 +160,44 @@ void _usage(int exitcode, switch_stream_handle_t *stream)
#define usage(_x) _usage(_x, stream); goto fail
switch_bool_t verify_ip(sres_record_t **answers, const char *ip, switch_bool_t ipv6)
{
char addr[64];
int i;
if (!answers) {
return SWITCH_FALSE;
}
if (!*answers) {
return SWITCH_FALSE;
}
for (i = 0; answers[i]; i++) {
if (ipv6) {
if (answers[i]->sr_record->r_type != sres_type_aaaa)
continue;
} else {
if (answers[i]->sr_record->r_type != sres_type_a)
continue;
}
if (answers[i]->sr_record->r_status != 0)
continue;
if (ipv6) {
su_inet_ntop(AF_INET6, &answers[i]->sr_aaaa->aaaa_addr, addr, sizeof addr);
} else {
su_inet_ntop(AF_INET, &answers[i]->sr_a->a_addr, addr, sizeof addr);
}
if (ip && !strcmp(addr, ip)) {
return SWITCH_TRUE;
}
}
return SWITCH_FALSE;
}
switch_status_t sip_dig_function(_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream)
{
@ -608,6 +602,27 @@ int dig_naptr(struct dig *dig,
return count;
}
switch_bool_t dig_all_srvs_simple(struct dig *dig,
char const *host, char const *ip, switch_bool_t ipv6)
{
sres_record_t **answers;
char *domain = su_strcat(NULL, dig->tports[0].srv, host);
switch_bool_t ret;
int error = -1;
if (domain) {
error = sres_blocking_query(dig->sres, sres_type_srv, domain, 0, &answers);
free(domain);
}
if (error >= 0) {
ret = dig_srv_at_simple_verify(dig, dig->tports[0].name, answers, ip, ipv6);
if (ret) return SWITCH_TRUE;
}
return SWITCH_FALSE;
}
int dig_all_srvs(struct dig *dig,
char const *tport,
char const *host,
@ -735,6 +750,36 @@ int dig_srv(struct dig *dig,
return count;
}
switch_bool_t dig_srv_at_simple_verify(struct dig *dig,
char const *tport,
sres_record_t **answers, const char *ip, switch_bool_t ipv6)
{
int i;
sres_record_t **retanswers = { 0 };
if (!answers) {
return SWITCH_FALSE;
}
for (i = 0; answers[i]; i++) {
sres_srv_record_t const *srv = answers[i]->sr_srv;
if (srv->srv_record->r_type != sres_type_srv)
continue;
if (srv->srv_record->r_status != 0)
continue;
retanswers = dig_addr_simple(dig, srv->srv_target, ipv6?sres_type_aaaa:sres_type_a);
if (verify_ip(retanswers, ip, ipv6)) {
sres_free_answers(dig->sres, retanswers);
return SWITCH_TRUE;
}
}
if (retanswers && *retanswers) {
sres_free_answers(dig->sres, retanswers);
}
return SWITCH_FALSE;
}
int dig_srv_at(struct dig *dig,
char const *tport,
sres_record_t **answers,
@ -758,7 +803,6 @@ int dig_srv_at(struct dig *dig,
if (srv->srv_priority != priority)
continue;
snprintf(port, sizeof port, "%u", srv->srv_port);
count += dig_addr(dig, tport, srv->srv_target, port,
weight * srv->srv_weight / pweight, stream);
}
@ -766,6 +810,17 @@ int dig_srv_at(struct dig *dig,
return count;
}
sres_record_t ** dig_addr_simple(struct dig *dig,
char const *host,
uint16_t type)
{
sres_record_t **answers = NULL;
sres_blocking_query(dig->sres, type, host, 0, &answers);
return answers;
}
int dig_addr(struct dig *dig,
char const *tport,
char const *host,

View File

@ -0,0 +1,197 @@
/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2006 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**
* This is an example program for @b sresolv library in synchronous mode.
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @date Original Created: Tue Jul 16 18:50:14 2002 ppessi
*/
/**@page sip-dig Resolve SIP URIs.
*
* @section sip_dig_synopsis Synopsis
* <tt>sip-dig [OPTIONS] uri...</tt>
*
* @section sip_dig_description Description
* The @em sip-dig utility resolves SIP URIs as described in @RFC3263. It
* queries NAPTR, SRV and A/AAAA records and prints out the resulting
* transport addresses.
*
* The default transports are: UDP, TCP, SCTP, TLS and TLS-SCTP. The SIPS
* URIs are resolved using only TLS transports, TLS and TLS-SCTP. If not
* otherwise indicated by NAPTR or SRV records, the sip-dig uses UDP and TCP
* as transports for SIP and TLS for SIPS URIs.
*
* The results are printed intended, with a preference followed by weight,
* then protocol name, port number and IP address in numeric format.
*
* @section sip_dig_options Command Line Options
* The @e sip-dig utility accepts following command line options:
* <dl>
* <dt>-p <em>protoname</em></dt>
* <dd>Use named transport protocol. The <em>protoname</em> can be either
* well-known, e.g., "udp", or it can specify NAPTR service and SRV
* identifier, e.g., "tls-udp/SIPS+D2U/_sips._udp.".
* </dd>
* <dt>--udp</dt>
* <dd>Use UDP transport protocol.
* </dd>
* <dt>--tcp</dt>
* <dd>Use TCP transport protocol.
* </dd>
* <dt>--tls</dt>
* <dd>Use TLS over TCP transport protocol.
* </dd>
* <dt>--sctp</dt>
* <dd>Use SCTP transport protocol.
* </dd>
* <dt>--tls-sctp</dt>
* <dd>Use TLS over SCTP transport protocol.
* </dd>
* <dt>--no-sctp</dt>
* <dd>Ignore SCTP or TLS-SCTP records in the list of default transports.
* This option has no effect if transport protocols has been explicitly
* listed.
* </dd>
* <dt>-4</dt>
* <dd>Query IP4 addresses (A records)
* </dd>
* <dt>-6</dt>
* <dd>Query IP6 addresses (AAAA records).
* </dd>
* <dt>-v</dt>
* <dd>Be verbatim.
* </dd>
* <dt></dt>
* <dd>
* </dd>
* </dl>
*
* @section sip_dig_return Return Codes
* <table>
* <tr><td>0<td>when successful (a 2XX-series response is received)
* <tr><td>1<td>when unsuccessful (a 3XX..6XX-series response is received)
* <tr><td>2<td>initialization failure
* </table>
*
* @section sip_dig_examples Examples
*
* Resolve sip:openlaboratory.net, prefer TLS over TCP, TCP over UDP:
* @code
* $ sip-dig --tls --tcp --udp sip:openlaboratory.net
* 1 0.333 tls 5061 212.213.221.127
* 2 0.333 tcp 5060 212.213.221.127
* 3 0.333 udp 5060 212.213.221.127
* @endcode
*
* Resolve sips:example.net with TLS over SCTP (TLS-SCTP) and TLS:
* @code
* $ sip-dig -p tls-sctp --tls sips:example.net
* 1 0.500 tls-udp 5061 172.21.55.26
* 2 0.500 tls 5061 172.21.55.26
* @endcode
*
* @section sip_dig_environment Environment
* #SRESOLV_DEBUG, SRESOLV_CONF
*
* @section sip_dig_bugs Reporting Bugs
* Report bugs to <sofia-sip-devel@lists.sourceforge.net>.
*
* @section sip_dig_author Author
* Written by Pekka Pessi <pekka -dot pessi -at- nokia -dot- com>
*
* @section sip_dig_copyright Copyright
* Copyright (C) 2006 Nokia Corporation.
*
* This program is free software; see the source for copying conditions.
* There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.
*/
#include "sofia-resolv/sres.h"
#include "sofia-resolv/sres_record.h"
enum { N_TPORT = 16 };
struct transport { char const *name, *service, *srv; };
struct dig {
sres_resolver_t *sres;
unsigned preference, ip4, ip6, sips, print;
struct transport tports[N_TPORT + 1];
};
int dig_naptr(struct dig *dig, char const *host, double weight, switch_stream_handle_t *stream);
int dig_all_srvs(struct dig *dig, char const *tport, char const *host,
double weight, switch_stream_handle_t *stream);
int dig_srv(struct dig *dig, char const *tport, char const *host,
double weight, switch_stream_handle_t *stream);
int dig_srv_at(struct dig *dig,
char const *tport, sres_record_t **answers,
double weight, int pweight,
int priority, switch_stream_handle_t *stream);
int dig_addr(struct dig *dig,
char const *tport, char const *host, char const *port,
double weight, switch_stream_handle_t *stream);
void print_addr_results(struct transport const *tports,
char const *tport, char const *tport2,
sres_record_t **answers, int type, int af,
char const *port,
double weight, int preference, switch_stream_handle_t *stream);
void print_result(char const *addr, char const *port, char const *tport,
double weight,
unsigned preference,
switch_stream_handle_t *stream);
int prepare_transport(struct dig *dig, char const *tport);
int count_transports(struct dig *dig,
char const *tp1,
char const *tp2);
switch_bool_t dig_srv_at_simple_verify(struct dig *dig,
char const *tport,
sres_record_t **answers, const char *ip, switch_bool_t ipv6);
switch_bool_t dig_all_srvs_simple(struct dig *dig,
char const *host, const char *ip, switch_bool_t ipv6);
sres_record_t ** dig_addr_simple(struct dig *dig,
char const *host,
uint16_t type);
switch_bool_t sofia_sip_resolve_compare(const char *domainname, const char *ip);
switch_bool_t verify_ip(sres_record_t **answers, const char *ip, switch_bool_t ipv6);

View File

@ -3973,6 +3973,10 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag,
}
gateway->register_transport = transport;
} else if (!strcmp(var, "gw-auth-acl")) {
if (!zstr(val)) {
gateway->gw_auth_acl = switch_core_strdup(gateway->pool, val);
}
}
}

View File

@ -38,6 +38,8 @@
*
*/
#include "mod_sofia.h"
#include "sofia-sip/hostdomain.h"
#include "sip-dig.h"
static void sofia_reg_new_handle(sofia_gateway_t *gateway_ptr, int attach)
{
@ -2432,6 +2434,126 @@ void sofia_reg_handle_sip_i_register(nua_t *nua, sofia_profile_t *profile, nua_h
}
switch_bool_t sip_resolve_prepare_transport(struct dig *dig, sofia_transport_t tport)
{
struct transport *tports = dig->tports;
if (tport == SOFIA_TRANSPORT_UDP) {
tports[0].name = "udp";
tports[0].service = "SIP+D2U";
tports[0].srv = "_sip._udp.";
}
else if (tport == SOFIA_TRANSPORT_TCP) {
tports[0].name = "tcp";
tports[0].service = "SIP+D2T";
tports[0].srv = "_sip._tcp.";
}
else if (tport == SOFIA_TRANSPORT_TCP_TLS) {
tports[0].name = "tls";
tports[0].service = "SIPS+D2T";
tports[0].srv = "_sips._tcp.";
}
else if (tport == SOFIA_TRANSPORT_SCTP) {
tports[0].name = "sctp";
tports[0].service = "SIP+D2S";
tports[0].srv = "_sip._sctp.";
} else {
return SWITCH_FALSE;
}
return SWITCH_TRUE;
}
/* resolve domain name (SRV + A + AAAA) and compare result with passed IP address */
switch_bool_t sip_resolve_compare(const char *domainname, const char *ip, sofia_transport_t transport)
{
url_t *uri = NULL;
char const *host;
char const *port;
struct dig dig[1] = {{ NULL }};
su_home_t *home = NULL;
sres_record_t **answers = NULL;
switch_bool_t ret = SWITCH_FALSE, ipv6 = SWITCH_FALSE;
if (!host_is_domain(domainname)) {
return ret;
}
if (!sip_resolve_prepare_transport(dig, transport)) {
return ret;
}
home = su_home_new(sizeof(*home));
dig->sres = sres_resolver_new(getenv("SRESOLV_CONF"));
uri = url_hdup(home, (void *)domainname);
if (uri && uri->url_type == url_unknown)
url_sanitize(uri);
if (uri && uri->url_type == url_any) {
goto out;
}
if (!uri || (uri->url_type != url_sip && uri->url_type != url_sips)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid uri\n");
goto out;
}
port = url_port(uri);
if (port && !port[0]) port = NULL;
host = uri->url_host;
if (strchr(ip, ':')) {
ipv6 = SWITCH_TRUE;
}
ret = dig_all_srvs_simple(dig, domainname, ip, ipv6);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "verify 1\n");
if (!ret) {
answers = dig_addr_simple(dig, host, ipv6?sres_type_aaaa:sres_type_a);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "verify 2\n");
ret = verify_ip(answers, ip, ipv6);
}
out:
su_home_unref(home);
sres_resolver_unref(dig->sres);
return ret;
}
static switch_bool_t is_legitimate_gateway(sofia_dispatch_event_t *de, sofia_gateway_t *gateway)
{
char remote_ip[80] = { 0 };
switch_bool_t ret = SWITCH_FALSE;
sofia_glue_get_addr(de->data->e_msg, remote_ip, sizeof(remote_ip), NULL);
if (gateway->gw_auth_acl) {
ret = switch_check_network_list_ip(remote_ip, gateway->gw_auth_acl);
if (!ret) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Challange from [%s] denied by gw-auth-acl.\n", remote_ip);
}
return ret;
} else {
char *register_host = sofia_glue_get_register_host(gateway->register_proxy);
const char *host = sofia_glue_strip_proto(register_host);
if (host_is_ip_address(host)) {
if (host && !strcmp(host, remote_ip)) {
ret = SWITCH_TRUE;
}
switch_safe_free(register_host);
return ret;
} else {
ret = sip_resolve_compare(host, remote_ip, gateway->register_transport);
switch_safe_free(register_host);
return ret;
}
}
}
void sofia_reg_handle_sip_r_register(int status,
char const *phrase,
@ -2463,6 +2585,7 @@ void sofia_reg_handle_sip_r_register(int status,
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &gateway->register_network_port);
if (!zstr_buf(network_ip)) {
snprintf(gateway->register_network_ip, sizeof(gateway->register_network_ip), (msg_addrinfo(de->data->e_msg))->ai_addr->sa_family == AF_INET6 ? "[%s]" : "%s", network_ip);
}
}
@ -2684,11 +2807,22 @@ void sofia_reg_handle_sip_r_challenge(int status,
if (sip_auth_username && sip_auth_password) {
switch_snprintf(authentication, sizeof(authentication), "%s:%s:%s:%s", scheme, realm, sip_auth_username, sip_auth_password);
} else if (gateway) {
} else if (gateway && is_legitimate_gateway(de, gateway)) {
switch_snprintf(authentication, sizeof(authentication), "%s:%s:%s:%s", scheme, realm, gateway->auth_username, gateway->register_password);
} else {
if (gateway) {
switch_event_t *s_event;
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_GATEWAY_INVALID_DIGEST_REQ) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Gateway", gateway->name);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", gateway->profile->name);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "realm", realm);
switch_event_fire(&s_event);
}
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Cannot locate any authentication credentials to complete an authentication request for realm '%s'\n", realm);
"Cannot locate any authentication credentials to complete an authentication request for realm '%s'\n", realm);
goto cancel;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2021, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dragos Oancea <dragos@signalwire.com>
*
*
* sipp-based-tests.c - Test FreeSwitch using sipp (https://github.com/SIPp/sipp)
*
*/
#include <switch.h>
#include <test/switch_test.h>
#include <stdlib.h>
int test_success = 0;
int test_sofia_debug = 1;
static switch_bool_t has_ipv6()
{
switch_stream_handle_t stream = { 0 };
SWITCH_STANDARD_STREAM(stream);
switch_api_execute("sofia", "status profile external-ipv6", NULL, &stream);
if (strstr((char *)stream.data, "Invalid Profile")) {
switch_safe_free(stream.data);
return SWITCH_FALSE;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "STATUS PROFILE: %s\n", (char *) stream.data);
switch_safe_free(stream.data);
return SWITCH_TRUE;
}
static int start_sipp_uac(const char *ip, int remote_port,const char *scenario_uac, const char *extra)
{
char *cmd = switch_mprintf("sipp %s:%d -nr -p 5062 -m 1 -s 1001 -recv_timeout 10000 -timeout 10s -sf %s -bg %s", ip, remote_port, scenario_uac, extra);
int sys_ret = switch_system(cmd, SWITCH_TRUE);
printf("%s\n", cmd);
switch_safe_free(cmd);
switch_sleep(1000 * 1000);
return sys_ret;
}
static void kill_sipp(void)
{
switch_system("pkill -x sipp", SWITCH_TRUE);
switch_sleep(1000 * 1000);
}
static void event_handler(switch_event_t *event)
{
const char *new_ev = switch_event_get_header(event, "Event-Subclass");
char *str;
if (new_ev && !strcmp(new_ev, "sofia::gateway_invalid_digest_req")) {
test_success = 1;
}
/*print the event*/
switch_event_serialize_json(event, &str);
if (str) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s\n", str);
switch_safe_free(str);
}
}
FST_CORE_EX_BEGIN("./conf-sipp", SCF_VG | SCF_USE_SQL)
{
FST_MODULE_BEGIN(mod_sofia, uac-uas)
{
FST_SETUP_BEGIN()
{
switch_stream_handle_t stream = { 0 };
SWITCH_STANDARD_STREAM(stream);
switch_api_execute("sofia", "global siptrace on", NULL, &stream);
if (test_sofia_debug) {
switch_api_execute("sofia", "loglevel all 9", NULL, &stream);
switch_api_execute("sofia", "tracelevel debug", NULL, &stream);
}
switch_safe_free(stream.data);
switch_core_set_variable("spawn_instead_of_system", "true");
fst_requires_module("mod_sndfile");
fst_requires_module("mod_voicemail");
fst_requires_module("mod_sofia");
fst_requires_module("mod_loopback");
fst_requires_module("mod_console");
fst_requires_module("mod_dptools");
fst_requires_module("mod_dialplan_xml");
fst_requires_module("mod_commands");
fst_requires_module("mod_say_en");
fst_requires_module("mod_tone_stream");
}
FST_SETUP_END()
FST_TEARDOWN_BEGIN()
{
}
FST_TEARDOWN_END()
FST_TEST_BEGIN(uac_digest_leak_udp)
{
switch_core_session_t *session;
switch_call_cause_t cause;
switch_status_t status;
switch_channel_t *channel;
const char *local_ip_v4 = switch_core_get_variable("local_ip_v4");
int sipp_ret;
switch_event_bind("sofia", SWITCH_EVENT_CUSTOM, NULL, event_handler, NULL);
status = switch_ivr_originate(NULL, &session, &cause, "loopback/+15553334444", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
sipp_ret = start_sipp_uac(local_ip_v4, 5080, "sipp-scenarios/uac_digest_leak.xml", "");
if (sipp_ret < 0 || sipp_ret == 127) {
fst_requires(0); /* sipp not found */
}
fst_check(status == SWITCH_STATUS_SUCCESS);
if (!session) {
fst_requires(session);
}
channel = switch_core_session_get_channel(session);
fst_xcheck(switch_channel_get_state(channel) < CS_HANGUP, "Expect call not to be hung up");
while (1) {
int ret;
switch_sleep(1000 * 1000);
ret = switch_system("pidof sipp", SWITCH_TRUE);
if (!ret) {
break;
}
}
switch_sleep(5000 * 1000);
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
switch_core_session_rwunlock(session);
switch_sleep(1000 * 1000);
switch_event_unbind_callback(event_handler);
/* sipp should timeout, attempt kill, just in case.*/
kill_sipp();
fst_check(test_success);
test_success = 0;
}
FST_TEST_END()
FST_TEST_BEGIN(uac_digest_leak_tcp)
{
switch_core_session_t *session;
switch_call_cause_t cause;
switch_status_t status;
switch_channel_t *channel;
const char *local_ip_v4 = switch_core_get_variable("local_ip_v4");
int sipp_ret;
switch_event_bind("sofia", SWITCH_EVENT_CUSTOM, NULL, event_handler, NULL);
status = switch_ivr_originate(NULL, &session, &cause, "loopback/+15553334444", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
sipp_ret = start_sipp_uac(local_ip_v4, 5080, "sipp-scenarios/uac_digest_leak-tcp.xml", "-t t1");
if (sipp_ret < 0 || sipp_ret == 127) {
fst_requires(0); /* sipp not found */
}
fst_check(status == SWITCH_STATUS_SUCCESS);
if (!session) {
fst_requires(session);
}
channel = switch_core_session_get_channel(session);
fst_xcheck(switch_channel_get_state(channel) < CS_HANGUP, "Expect call not to be hung up");
while (1) {
int ret;
switch_sleep(1000 * 1000);
ret = switch_system("pidof sipp", SWITCH_TRUE);
if (!ret) {
break;
}
}
switch_sleep(5000 * 1000);
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
switch_core_session_rwunlock(session);
switch_sleep(1000 * 1000);
switch_event_unbind_callback(event_handler);
/* sipp should timeout, attempt kill, just in case.*/
kill_sipp();
fst_check(test_success);
test_success = 0;
}
FST_TEST_END()
FST_TEST_BEGIN(uac_digest_leak_udp_ipv6)
{
switch_core_session_t *session;
switch_call_cause_t cause;
switch_status_t status;
switch_channel_t *channel;
const char *local_ip_v6 = switch_core_get_variable("local_ip_v6");
int sipp_ret;
char *ipv6 = NULL;
if (!has_ipv6()) {
goto skiptest;
}
switch_event_bind("sofia", SWITCH_EVENT_CUSTOM, NULL, event_handler, NULL);
if (!strchr(local_ip_v6,'[')) {
ipv6 = switch_mprintf("[%s]", local_ip_v6);
}
status = switch_ivr_originate(NULL, &session, &cause, "loopback/+15553334444", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
if (!ipv6) {
sipp_ret = start_sipp_uac(local_ip_v6, 6060, "sipp-scenarios/uac_digest_leak-ipv6.xml", "-i [::1]");
} else {
sipp_ret = start_sipp_uac(ipv6, 6060, "sipp-scenarios/uac_digest_leak-ipv6.xml", "-i [::1] -mi [::1]");
}
if (sipp_ret < 0 || sipp_ret == 127) {
fst_requires(0); /* sipp not found */
}
fst_check(status == SWITCH_STATUS_SUCCESS);
if (!session) {
fst_requires(session);
}
channel = switch_core_session_get_channel(session);
fst_xcheck(switch_channel_get_state(channel) < CS_HANGUP, "Expect call not to be hung up");
while (1) {
int ret;
switch_sleep(1000 * 1000);
ret = switch_system("pidof sipp", SWITCH_TRUE);
if (!ret) {
break;
}
}
switch_sleep(5000 * 1000);
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
switch_core_session_rwunlock(session);
switch_sleep(1000 * 1000);
switch_event_unbind_callback(event_handler);
/* sipp should timeout, attempt kill, just in case.*/
kill_sipp();
switch_safe_free(ipv6);
fst_check(test_success);
skiptest:
test_success = 0;
}
FST_TEST_END()
}
FST_MODULE_END()
}
FST_CORE_END()

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- SIP digest leak test scenario -->
<!-- Note: realm at WWW-Authenticate may have to be changed if phone realm configuration is not empty -->
<!-- (empty realm at phone configuration = auth against any realm) -->
<!-- http://tomeko.net -->
<scenario name="SIP digest leak test">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<!-- FS: ipv4 in sdp is intentional -->
<send retrans="500">
<![CDATA[
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:1001@[local_ip]:[local_port]>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:1001@[local_ip]:[local_port]
Max-Forwards: 70
Subject: Performance Test
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] 0000:0000:0000:0000:0000:0000:0000:0001
s=-
c=IN IP6 0000:0000:0000:0000:0000:0000:0000:0001
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv response="100"
optional="true">
</recv>
<recv response="183" optional="true">
</recv>
<recv response="180" optional="true">
</recv>
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv response="200" rtd="true">
</recv>
<!-- Packet lost can be simulated in any send/recv message by -->
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
<send>
<![CDATA[
ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Contact: sip:1001@[local_ip]:[local_port]
Max-Forwards: 70
Subject: Performance Test
Content-Length: 0
]]>
</send>
<!-- At this moment second party is hanging up call (no audio there) -->
<recv request="BYE"></recv>
<!-- Test with SIP hardware phone: must use WWW-Authenticate instead of Proxy-Authenticate.
You could also try with SIP/2.0 401 Unauthorized. -->
<send>
<![CDATA[
SIP/2.0 407 Proxy Authentication Required
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Proxy-Authenticate: Digest algorithm=MD5, realm="freeswitch.org", nonce="69d327e5"
Content-Length: 0
]]>
</send>
<recv request="BYE"></recv>
</scenario>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- SIP digest leak test scenario -->
<!-- Note: realm at WWW-Authenticate may have to be changed if phone realm configuration is not empty -->
<!-- (empty realm at phone configuration = auth against any realm) -->
<!-- http://tomeko.net -->
<scenario name="SIP digest leak test">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
INVITE sip:[service]@[remote_ip]:[remote_port];transport=tcp SIP/2.0
Via: SIP/2.0/[transport] 127.0.0.1:[local_port];branch=[branch];transport=tcp
From: sipp <sip:1001@127.0.0.1:[local_port];transport=tcp>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port];transport=tcp>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: <sip:1001@127.0.0.1:[local_port];transport=tcp>
Max-Forwards: 70
Subject: Performance Test
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] 127.0.0.1
s=-
c=IN IP[media_ip_type] 127.0.0.1
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv response="100"
optional="true">
</recv>
<recv response="183" optional="true">
</recv>
<recv response="180" optional="true">
</recv>
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv response="200" rtd="true">
</recv>
<!-- Packet lost can be simulated in any send/recv message by -->
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
<send>
<![CDATA[
ACK sip:[service]@[remote_ip]:[remote_port];transport=tcp SIP/2.0
Via: SIP/2.0/[transport] 127.0.0.1:[local_port];branch=[branch];transport=tcp
From: sipp <sip:sipp@127.0.0.1:[local_port]>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Contact: <sip:1001@127.0.0.1:[local_port];transport=tcp>
Max-Forwards: 70
Subject: Performance Test
Content-Length: 0
]]>
</send>
<!-- At this moment second party is hanging up call (no audio there) -->
<recv request="BYE"></recv>
<!-- <pause milliseconds="1000"/> -->
<!-- Test with SIP hardware phone: must use WWW-Authenticate instead of Proxy-Authenticate.
You could also try with SIP/2.0 401 Unauthorized. -->
<send>
<![CDATA[
SIP/2.0 407 Proxy Authentication Required
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Proxy-Authenticate: Digest algorithm=MD5, realm="freeswitch.org", nonce="69d327e5"
Content-Length: 0
]]>
</send>
<recv request="BYE"></recv>
</scenario>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- SIP digest leak test scenario -->
<!-- Note: realm at WWW-Authenticate may have to be changed if phone realm configuration is not empty -->
<!-- (empty realm at phone configuration = auth against any realm) -->
<!-- http://tomeko.net -->
<scenario name="SIP digest leak test">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] 127.0.0.1:[local_port];branch=[branch]
From: sipp <sip:1001@127.0.0.1:[local_port]>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:1001@127.0.0.1:[local_port]
Max-Forwards: 70
Subject: Performance Test
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv response="100"
optional="true">
</recv>
<recv response="183" optional="true">
</recv>
<recv response="180" optional="true">
</recv>
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv response="200" rtd="true">
</recv>
<!-- Packet lost can be simulated in any send/recv message by -->
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
<send>
<![CDATA[
ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] 127.0.0.1:[local_port];branch=[branch]
From: sipp <sip:sipp@127.0.0.1:[local_port]>;tag=[call_number]
To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Contact: sip:1001@127.0.0.1:[local_port]
Max-Forwards: 70
Subject: Performance Test
Content-Length: 0
]]>
</send>
<!-- At this moment second party is hanging up call (no audio there) -->
<recv request="BYE"></recv>
<!-- Test with SIP hardware phone: must use WWW-Authenticate instead of Proxy-Authenticate.
You could also try with SIP/2.0 401 Unauthorized. -->
<send>
<![CDATA[
SIP/2.0 407 Proxy Authentication Required
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Proxy-Authenticate: Digest algorithm=MD5, realm="freeswitch.org", nonce="69d327e5"
Content-Length: 0
]]>
</send>
<recv request="BYE"></recv>
</scenario>

View File

@ -0,0 +1,3 @@
#/bin/sh
./sipp-based-tests