diff --git a/stir-shaken/08.verify-error-403-wrong-date/README.md b/stir-shaken/08.A.verify-error-403-wrong-date-past/README.md similarity index 100% rename from stir-shaken/08.verify-error-403-wrong-date/README.md rename to stir-shaken/08.A.verify-error-403-wrong-date-past/README.md diff --git a/stir-shaken/08.verify-error-403-wrong-date/opensips.cfg b/stir-shaken/08.A.verify-error-403-wrong-date-past/opensips.cfg similarity index 100% rename from stir-shaken/08.verify-error-403-wrong-date/opensips.cfg rename to stir-shaken/08.A.verify-error-403-wrong-date-past/opensips.cfg diff --git a/stir-shaken/08.verify-error-403-wrong-date/scenario.yml b/stir-shaken/08.A.verify-error-403-wrong-date-past/scenario.yml similarity index 100% rename from stir-shaken/08.verify-error-403-wrong-date/scenario.yml rename to stir-shaken/08.A.verify-error-403-wrong-date-past/scenario.yml diff --git a/stir-shaken/08.verify-error-403-wrong-date/scripts/uac.xml b/stir-shaken/08.A.verify-error-403-wrong-date-past/scripts/uac.xml similarity index 100% rename from stir-shaken/08.verify-error-403-wrong-date/scripts/uac.xml rename to stir-shaken/08.A.verify-error-403-wrong-date-past/scripts/uac.xml diff --git a/stir-shaken/08.verify-error-403-wrong-date/stir-shaken-ca/ca-cert.pem b/stir-shaken/08.A.verify-error-403-wrong-date-past/stir-shaken-ca/ca-cert.pem similarity index 100% rename from stir-shaken/08.verify-error-403-wrong-date/stir-shaken-ca/ca-cert.pem rename to stir-shaken/08.A.verify-error-403-wrong-date-past/stir-shaken-ca/ca-cert.pem diff --git a/stir-shaken/08.verify-error-403-wrong-date/stir_shaken_verify.cfg b/stir-shaken/08.A.verify-error-403-wrong-date-past/stir_shaken_verify.cfg similarity index 100% rename from stir-shaken/08.verify-error-403-wrong-date/stir_shaken_verify.cfg rename to stir-shaken/08.A.verify-error-403-wrong-date-past/stir_shaken_verify.cfg diff --git a/stir-shaken/08.B.verify-error-403-wrong-date-future/README.md b/stir-shaken/08.B.verify-error-403-wrong-date-future/README.md new file mode 100644 index 0000000..403ad16 --- /dev/null +++ b/stir-shaken/08.B.verify-error-403-wrong-date-future/README.md @@ -0,0 +1,12 @@ +# Diagram +```mermaid +sequenceDiagram + uac-sipp-stir-shaken->>+opensips: With identity header + opensips-->>-uac-sipp-stir-shaken: 403 Stale Date +``` + +# Explanations: +We forced future Date header in UAC +```php +Date: Tue, 22 Sep 2150 23:29:00 GMT +``` diff --git a/stir-shaken/08.B.verify-error-403-wrong-date-future/opensips.cfg b/stir-shaken/08.B.verify-error-403-wrong-date-future/opensips.cfg new file mode 100644 index 0000000..9fecde8 --- /dev/null +++ b/stir-shaken/08.B.verify-error-403-wrong-date-future/opensips.cfg @@ -0,0 +1,159 @@ +# +# OpenSIPS residential configuration script +# by OpenSIPS Solutions +# +# This script was generated via "make menuconfig", from +# the "Residential" scenario. +# You can enable / disable more features / functionalities by +# re-generating the scenario with different options.# +# +# Please refer to the Core CookBook at: +# https://opensips.org/Resources/DocsCookbooks +# for a explanation of possible statements, functions and parameters. +# + + +####### Global Parameters ######### +###################################################################### +/* uncomment the following lines to enable debugging */ +#debug_mode=yes + +log_level=4 +xlog_level=4 +log_stderror=yes + +udp_workers=4 + +####### Modules Section ######## + +#set module path +mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/" + +#### SIGNALING module +loadmodule "signaling.so" + +#### StateLess module +loadmodule "sl.so" + +#### Transaction Module +loadmodule "tm.so" +modparam("tm", "fr_timeout", 5) +modparam("tm", "fr_inv_timeout", 30) +modparam("tm", "restart_fr_on_each_reply", 0) +modparam("tm", "onreply_avp_mode", 1) + +#### SIP MSG OPerationS module +loadmodule "sipmsgops.so" + +#### MySQL module +#loadmodule "db_mysql.so" + +#### Dialog module +loadmodule "dialog.so" +#modparam("dialog", "db_mode", 2) +#modparam("dialog", "db_update_period", 2) +#modparam("dialog", "db_url", "mysql://root@192.168.52.2/opensips") + +#### MAX ForWarD module +loadmodule "maxfwd.so" + +#### Record Route Module +loadmodule "rr.so" +/* do not append from tag to the RR (no need for this script) */ +modparam("rr", "append_fromtag", 0) + +loadmodule "proto_udp.so" + +loadmodule "httpd.so" +loadmodule "mi_http.so" + +#### Stir and Shaken +loadmodule "stir_shaken.so" +modparam("stir_shaken", "ca_list", "/etc/opensips/stir-shaken-ca/ca-cert.pem") +modparam("stir_shaken", "require_date_hdr", 0) +modparam("stir_shaken", "verify_date_freshness", 300) # => please change to 60 for French reglementation + + +include_file "stir_shaken_verify.cfg" + + +####### Routing Logic ######## + +# main request routing logic + +route { + + $var(cert) = "-----BEGIN CERTIFICATE----- +MIIByzCCAXGgAwIBAgIUWfW2wiP6QMbm7OlahCyplooFTl0wCgYIKoZIzj0EAwIw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA1MDkwOTE2NThaFw0yNTA4MTEw +OTE2NThaMGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTESMBAGA1UEBwwJU29t +ZXdoZXJlMRowGAYDVQQKDBFBY21lVGVsZWNvbSwgSW5jLjENMAsGA1UECwwEVk9J +UDEPMA0GA1UEAwwGU0hBS0VOMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuyQP +0hteN1oKDUxo/2zvTp+0ppJ2IntNSdu36QFsUPDsCWlr4iTUMsjPtD+XQ58xQEf6 +n/zTE9cwZhs46NJWdKMaMBgwFgYIKwYBBQUHARoECjAIoAYWBDEwMDEwCgYIKoZI +zj0EAwIDSAAwRQIga2buNdRtI/56SZ0pBOUd21UxVNacFelmTpnda145zYICIQDz +yWoJxs18OGdJL0sfcw2JKiWQ9i6AKQAgGh31oKxXHg== +-----END CERTIFICATE-----"; + + if (!mf_process_maxfwd_header(10)) { + send_reply(483,"Too Many Hops"); + exit; + } + + if (has_totag()) { + + # handle hop-by-hop ACK (no routing required) + if (is_method("ACK") && t_check_trans()) { + t_relay(); + exit; + } + + # sequential request within a dialog should + # take the path determined by record-routing + if (!loose_route() && !match_dialog()) { + # we do record-routing for all our traffic, so we should not + # receive any sequential requests without Route hdr. + send_reply(404,"Not here"); + exit; + } + + # route it out to whatever destination was set by loose_route() + # in $du (destination URI). + + t_relay(); + exit; + } + + # CANCEL processing + if (is_method("CANCEL")) { + if (t_check_trans()) + t_relay(); + exit; + } + + # accept just INVITE requests + if (!is_method("INVITE")) { + send_reply(503, "Service Unavailable"); + exit; + } + else + { + $var(kill_calls) = true; + route(stir_shaken_verify); + } + + if (!create_dialog()) { + send_reply(500, "Internal Server Error"); + exit; + } + record_route(); + + if (!t_relay()) + send_reply(500, "Internal Error"); + exit; + + + + +} diff --git a/stir-shaken/08.B.verify-error-403-wrong-date-future/scenario.yml b/stir-shaken/08.B.verify-error-403-wrong-date-future/scenario.yml new file mode 100644 index 0000000..ac6cb20 --- /dev/null +++ b/stir-shaken/08.B.verify-error-403-wrong-date-future/scenario.yml @@ -0,0 +1,23 @@ +--- +# generate CA: https://blog.opensips.org/2022/10/31/how-to-generate-self-signed-stir-shaken-certificates/ + +timeout: 30 + +tasks: + - name: OpenSIPS + type: opensips + + - name: SIPP UAC + type: uac-sipp-stir-shaken + service: "+33987654321" + config_file: scripts/uac.xml + remote: {{ uas_ip }}:{{ uas_port }} + caller: "+33612345678" + duration: 10000 + stir_shaken_origid: "toto" + stir_shaken_private_key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIIOvgr23lbJ5rIOhiF+LR/VU4piEc1EYLT1CF5SN5HtZoAoGCCqGSM49 + AwEHoUQDQgAEuyQP0hteN1oKDUxo/2zvTp+0ppJ2IntNSdu36QFsUPDsCWlr4iTU + MsjPtD+XQ58xQEf6n/zTE9cwZhs46NJWdA== + -----END EC PRIVATE KEY----- diff --git a/stir-shaken/08.B.verify-error-403-wrong-date-future/scripts/uac.xml b/stir-shaken/08.B.verify-error-403-wrong-date-future/scripts/uac.xml new file mode 100644 index 0000000..fecc4fc --- /dev/null +++ b/stir-shaken/08.B.verify-error-403-wrong-date-future/scripts/uac.xml @@ -0,0 +1,68 @@ + + + + + + + + + + ;tag=[call_number] + To: + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: + P-Asserted-Identity: + Date: Tue, 22 Sep 2150 23:29:00 GMT + Identity: [stir_and_shaken_jwt];info=<[stir_and_shaken_info]>;alg=[stir_shaken_alg];ppt=[stir_shaken_ppt] + 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 + + ]]> + + + + + + + + ;tag=[call_number] + To: [peer_tag_param] + [routes] + CSeq: 1 ACK + Contact: + Call-ID: [call_id] + Max-Forwards: 70 + Subject: Performance Test + User-Agent: sipp + Content-Length: 0 + + ]]> + + + + + + + + + + + diff --git a/stir-shaken/08.B.verify-error-403-wrong-date-future/stir-shaken-ca/ca-cert.pem b/stir-shaken/08.B.verify-error-403-wrong-date-future/stir-shaken-ca/ca-cert.pem new file mode 100644 index 0000000..f7dc2e7 --- /dev/null +++ b/stir-shaken/08.B.verify-error-403-wrong-date-future/stir-shaken-ca/ca-cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB4DCCAYWgAwIBAgIUXwIAhKkOWfmttuAk6B+Tg/cgBoowCgYIKoZIzj0EAwIw +RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA1MDkwOTE0MzRaFw0yODA1MDcw +OTE0MzRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAARcBrtyH0D+amkf6dPsRmfWXEYWG2ISHdnemjvc+U1+vN7sxtN5Z++v +BIU+1IYOnoRmbq0s4WAdYO+KkqF/0PH8o1MwUTAdBgNVHQ4EFgQUzji/c4nCqtOS +vhnfZ1AMxQj0qccwHwYDVR0jBBgwFoAUzji/c4nCqtOSvhnfZ1AMxQj0qccwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEA6ec6TnJcajbc7iGyDqCn +n0/b4w9Wxqf5NYFiehkDSgQCIQCPp4lKBWp3dTqxhxccFwgC1yth5Tb705YlRY9/ +5HwgKA== +-----END CERTIFICATE----- diff --git a/stir-shaken/08.B.verify-error-403-wrong-date-future/stir_shaken_verify.cfg b/stir-shaken/08.B.verify-error-403-wrong-date-future/stir_shaken_verify.cfg new file mode 100644 index 0000000..7bf16cf --- /dev/null +++ b/stir-shaken/08.B.verify-error-403-wrong-date-future/stir_shaken_verify.cfg @@ -0,0 +1,191 @@ +########################################################## +# Stir and shaken verify for french regulation # +# Create By Mickael HUBERT https://github.com/mickaelh51 # +########################################################## + +route[stir_and_shaken_create_err_code] +{ + xlog("L_INFO", "MAN all identity: $identity(header) / $identity(payload) / $identity(iat) / $identity(attest)\n"); + + if ($var(identity_param_count)>4) + { + xlog("L_ERR", "MAN $var(identity_param_count) > 4\n"); + xlog("L_ERR", "MAN section 5.5.2.3 -> 438\n"); + $var(stir_shaken_verify_rc) = -4; + + } + + if ($identity(attest) !~ 'A|B|C') + { + xlog("L_ERR", "MAN attest section 5.5.2.4 -> 438\n"); + $var(stir_shaken_verify_rc) = -4; + } + + xlog("L_INFO", "MAN <$identity(x5u)> // $var(identity_info)\n"); + $var(new_identity_info) = "<" + $identity(x5u) + ">"; + if ($var(new_identity_info) != $var(identity_info)) + { + xlog("L_ERR", "MAN section 5.5.2.3 -> 436\n"); + $var(stir_shaken_verify_rc) = -100; + } + + if (!$var(identity_alg) || $var(identity_alg) != "ES256") + { + xlog("L_ERR", "MAN issue alg section 5.5.2.3-> 437\n"); + $var(stir_shaken_verify_rc) = -7; + } + + ######################################## + # Please change XX256 to ES256 in prod # + ######################################## + # Other method (but official opensips docker image doesn't contain json module) + #$json(identity_header) := $identity(header); + #if (!$json(identity_header/alg) || $json(identity_header/alg) != 'ES256') + if ($identity(header) !~ 'ES256') + { + xlog("L_ERR", "MAN issue alg not ES256 header section 5.5.2.4 -> 436\n"); + $var(stir_shaken_verify_rc) = -7; + } + + # Other method (but official opensips docker image doesn't contain json module) + #$json(identity_header) := $identity(header); + #if (!$json(identity_header/ppt) || $json(identity_header/ppt) != 'passport') + if ($identity(header) !~ 'passport') + { + xlog("L_ERR", "MAN issue typ not passport header section 5.5.2.4 -> 437\n"); + $var(stir_shaken_verify_rc) = -7; + } + + if ($identity(header) !~ 'alg' || $identity(header) !~ 'ppt' || $identity(header) !~ 'typ' || $identity(header) !~ 'x5u') + { + xlog("L_ERR", "MAN issue no alg or ppt or typ or x5u in token header section 5.5.2.4 -> 436\n"); + $var(stir_shaken_verify_rc) = -100; + } + + if (!$var(identity_ppt) || $var(identity_ppt) != "shaken") + { + xlog("L_ERR", "MAN issue ppt section 5.5.2.3-> 438\n"); + $var(stir_shaken_verify_rc) = -4; + } +} + +route[stir_shaken_verify] +{ + xlog("L_INFO", "MAN $identity\n"); + + if ($fU == "anonymous" || $fU == "unavailable") { $var(orig) = $(ai{uri.user}); } else { $var(orig) = $fU; } + stir_shaken_verify($var(cert), $var(err_code), $var(err_reason), "$var(orig)", "$tU"); + + $var(stir_shaken_verify_rc) = $rc; + + if (is_present_hf("Identity")) + { + $var(identity_info) = $(hdr(Identity)[0]{param.value,info}); + $var(identity_alg) = $(hdr(Identity)[0]{param.value,alg}); + $var(identity_ppt) = $(hdr(Identity)[0]{param.value,ppt}); + $var(identity_param_count) = $(hdr(Identity)[0]{param.count}); + xlog("L_INFO", "MAN $var(identity_info) / $var(identity_alg) / $var(identity_ppt)\n"); + + route(stir_and_shaken_create_err_code); + remove_hf("Identity"); + } + + if ($var(stir_shaken_verify_rc) < -1) { + xlog("L_ERR", "verify rc MAN: $var(stir_shaken_verify_rc)\n"); + xlog("L_ERR", "MAN: default err_code=$var(err_code) err_reason=$var(err_reason)\n"); + + + switch($var(stir_shaken_verify_rc)) + { + #MAN_Mode_operatoire_Mecanisme_de_Confiance_v1.7_20230616.pdf (page 59) + case -1: + # Internal error ==> default 500 + xlog("L_ERR", "Internal error ==> default 500\n"); + break; + case -2: + # No Identity or Date header found ==> default 428 Use Identity Header + #MAN No identity header -> 428 Use Identity Header + xlog("L_ERR", "No Identity or Date header found ==> default 428 Use Identity Header\n"); + #$var(err_code) = 428; + #$var(err_reason) = "Use Identity Header"; + break; + case -3: + #MAN From and To malformed -> 400 Bad Request ==> default 500 Internal Server Error + xlog("L_ERR", "-3 default 500, rewrite to 400\n"); + $var(err_code) = 400; + $var(err_reason) = "Bad Request"; + break; + case -4: + # Invalid identity header ==> default 438 invalid Identity Header + xlog("L_ERR", "Invalid identity header ==> default 438 invalid Identity Header\n"); + $var(err_code) = 438; + $var(err_reason) = "Invalid Identity Header"; + break; + case -5: + # Unsupported 'ppt' or 'alg' Identity header parameter ==> default 438 invalid Identity Header + xlog("L_ERR", "Unsupported 'ppt' or 'alg' Identity header parameter ==> default 438 invalid Identity Header\n"); + break; + case -6: + #MAN Date or iat > 60s -> 403 Stale Date + #Date header value is older than local policy for freshness ==> default 403 Stale Date + xlog("L_ERR", "MAN Date or iat > 60s -> 403 Stale Date\n"); + #$var(err_code) = 403; + #$var(err_reason) = "Stale Date"; + break; + case -7: + #The Date header value does not fall within the certificate validity ==> default 403 Stale Date + xlog("L_ERR", "The Date header value does not fall within the certificate validity ==> default 403 Stale Date\n"); + $var(err_code) = 437; + $var(err_reason) = "Unsupported Credential"; + break; + case -8: + # Invalid certificate ==> default 437 Unsupported Credential + xlog("L_ERR", "Invalid certificate ==> default 437 Unsupported Credential\n"); + break; + case -9: + # Signature does not verify successfully ==> default 438 invalid Identity Header + xlog("L_ERR", "Signature does not verify successfully ==> default 438 invalid Identity Header\n"); + break; + case -100: + # Add case, beacause sip eoor 436 doen't exist in stir and shaken module + xlog("L_ERR", "default 436 Bad Identity Info\n"); + $var(err_code) = 436; + $var(err_reason) = "Bad Identity Info"; + break; + default: + xlog("L_ERR", "no case -X, defaut 500\n"); + $var(err_code) = 500; + $var(err_reason) = "Internal Server Error"; + } + + xlog("MAN: final err_code=$var(err_code) err_reason=$var(err_reason)\n"); + if ($var(kill_calls)) + { + xlog("MAN: Kill call ! ($var(kill_calls))\n"); + send_reply($var(err_code), $var(err_reason)); + exit; + } + else + { + xlog("MAN: Not kill call ! ($var(kill_calls)) Add reason header in 200OK\n"); + # add variable into avp to be usable in onreply_route + $avp(err_code) = $var(err_code); + $avp(err_reason) = $var(err_reason); + t_on_reply("reply_stir_shaken"); + } + + } + else + { + xlog("L_INFO", "MAN: verification OK\n"); + } +} + +onreply_route[reply_stir_shaken] +{ + if(t_check_status("200")) + { + #Reason: SIP; cause=436; text="Bad Identity Info" + append_hf("Reason: SIP; cause=$avp(err_code); text=\"$avp(err_reason)\"\r\n"); + } +}