blob: a253c9f4eb89ce1931a50e6f98bda582134c9f80 [file] [log] [blame]
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +02001/* Component implementing a IMS server towards Asterisk's IMS UE
2 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11module IMS_ConnectionHandler {
12
Pau Espin Pedrol717379f2024-05-17 18:36:51 +020013import from TCCEncoding_Functions all;
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020014import from TCCOpenSecurity_Functions all;
15import from General_Types all;
16import from Osmocom_Types all;
17import from Native_Functions all;
18import from Misc_Helpers all;
19
Pau Espin Pedrola674d612024-05-14 19:56:33 +020020/* the PIPE asp port allows us to interact with ip xfrm via stdin/stdout */
21import from PIPEasp_PortType all;
22import from PIPEasp_Types all;
23import from PIPEasp_Templates all;
24
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020025import from SDP_Types all;
26import from SDP_Templates all;
27
28import from SIP_Emulation all;
29import from SIPmsg_Types all;
30import from SIP_Templates all;
31
Pau Espin Pedrola674d612024-05-14 19:56:33 +020032
33modulepar {
34 charstring mp_ipsec_setup_script_path := "./IMS_ipsec_setup.sh";
35}
36
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020037const char c_sip_server_name := "osmo-ttcn3-hacks/0.23";
38
39
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020040type port IMSCoord_PT message
41{
42 inout charstring;
43} with { extension "internal" };
44
45const charstring IMS_COORD_CMD_REGISTERED := "COORD_CMD_REGISTERED";
46
47type component IMS_ConnHdlr extends SIP_ConnHdlr {
48 var charstring g_name;
49 var IMS_ConnHdlrPars g_pars;
50 timer g_Tguard;
51 var PDU_SIP_Request g_rx_sip_req;
52 var PDU_SIP_Response g_rx_sip_resp;
53
54 port IMSCoord_PT COORD;
Pau Espin Pedrola674d612024-05-14 19:56:33 +020055 port PIPEasp_PT PIPE;
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020056}
57type record of IMS_ConnHdlr IMS_ConnHdlrList;
58
59type record IMS_ConnHdlrPars {
60 float t_guard,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020061 charstring realm,
62 charstring local_sip_host,
63 uint16_t local_sip_port,
64 charstring remote_sip_host optional,
65 uint16_t remote_sip_port optional,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020066 charstring user,
67 charstring display_name,
68 charstring password,
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +020069 charstring msisdn,
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020070 /* Expected User-Location-Info in P-Access-Network-Info */
71 charstring uli_str,
Pau Espin Pedrol717379f2024-05-17 18:36:51 +020072 octetstring rand,
73 octetstring autn,
Pau Espin Pedrola674d612024-05-14 19:56:33 +020074 charstring ipsec_auth_key,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020075 integer ipsec_local_spi_c,
76 integer ipsec_local_spi_s,
77 integer ipsec_remote_spi_c optional,
78 integer ipsec_remote_spi_s optional,
Pau Espin Pedrola674d612024-05-14 19:56:33 +020079 uint16_t ipsec_remote_port_c optional,
80 uint16_t ipsec_remote_port_s optional,
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +020081 SipUrl registrar_sip_req_uri,
82 SipAddr registrar_sip_record,
83 CallidString registrar_sip_call_id,
84 integer registrar_sip_seq_nr,
85 Via local_via,
86 SipUrl local_sip_url_ext,
87 SipAddr local_sip_record,
88 Contact local_contact,
89 IMS_CallPars cp optional
90}
91type record of IMS_ConnHdlrPars IMS_ConnHdlrParsList;
92
93type record IMS_CallParsMT {
94 /* Whether to wait for COORD.receive(COORD_CMD_PICKUP) before accepting the call. */
95 boolean wait_coord_cmd_pickup,
96 /* Whether to expect CANCEL instead of ACK as answer to our OK */
97 boolean exp_cancel
98}
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +020099template (value) IMS_CallParsMT t_IMS_CallParsMT := {
100 wait_coord_cmd_pickup := false,
101 exp_cancel := false
102}
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +0200103
104type record IMS_CallPars {
105 SipAddr calling optional,
106 SipAddr called optional,
107
108 SipAddr from_addr optional,
109 SipAddr to_addr optional,
110
111 CallidString sip_call_id,
112 integer sip_seq_nr,
113 charstring sip_body optional,
114
115 charstring local_rtp_addr,
116 uint16_t local_rtp_port,
117
118 SDP_Message peer_sdp optional,
119 IMS_CallParsMT mt
120}
121
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200122template (value) IMS_CallPars t_IMS_CallPars(charstring local_rtp_addr,
123 uint16_t local_rtp_port := 0,
124 template (omit) SipAddr calling := omit,
125 template (omit) SipAddr called := omit) := {
126 calling := calling,
127 called := called,
128 from_addr := omit,
129 to_addr := omit,
130 sip_call_id := hex2str(f_rnd_hexstring(15)),
131 sip_seq_nr := f_sip_rand_seq_nr(),
132 sip_body := omit,
133 local_rtp_addr := local_rtp_addr,
134 local_rtp_port := local_rtp_port,
135 peer_sdp := omit,
136 mt := t_IMS_CallParsMT
137}
138
139template (value) IMS_ConnHdlrPars t_IMS_Pars(charstring local_sip_host,
140 uint16_t local_sip_port,
141 charstring user,
142 charstring display_name := "Anonymous",
143 charstring password := "secret",
144 template (omit) IMS_CallPars cp := omit) := {
145 t_guard := 30.0,
146 realm := local_sip_host,
147 local_sip_host := local_sip_host,
148 local_sip_port := local_sip_port,
149 remote_sip_host := omit,
150 remote_sip_port := omit,
151 user := user,
152 display_name := f_sip_str_quote(display_name),
153 password := password,
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200154 msisdn := "90828",
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200155 uli_str := "2380100010000101",
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200156 /* The Nonce field is the Base64 encoded version of the RAND value and concatenated with the AUTN: */
Pau Espin Pedrol717379f2024-05-17 18:36:51 +0200157 rand := '14987631f65f8e3788a0798b6ebcd08e'O,
158 autn := 'f6e19a7ccb028000a06b19c9544516e5'O,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200159 ipsec_auth_key := "0x5238297dfcca759bd05d48ff49bc63fa00000000",
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200160 ipsec_local_spi_c := 4142,
161 ipsec_local_spi_s := 4143,
162 ipsec_remote_spi_c := omit,
163 ipsec_remote_spi_s := omit,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200164 ipsec_remote_port_c := omit,
165 ipsec_remote_port_s := omit,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200166 registrar_sip_req_uri := valueof(ts_SipUrlHost(local_sip_host)),
167 registrar_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
168 ts_UserInfo(user),
169 f_sip_str_quote(display_name)),
170 registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & local_sip_host,
171 registrar_sip_seq_nr := f_sip_rand_seq_nr(),
172 local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
173 local_sip_url_ext := ts_SipUrl(ts_HostPort(local_sip_host, local_sip_port),
174 ts_UserInfo(user)),
175 local_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
176 ts_UserInfo(user)),
177 local_contact := valueof(ts_Contact({
178 ts_ContactAddress(
179 ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
180 local_sip_host,
181 local_sip_port),
182 ts_UserInfo(user))),
183 omit)
184 })),
185 cp := cp
186}
187
188private altstep as_Tguard() runs on IMS_ConnHdlr {
189 [] g_Tguard.timeout {
190 setverdict(fail, "Tguard timeout");
191 mtc.stop;
192 }
193}
194
195type function ims_void_fn(charstring id) runs on IMS_ConnHdlr;
196function f_ims_handler_init(ims_void_fn fn, charstring id, IMS_ConnHdlrPars pars)
197runs on IMS_ConnHdlr {
198 g_name := id;
199 g_pars := pars;
200 g_Tguard.start(pars.t_guard);
201 activate(as_Tguard());
202
203 /* call the user-supied test case function */
204 fn.apply(id);
205}
206
207private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on IMS_ConnHdlr
208{
209 var PDU_SIP_Request sip_req;
210 [] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
211 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
212 log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
213 }
214}
215
216private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on IMS_ConnHdlr
217{
218 var PDU_SIP_Response sip_resp;
219 [] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
220 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
221 log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
222 }
223}
224
Pau Espin Pedrol717379f2024-05-17 18:36:51 +0200225private function f_nonce_from_rand_autn(octetstring rand, octetstring autn) return charstring {
226 var octetstring concat := rand & autn;
227 var charstring nonce := enc_MIME_Base64(concat);
228 log("rand=", rand, " & autn=",autn, " => nonce=", nonce);
229 return nonce;
230}
231
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200232/* HTTP Digest Authentication Using AKA (AKAv1-MD5): RFC 3310 */
233function f_tr_Authorization_AKAv1MD5(WwwAuthenticate www_authenticate,
234 charstring username,
235 charstring uri,
236 integer nc_int := 1)
237return template (present) Authorization {
238 var CommaParam_List digestCln;
239 var template (present) Authorization authorization;
240 var template (present) Credentials cred;
241 var template (omit) GenericParam rx_param;
242
243 digestCln := www_authenticate.challenge[0].digestCln;
244
245 var charstring algorithm := f_sip_param_get_value_present_or_fail(digestCln, "algorithm");
246 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
247 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
248
249 var template (present) CommaParam_List digestResponse := superset(
250 tr_Param("username", f_sip_str_quote(username)),
251 tr_Param("realm", f_sip_str_quote(realm)),
252 tr_Param("nonce", f_sip_str_quote(nonce)),
253 tr_Param("uri", f_sip_str_quote(uri)),
254 tr_Param("response", ?),
255 tr_Param("algorithm", algorithm),
256 tr_Param("qop", "auth"),
257 tr_Param("cnonce", ?),
258 tr_Param("nc", ?)
259 );
260 cred := tr_Credentials_DigestResponse(digestResponse);
261 authorization := tr_Authorization(cred);
262 return authorization;
263}
264
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200265private function f_ims_validate_register_contact(Contact rx_contact)
266{
267/* IMS contact shows up like this:
268 * Contact: <sip:8adf9f3d-9342-4060-aa4f-a909f37fd6f6@192.168.101.2:5060>;+g.3gpp.accesstype="cellular2";video;audio;+g.3gpp.smsip;+g.3gpp.nw-init-ussi;+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel";+sip.instance="<urn:gsma:imei:35589811-338445-0>"
269 */
270 /* TODO: "that the UE must include the IMS Communication Service Identifier (ICSI)
271in the contact: header to indicate IMS Multimedia Telephony." */
272 /* TODO: "The UE must include an IMEI URN in the +sip.instance header field
273parameter of the contact: header." */
274 /* TODO: "If the UE supports SMS over IP, it must include the feature tag
275“+g.3gpp.smsip” in the contact: header." */
276 /* TODO: "If the UE supports conversational audio and video service, then this must
277be indicated by adding a “video” media feature tag to the contact: header." */
278}
279
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200280/* Validate P-Access-Network-Info: RFC7315 6.4 */
281private function f_ims_validate_register_P_Access_Network_info(PDU_SIP_Request req,
282 boolean exp_present := true) runs on IMS_ConnHdlr
283
284{
285 if (not exp_present) {
286 if (ispresent(g_rx_sip_req.msgHeader.p_access_network_info)) {
287 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
288 log2str(g_name & ": Received unexpected [rfc7315 6.4] P-Access-Info := ",
289 g_rx_sip_req.msgHeader.p_access_network_info));
290 }
291 return;
292 }
293
294 /* exp_present: */
295 var template (present) P_Access_Network_Info expl_tmpl :=
296 tr_P_Access_Network_Info({ tr_Access_net_spec_EUTRAN(g_pars.uli_str) });
297
298 if (not ispresent(g_rx_sip_req.msgHeader.p_access_network_info)) {
299 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
300 log2str(g_name & ": Received no P-Access-Info vs exp := ",
301 expl_tmpl));
302 }
303 if (not match(g_rx_sip_req.msgHeader.p_access_network_info, expl_tmpl)) {
304 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
305 log2str(g_name & ": Received unexpected P-Access-Info := ",
306 g_rx_sip_req.msgHeader.p_access_network_info,
307 "\nvs exp := ", expl_tmpl));
308 }
309}
310
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200311private function f_ims_parse_security_client(Security_client security_client) runs on IMS_ConnHdlr
312{
313 var boolean found := false;
314 for (var integer i := 0; i < lengthof(security_client.sec_mechanism_list); i := i + 1) {
315 var Security_mechanism sec_mec := security_client.sec_mechanism_list[i];
316 if (sec_mec.mechanism_name != "ipsec-3gpp") {
317 log("Skipping Security Mechansim: ", sec_mec.mechanism_name);
318 continue;
319 }
320 var SemicolonParam_List sec_pars := sec_mec.mechanism_params;
321 var charstring par_val;
322 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "alg");
323 if (par_val != "hmac-sha-1-96") {
324 log("Skipping Security Mechansim Algo: ", par_val);
325 continue;
326 }
327 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "spi-c");
328 g_pars.ipsec_remote_spi_c := str2int(par_val);
329 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "spi-s");
330 g_pars.ipsec_remote_spi_s := str2int(par_val);
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200331 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "port-c");
332 g_pars.ipsec_remote_port_c := str2int(par_val);
333 par_val := f_sip_param_get_value_present_or_fail(sec_pars, "port-s");
334 g_pars.ipsec_remote_port_s := str2int(par_val);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200335 found := true;
336 break;
337 }
338
339 if (not found) {
340 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
341 log2str(g_name & "alg=hmac-sha-1-96 not found: ", security_client));
342 }
343
344 log("ipsec: remote_spi_c=", g_pars.ipsec_remote_spi_c, " remote_spi_s=", g_pars.ipsec_remote_spi_s,
345 "local_spi_c=", g_pars.ipsec_local_spi_c, " local_spi_s=", g_pars.ipsec_local_spi_s);
346}
347
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200348private function f_IMS_exec_sync(charstring cmdline, template (present) integer rc := 0)
349 runs on IMS_ConnHdlr return ASP_PResult {
350 var ASP_PResult res;
351
352 map(self:PIPE, system:PIPE);
353 res := f_PIPEasp_exec_sync_PResult(PIPE, cmdline, tr_PResult(?, ?, rc));
354 unmap(self:PIPE, system:PIPE);
355
356 return res;
357}
358
359private function f_ims_setup_ipsec() runs on IMS_ConnHdlr
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200360{
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200361 var ASP_PResult res;
362
363 var charstring cmd := mp_ipsec_setup_script_path & " " &
364 g_pars.local_sip_host & " " &
365 int2str(g_pars.local_sip_port) & " " & int2str(g_pars.ipsec_local_spi_c) & " " &
366 int2str(g_pars.local_sip_port) & " " & int2str(g_pars.ipsec_local_spi_s) & " " &
367 g_pars.remote_sip_host & " " &
368 int2str(g_pars.ipsec_remote_port_c) & " " & int2str(g_pars.ipsec_remote_spi_c) & " " &
369 int2str(g_pars.ipsec_remote_port_s) & " " & int2str(g_pars.ipsec_remote_spi_s) & " " &
370 g_pars.ipsec_auth_key;
371
372 res := f_IMS_exec_sync(cmd);
373
374 /* Debug applied rules: */
375 /*
376 res := f_IMS_exec_sync("ip xfrm state");
377 log("ip-xfrm-state Result-Stdout: " & res.stdout);
378
379 res := f_IMS_exec_sync("ip xfrm policy");
380 log("ip-xfrm-policy Result-Stdout: " & res.stdout);
381 */
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200382}
383
384/* Peer is calling us, accept it: */
385altstep as_IMS_register(boolean exp_update_to_direct_rtp := true,
386 boolean fail_others := true) runs on IMS_ConnHdlr
387{
388 var template (present) PDU_SIP_Request exp_req :=
389 tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
390 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200391 tr_From(),
392 tr_To(),
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200393 tr_Via_from(?),
394 require := tr_Require(superset("sec-agree")),
395 security_client := tr_Security_client(superset(tr_Security_mechanism("ipsec-3gpp",
396 superset(tr_Param("alg","hmac-sha-1-96"))))),
397 supported := tr_Supported(superset("path", "sec-agree")));
398 var charstring sip_expect_str := log2str(exp_req);
399
400 [] SIP.receive(exp_req) -> value g_rx_sip_req {
401 var template (value) PDU_SIP_Response tx_resp;
402 var Via via;
403 var CallidString sip_call_id;
404 var Contact contact;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200405 var template (value) From from_addr;
406 var template (value) To to_addr;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200407 var template (value) CommaParam_List digestCln ;
408 var template (value) WwwAuthenticate wwwAuthenticate;
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200409 var template (value) P_Associated_Uri p_associated_uri := ts_P_Associated_Uri({});
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200410 var template (value) Security_server security_server;
411 var template (value) Server server_name := ts_Server({c_sip_server_name});
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200412 var template (value) Require require := ts_Require({"sec-agree"});
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200413 var template (value) Supported supported := ts_Supported({"sec-agree"});
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200414 var template (present) Authorization authorization;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200415 var integer sip_seq_nr;
416 var charstring tx_sdp;
417
418 sip_call_id := g_rx_sip_req.msgHeader.callId.callid;
419 via := g_rx_sip_req.msgHeader.via;
420 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "rport", "1234"); /* TODO: set remote src port of the REGISTER */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200421 from_addr := g_rx_sip_req.msgHeader.fromField;
422 to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200423 sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
424
425 contact := g_rx_sip_req.msgHeader.contact;
426 f_ims_validate_register_contact(contact);
427
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200428 /* Validate P-Access-Network-Info: rfc7315 6.4:
429 * "3GPP will use the P-Access-Network-Info header field to
430 * carry relatively sensitive information like the cell ID. Therefore,
431 * the information MUST NOT be sent outside of the 3GPP domain.""
432 * [...] "the sensitive information carried in the
433 * P-Access-Network-Info header field MUST NOT be sent in any initial
434 * unauthenticated and unprotected requests (e.g., REGISTER)."
435 */
436 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := false);
437
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200438 /* TODO: Validate "Expires" is 600000 */
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200439
440 /* Tx 100 Tyring */
441 tx_resp := ts_SIP_Response_Trying(sip_call_id,
442 from_addr,
443 to_addr,
444 via,
445 sip_seq_nr,
446 "REGISTER",
447 allow := omit,
448 server := server_name,
449 userAgent := omit);
450 SIP.send(tx_resp);
451
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200452 g_pars.remote_sip_host := valueof(contact.contactBody.contactAddresses[0].addressField.nameAddr.addrSpec.hostPort.host);
453 f_ims_parse_security_client(g_rx_sip_req.msgHeader.security_client);
454 f_ims_setup_ipsec();
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200455
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200456 to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag());
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200457
458 digestCln := {
459 ts_Param("realm", f_sip_str_quote(g_pars.realm)),
460 ts_Param("qop", f_sip_str_quote("auth")),
461 ts_Param("algorithm", "AKAv1-MD5"),
Pau Espin Pedrol717379f2024-05-17 18:36:51 +0200462 ts_Param("nonce", f_sip_str_quote(f_nonce_from_rand_autn(g_pars.rand, g_pars.autn)))
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200463 /* "opaque not needed in IMS "*/
464 };
465 wwwAuthenticate := ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
466
467 /* Security-Server: ipsec-3gpp;q=0.1;prot=esp;mod=trans;spi-c=4096;spi-s=4097;port-c=5104;port-s=6104;alg=hmac-sha-1-96;ealg=null */
468 var template (value) SemicolonParam_List sec_params := {
469 ts_Param("q", "0.1"),
470 ts_Param("prot", "esp"),
471 ts_Param("mod", "trans"),
472 ts_Param("spi-c", int2str(g_pars.ipsec_local_spi_c)),
473 ts_Param("spi-s", int2str(g_pars.ipsec_local_spi_s)),
474 ts_Param("port-c", int2str(g_pars.local_sip_port)),
475 ts_Param("port-s", int2str(g_pars.local_sip_port)),
476 ts_Param("alg", "hmac-sha-1-96"),
477 ts_Param("ealg", "null")
478 };
479 security_server := ts_Security_server({
480 ts_Security_mechanism("ipsec-3gpp", sec_params)
481 });
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200482
483 /* Tx 401 Unauthorized */
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200484 tx_resp := ts_SIP_Response_Unauthorized(sip_call_id,
485 from_addr,
486 to_addr,
487 via,
488 wwwAuthenticate,
489 sip_seq_nr,
490 "REGISTER",
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200491 p_associated_uri := p_associated_uri,
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200492 security_server := security_server,
493 server := server_name,
494 supported := supported,
495 userAgent := omit);
496 SIP.send(tx_resp);
497
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200498 /* Now we should receive a new REGISTER over ipsec: */
499
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200500 /* TODO: Generate expected Authoritzation based on AKAv1-MD5: */
501 /*authorization := f_sip_digest_gen_Authorization(valueof(wwwAuthenticate),
502 g_pars.user, g_pars.password,
503 "REGISTER",
504 f_sip_SipUrl_to_str(g_pars.registrar_sip_record.addr.nameAddr.addrSpec))
505 */
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200506 authorization := f_tr_Authorization_AKAv1MD5(valueof(wwwAuthenticate),
507 g_pars.user & "@" & g_pars.realm,
Pau Espin Pedrolb0dbf7a2024-05-22 18:12:15 +0200508 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri));
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200509 /* TODO: match Authorization from above: */
510 exp_req :=
511 tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
512 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200513 tr_From(),
514 tr_To(),
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200515 tr_Via_from(?),
516 authorization := authorization);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200517 SIP.receive(exp_req) -> value g_rx_sip_req;
518
519 sip_call_id := g_rx_sip_req.msgHeader.callId.callid;
520 via := g_rx_sip_req.msgHeader.via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200521 from_addr := g_rx_sip_req.msgHeader.fromField;
522 to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200523 sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
524
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200525 /* Tx 100 Trying */
526 tx_resp := ts_SIP_Response_Trying(sip_call_id,
527 from_addr,
528 to_addr,
529 via,
530 sip_seq_nr,
531 "REGISTER",
532 allow := omit,
533 server := server_name,
534 userAgent := omit);
535 SIP.send(tx_resp);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200536
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200537 /* Validate P-Access-Network-Info: */
538 f_ims_validate_register_P_Access_Network_info(g_rx_sip_req, exp_present := true);
539
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200540 p_associated_uri := ts_P_Associated_Uri({
541 ts_P_Assoc_uri_spec(ts_NameAddr(ts_SipUrl(ts_HostPort(g_pars.realm),
542 ts_UserInfo(g_pars.msisdn),
543 scheme := "sip"))),
544 ts_P_Assoc_uri_spec(ts_NameAddr(ts_SipUrl(ts_HostPort(g_pars.msisdn),
545 omit,
546 scheme := "tel"))),
547 ts_P_Assoc_uri_spec(g_rx_sip_req.msgHeader.toField.addressField.nameAddr)
548 });
549
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200550 /* Tx 200 OK */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200551 to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag());
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200552 tx_resp := ts_SIP_Response(sip_call_id,
553 from_addr,
554 to_addr,
555 "REGISTER", 200,
556 sip_seq_nr,
557 "OK",
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200558 via,
Pau Espin Pedrol4e6672c2024-05-22 17:03:53 +0200559 p_associated_uri := p_associated_uri,
Pau Espin Pedrola674d612024-05-14 19:56:33 +0200560 require := require,
561 server := server_name,
562 supported := supported,
563 userAgent := omit);
Pau Espin Pedrola2812ec2024-05-10 20:30:44 +0200564 SIP.send(tx_resp);
565 }
566 [fail_others] as_SIP_fail_resp(sip_expect_str);
567 [fail_others] as_SIP_fail_req(sip_expect_str);
568
569}
570
Pau Espin Pedrolac8a0542024-04-19 17:30:57 +0200571}