|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
<depend>pjproject</depend>
|
|
|
|
|
<depend>res_pjsip</depend>
|
|
|
|
|
<depend>res_pjsip_session</depend>
|
|
|
|
|
<depend>res_pjsip_outbound_authenticator_digest</depend>
|
|
|
|
|
<support_level>core</support_level>
|
|
|
|
|
***/
|
|
|
|
|
|
|
|
|
@ -21,6 +22,7 @@
|
|
|
|
|
#include "asterisk/threadpool.h"
|
|
|
|
|
|
|
|
|
|
#include "res_pjsip_volte/netlink_xfrm.h"
|
|
|
|
|
#include "res_pjsip_volte/milenage.h"
|
|
|
|
|
|
|
|
|
|
#define PJ_CONSUME PJ_TRUE
|
|
|
|
|
|
|
|
|
@ -29,6 +31,7 @@ const pj_str_t STR_REQUIRE = { "Require", 7 };
|
|
|
|
|
const pj_str_t STR_PROXY_REQUIRE = { "Proxy-Require", 13 };
|
|
|
|
|
const pj_str_t STR_PATH = { "path", 4 };
|
|
|
|
|
const pj_str_t STR_SEC_AGREE = { "sec-agree", 9 };
|
|
|
|
|
const pj_str_t STR_AUTHORIZATION = { "Authorization", 13 };
|
|
|
|
|
|
|
|
|
|
const pj_str_t STR_SECURITY_CLIENT = { "Security-Client", 15 };
|
|
|
|
|
const pj_str_t STR_SECURITY_SERVER = { "Security-Server", 15 };
|
|
|
|
@ -45,10 +48,12 @@ const struct ipsec_alg ipsec_alg[] = {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct ipsec_alg ipsec_ealg[] = {
|
|
|
|
|
{ "null", "cipher_null" },
|
|
|
|
|
{ "null", "ecb(cipher_null)" },
|
|
|
|
|
{ NULL, NULL }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static unsigned char aka_res[8];
|
|
|
|
|
|
|
|
|
|
static struct mnl_socket *g_mnl_socket = NULL;
|
|
|
|
|
struct sockaddr_storage g_src_addr, g_dst_addr;
|
|
|
|
|
static uint32_t g_spi_c, g_spi_s;
|
|
|
|
@ -88,6 +93,7 @@ static char *sockaddr_storage_to_string(const struct sockaddr_storage *addr, cha
|
|
|
|
|
return ip_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Delete old SA and SP entries upon new registration or module exit. */
|
|
|
|
|
static void cleanup_xfrm(void)
|
|
|
|
|
{
|
|
|
|
|
if (g_spi_c_valid) {
|
|
|
|
@ -99,6 +105,7 @@ static void cleanup_xfrm(void)
|
|
|
|
|
g_spi_s_valid = PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static pj_status_t on_load(pjsip_endpoint *endpt)
|
|
|
|
|
{
|
|
|
|
|
printf("endpoint!\n");
|
|
|
|
@ -111,23 +118,29 @@ static pj_bool_t is_ims(const pjsip_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
pjsip_fromto_hdr *hdr;
|
|
|
|
|
pjsip_sip_uri *uri;
|
|
|
|
|
size_t uri_len;
|
|
|
|
|
size_t host_len;
|
|
|
|
|
|
|
|
|
|
/* Get URI. */
|
|
|
|
|
hdr = pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL);
|
|
|
|
|
if (!hdr)
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
uri = pjsip_uri_get_uri(hdr->uri);
|
|
|
|
|
uri_len = strlen(uri->host.ptr);
|
|
|
|
|
host_len = uri->host.slen;
|
|
|
|
|
|
|
|
|
|
if (uri_len < 20)
|
|
|
|
|
if (host_len < 20) {
|
|
|
|
|
ast_log(LOG_DEBUG, "SIP message is not IMS message, ignoring.");
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!!strncmp(uri->host.ptr, "ims.", 4))
|
|
|
|
|
if (!!strncmp(uri->host.ptr, "ims.", 4)) {
|
|
|
|
|
ast_log(LOG_DEBUG, "SIP message is not IMS message, ignoring.");
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!!strncmp(uri->host.ptr + uri_len - 16, ".3gppnetwork.org", 16))
|
|
|
|
|
if (!!strncmp(uri->host.ptr + host_len - 16, ".3gppnetwork.org", 16)) {
|
|
|
|
|
ast_log(LOG_DEBUG, "SIP message is not IMS message, ignoring.");
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PJ_TRUE;
|
|
|
|
|
}
|
|
|
|
@ -205,54 +218,63 @@ static void add_value_array_hdr(pjsip_tx_data *tdata, const pj_str_t *name, cons
|
|
|
|
|
static pj_status_t on_tx_register_request(pjsip_tx_data *tdata)
|
|
|
|
|
{
|
|
|
|
|
char src_str[64], dst_str[64];
|
|
|
|
|
pjsip_generic_array_hdr *hdr;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR, "Unsetting RES paassword.\n");
|
|
|
|
|
volte_auth = NULL;
|
|
|
|
|
|
|
|
|
|
if (!is_ims(tdata->msg))
|
|
|
|
|
return PJ_SUCCESS;
|
|
|
|
|
|
|
|
|
|
/* Get local and remote peer address. */
|
|
|
|
|
copy_pj_sockaddr_to_sockaddr_storage(&tdata->tp_info.transport->local_addr, &g_src_addr);
|
|
|
|
|
copy_pj_sockaddr_to_sockaddr_storage(&tdata->tp_info.dst_addr, &g_dst_addr);
|
|
|
|
|
ast_log(LOG_DEBUG, "peers %s->%s\n", sockaddr_storage_to_string(&g_src_addr, src_str, sizeof(src_str)), sockaddr_storage_to_string(&g_dst_addr, dst_str, sizeof(dst_str)));
|
|
|
|
|
|
|
|
|
|
/* Allocate SPI-C and SPI-S towards remote peer. */
|
|
|
|
|
rc = xfrm_spi_alloc(g_mnl_socket, 2342, &g_spi_c, (const struct sockaddr *)&g_src_addr, (const struct sockaddr *)&g_dst_addr);
|
|
|
|
|
if (rc < 0) {
|
|
|
|
|
spi_alloc_failed:
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to request SPI.\n");
|
|
|
|
|
return PJ_CONSUME;
|
|
|
|
|
}
|
|
|
|
|
g_spi_s_valid = PJ_TRUE;
|
|
|
|
|
rc = xfrm_spi_alloc(g_mnl_socket, 2342, &g_spi_s, (const struct sockaddr *)&g_src_addr, (const struct sockaddr *)&g_dst_addr);
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
goto spi_alloc_failed;
|
|
|
|
|
g_spi_c_valid = PJ_TRUE;
|
|
|
|
|
ast_log(LOG_DEBUG, "SPI-C=0x%08x SPI-S=0x%08x\n", g_spi_s, g_spi_c);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* "Require: sec-agree" */
|
|
|
|
|
add_value_array_hdr(tdata, &STR_REQUIRE, &STR_SEC_AGREE);
|
|
|
|
|
/* "Proxy-Require: sec-agree" */
|
|
|
|
|
add_value_string_hdr(tdata, &STR_PROXY_REQUIRE, &STR_SEC_AGREE);
|
|
|
|
|
/* "Supported: path,sec-agree" */
|
|
|
|
|
add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_PATH);
|
|
|
|
|
/* "Security-Client: ..." */
|
|
|
|
|
add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_SEC_AGREE);
|
|
|
|
|
add_securety_client_hdr(tdata, ipsec_alg, ipsec_ealg, g_spi_c, g_spi_s, 43419, 42318);
|
|
|
|
|
|
|
|
|
|
pj_bool_t created = PJ_FALSE;
|
|
|
|
|
|
|
|
|
|
/* Create header, if not yet created. */
|
|
|
|
|
hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &STR_SECURITY_CLIENT, NULL);
|
|
|
|
|
if (!hdr) {
|
|
|
|
|
puts("first register\n");
|
|
|
|
|
cleanup_xfrm();
|
|
|
|
|
|
|
|
|
|
/* Get local and remote peer address. */
|
|
|
|
|
copy_pj_sockaddr_to_sockaddr_storage(&tdata->tp_info.transport->local_addr, &g_src_addr);
|
|
|
|
|
copy_pj_sockaddr_to_sockaddr_storage(&tdata->tp_info.dst_addr, &g_dst_addr);
|
|
|
|
|
ast_log(LOG_DEBUG, "peers %s->%s\n", sockaddr_storage_to_string(&g_src_addr, src_str, sizeof(src_str)), sockaddr_storage_to_string(&g_dst_addr, dst_str, sizeof(dst_str)));
|
|
|
|
|
|
|
|
|
|
/* Allocate SPI-C and SPI-S towards remote peer. */
|
|
|
|
|
rc = xfrm_spi_alloc(g_mnl_socket, 2342, &g_spi_c, (const struct sockaddr *)&g_src_addr, (const struct sockaddr *)&g_dst_addr);
|
|
|
|
|
if (rc < 0) {
|
|
|
|
|
spi_alloc_failed:
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to request SPI.\n");
|
|
|
|
|
return PJ_CONSUME;
|
|
|
|
|
}
|
|
|
|
|
g_spi_s_valid = PJ_TRUE;
|
|
|
|
|
rc = xfrm_spi_alloc(g_mnl_socket, 2342, &g_spi_s, (const struct sockaddr *)&g_src_addr, (const struct sockaddr *)&g_dst_addr);
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
goto spi_alloc_failed;
|
|
|
|
|
g_spi_c_valid = PJ_TRUE;
|
|
|
|
|
ast_log(LOG_DEBUG, "SPI-C=0x%08x SPI-S=0x%08x\n", g_spi_s, g_spi_c);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* "Require: sec-agree" */
|
|
|
|
|
add_value_array_hdr(tdata, &STR_REQUIRE, &STR_SEC_AGREE);
|
|
|
|
|
/* "Proxy-Require: sec-agree" */
|
|
|
|
|
add_value_string_hdr(tdata, &STR_PROXY_REQUIRE, &STR_SEC_AGREE);
|
|
|
|
|
/* "Supported: path,sec-agree" */
|
|
|
|
|
add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_PATH);
|
|
|
|
|
/* "Security-Client: ..." */
|
|
|
|
|
add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_SEC_AGREE);
|
|
|
|
|
add_securety_client_hdr(tdata, ipsec_alg, ipsec_ealg, g_spi_c, g_spi_s, 43419, 42318);
|
|
|
|
|
|
|
|
|
|
#warning HACKING: Asterisk must do this on first register! Or we must do it!
|
|
|
|
|
const pj_str_t STR_AUTHORIZATION = { "Authorization", 13 };
|
|
|
|
|
char xxx[] = "Digest uri=\"sip:ims.mnc001.mcc238.3gppnetwork.org\",username=\"238010000090828@ims.mnc001.mcc238.3gppnetwork.org\",response=\"\",realm=\"ims.mnc001.mcc238.3gppnetwork.org\",nonce=\"\"";
|
|
|
|
|
const pj_str_t STR_XXX = { xxx, strlen(xxx)};
|
|
|
|
|
add_value_string_hdr(tdata, &STR_AUTHORIZATION, &STR_XXX);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char xxx[] = "Digest uri=\"sip:ims.mnc001.mcc238.3gppnetwork.org\",username=\"238010000090828@ims.mnc001.mcc238.3gppnetwork.org\",response=\"\",realm=\"ims.mnc001.mcc238.3gppnetwork.org\",nonce=\"\"";
|
|
|
|
|
const pj_str_t STR_XXX = { xxx, strlen(xxx)};
|
|
|
|
|
add_value_string_hdr(tdata, &STR_AUTHORIZATION, &STR_XXX);
|
|
|
|
|
} else {
|
|
|
|
|
puts("second register\n");
|
|
|
|
|
hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &STR_AUTHORIZATION, NULL);
|
|
|
|
|
if (hdr)
|
|
|
|
|
pj_list_erase(hdr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PJ_SUCCESS;
|
|
|
|
|
}
|
|
|
|
@ -288,11 +310,14 @@ static pj_bool_t on_rx_401_response(pjsip_rx_data *rdata)
|
|
|
|
|
{
|
|
|
|
|
pjsip_generic_string_hdr *hdr;
|
|
|
|
|
|
|
|
|
|
if (!is_ims(rdata->msg_info.msg))
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
|
|
|
|
|
/* Get Security-Server from header. */
|
|
|
|
|
hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_SECURITY_SERVER, NULL);
|
|
|
|
|
if (!hdr) {
|
|
|
|
|
ast_log(LOG_ERROR, "Missing 'Security-Server' in REGISTER reply.");
|
|
|
|
|
return PJ_CONSUME;
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
#if 0
|
|
|
|
|
if (!hdr->count) {
|
|
|
|
@ -303,6 +328,48 @@ static pj_bool_t on_rx_401_response(pjsip_rx_data *rdata)
|
|
|
|
|
puts("1");
|
|
|
|
|
printf("hurra: %s\n", hdr->hvalue.ptr);
|
|
|
|
|
|
|
|
|
|
pjsip_www_authenticate_hdr *auth_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_WWW_AUTHENTICATE, NULL);
|
|
|
|
|
if (!auth_hdr || !auth_hdr->challenge.digest.nonce.ptr || !auth_hdr->challenge.digest.algorithm.ptr) {
|
|
|
|
|
ast_log(LOG_ERROR, "Authentication header missing or incomplete.\n");
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("NONCE: %s\n", auth_hdr->challenge.digest.nonce.ptr);
|
|
|
|
|
printf("algo: %s\n", auth_hdr->challenge.digest.algorithm.ptr);
|
|
|
|
|
if (!strncmp(auth_hdr->challenge.digest.algorithm.ptr, "AKAv2-MD5", 9)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Authentication algorithm not supported. See third-party/pjproject/source/pjsip/src/pjsip/sip_auth_aka.c for implementation.\n");
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (!!strncmp(auth_hdr->challenge.digest.algorithm.ptr, "AKAv1-MD5", 9)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Authentication algorithm not supported.\n");
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
|
#warning hacking
|
|
|
|
|
const uint8_t opc[16] = { 0x77, 0x5A, 0x1F, 0x88, 0x7D, 0x2A, 0xD6, 0x6F, 0x97, 0x19, 0xC2, 0xC7, 0x9F, 0x84, 0x7B, 0x50 };
|
|
|
|
|
const uint8_t ki[16] = { 0xD5, 0x34, 0xE0, 0x78, 0x54, 0xB7, 0x5E, 0x47, 0x5C, 0x66, 0x7A, 0x85, 0x6A, 0xA3, 0x1F, 0x9C };
|
|
|
|
|
const uint8_t amf[2] = { 0x80, 0x00 };
|
|
|
|
|
const uint8_t sqn[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
uint8_t rand_auth[32], *rand = rand_auth, *auth = rand_auth + 16;
|
|
|
|
|
uint8_t out_autn[16], out_ik[16], out_ck[16];
|
|
|
|
|
size_t out_res_len = 8;
|
|
|
|
|
ast_base64decode(rand_auth, auth_hdr->challenge.digest.nonce.ptr, sizeof(rand_auth));
|
|
|
|
|
hexdump(LOG_DEBUG, "nonce", rand_auth, 32);
|
|
|
|
|
|
|
|
|
|
milenage_generate(opc, amf, ki, sqn, rand, out_autn, out_ik, out_ck, aka_res, &out_res_len);
|
|
|
|
|
if (out_res_len != 8) {
|
|
|
|
|
ast_log(LOG_ERROR, "Milenage computation failed.\n");
|
|
|
|
|
}
|
|
|
|
|
hexdump(LOG_DEBUG, "IK", out_ik, 16);
|
|
|
|
|
hexdump(LOG_DEBUG, "CK", out_ck, 16);
|
|
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR, "Setting RES as paassword.\n");
|
|
|
|
|
volte_auth = aka_res;
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -408,18 +475,14 @@ static int load_module(void)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return AST_MODULE_LOAD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int unload_module(void)
|
|
|
|
|
{
|
|
|
|
|
ast_sip_unregister_service(&volte_module);
|
|
|
|
|
xfrm_exit_mnl_socket(g_mnl_socket);
|
|
|
|
|
|
|
|
|
|
cleanup_xfrm();
|
|
|
|
|
xfrm_exit_mnl_socket(g_mnl_socket);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|