<!-- Module User's Guide -->

<chapter>

	<title>&adminguide;</title>

	<section id="overview" xreflabel="Overview">
	<title>Overview</title>
	<para>This module adds support for implementing STIR/SHAKEN (RFC 8224, RFC 8588)
	Authentication and Verification services in &osips;.</para>
    </section>
    <section id="dependencies" xreflabel="Dependencies">
	<title>Dependencies</title>
	<section>
	    <title>&osips; Modules</title>
	    <para>
		The following modules must be loaded before this module:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>No dependencies on other &osips; modules</emphasis>.
			</para>
			</listitem>
			</itemizedlist>
		</para>
	</section>
	<section>
		<title>External Libraries or Applications</title>
		<para>
		The following libraries or applications must be installed before running
		&osips; with this module loaded:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>openssl (libssl)</emphasis>.
			</para>
			</listitem>
			</itemizedlist>
		</para>
	</section>
    </section>
    <section id="exported_parameters" xreflabel="Exported Parameters">
	<title>Exported Parameters</title>

	<section id="param_auth_date_freshness" xreflabel="auth_date_freshness">
	    <title><varname>auth_date_freshness</varname> (integer)</title>
	    <para>
		The maximum number of seconds that the value in the Date header field
		can be older than the current time.
		</para>
		<para>
		This parameter is only relevant
		for the <xref linkend="func_stir_shaken_auth"/> function.
		</para>
	    <para>The default value is <emphasis>60</emphasis>.</para>
	    <example>
		<title>Set <varname>auth_date_freshness</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "auth_date_freshness", 300)
...
</programlisting>
	    </example>
	</section>

	<section id="param_verify_date_freshness" xreflabel="verify_date_freshness">
	    <title><varname>verify_date_freshness</varname> (integer)</title>
	    <para>
		The maximum number of seconds that the value in the Date header field can be
		older than the current time. Also, if the <emphasis>iat</emphasis> value in
		the PASSporT is different than the Date value, but remains within the
		permitted interval, it will be used in the verification process (for the
		reconstructed PASSporT) instead of the Date value.
		</para>
		<para>
		If the <xref linkend="param_require_date_hdr"/> parameter is set to not
		required and the Date header is missing, the <emphasis>iat</emphasis> value
		will be used for this check instead.
		</para>
		<para>
		This parameter is only relevant for the
		<xref linkend="func_stir_shaken_verify"/> function.
		</para>
	    <para>The default value is <emphasis>60</emphasis>.</para>
	    <example>
		<title>Set <varname>verify_date_freshness</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "verify_date_freshness", 300)
...
</programlisting>
	    </example>
	</section>

	<section id="param_ca_list" xreflabel="ca_list">
	    <title><varname>ca_list</varname> (string)</title>
	    <para>
		Path to a file containing trusted CA certificates for the verifier.
		The certificates must be in PEM format, one after another.
	    </para>
	    <example>
		<title>Set <varname>ca_list</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "ca_list", "/stir_certs/ca_list.pem")
...
</programlisting>
	    </example>
	</section>

	<section id="param_ca_dir" xreflabel="ca_dir">
	    <title><varname>ca_dir</varname> (string)</title>
	    <para>
		Path to a directory containing trusted CA certificates for the verifier.
		The certificates in the directory must be in hashed form, as described
		in the <ulink url="https://www.openssl.org/docs/manmaster/man3/X509_LOOKUP_hash_dir.html">
		<citetitle>openssl documentation</citetitle></ulink> for the
		<emphasis>Hashed Directory Method</emphasis>.
	    </para>
	    <example>
		<title>Set <varname>ca_dir</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "ca_dir", "/stir_certs/cas")
...
</programlisting>
	    </example>
	</section>

	<section id="param_crl_list" xreflabel="crl_list">
	    <title><varname>crl_list</varname> (string)</title>
	    <para>
		Path to a file containing certificate revocation lists (CRLs) for the verifier.
	    </para>
	    <example>
		<title>Set <varname>crl_list</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "crl_list", "/stir_certs/crl_list.pem")
...
</programlisting>
	    </example>
	</section>

	<section id="param_crl_dir" xreflabel="crl_dir">
	    <title><varname>crl_dir</varname> (string)</title>
	    <para>
		Path to a directory containing certificate revocation lists (CRLs) for
		the verifier. The CRLs in the directory must be in hashed form, as described
		in the <ulink url="https://www.openssl.org/docs/manmaster/man3/X509_LOOKUP_hash_dir.html">
		<citetitle>openssl documentation</citetitle></ulink> for the
		<emphasis>Hashed Directory Method</emphasis>.
	    </para>
	    <example>
		<title>Set <varname>crl_dir</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "crl_dir", "/stir_certs/crls")
...
</programlisting>
	    </example>
	</section>

	<section id="param_e164_strict_mode" xreflabel="e164_strict_mode">
	    <title><varname>e164_strict_mode</varname> (integer)</title>
	    <para>
		Require a leading <emphasis>"+"</emphasis> to be present in
		the originating/destination SHAKEN identity, on top of mandating an E.164
		telephone number by default.  Additionally, require the URI to be either
		a <emphasis>tel</emphasis> URI or a <emphasis>sip</emphasis> /
		<emphasis>sips</emphasis> URI with the <emphasis>user=phone</emphasis>
		parameter.
	    </para>
	    <para>The default value is <emphasis>0</emphasis> (disabled).</para>
	    <example>
		<title>Set <varname>e164_strict_mode</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "e164_strict_mode", 1)
...
</programlisting>
	    </example>
	</section>

	<section id="param_e164_max_length" xreflabel="e164_max_length">
	    <title><varname>e164_max_length</varname> (integer)</title>
	    <para>
		This parameter allows the 15-digit number length restriction of the E.164
		format to be bypassed.  Especially useful in scenarios where various
		telephony number prefixes are in use, causing some numbers to exceed
		the standard maximum length.
	    </para>
	    <para>The default value is <emphasis>15</emphasis>.</para>
	    <example>
		<title>Set <varname>e164_max_length</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "e164_max_length", 16)
...
</programlisting>
	    </example>
	</section>

	<section id="param_require_date_hdr" xreflabel="require_date_hdr">
	    <title><varname>require_date_hdr</varname> (integer)</title>
	    <para>
	    Specifies whether the Date header is mandatory when doing verification
	    with the <xref linkend="func_stir_shaken_verify"/> function.
	    </para>
	    <para>
	    A value of <emphasis>1</emphasis> means required and <emphasis>0</emphasis>
	    not required.
	    </para>
	    <para>
	    If the parameter is set to "not required" but the Date header is present in the
	    message, the header value will be used as normally to check the freshness (as
	    configured in the <xref linkend="param_verify_date_freshness"/>
	    parameter). If the Date header is indeed missing, the value of the
	    <emphasis>iat</emphasis> claim in the PASSporT will be used instead.
		</para>
	    <para>The default value is <emphasis>1</emphasis> (required).</para>
	    <example>
		<title>Set <varname>require_date_hdr</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("stir_shaken", "require_date_hdr", 0)
...
</programlisting>
	    </example>
	</section>

    </section>
    <section id="exported_functions" xreflabel="exported_functions">
	<title>Exported Functions</title>

	<section id="func_stir_shaken_auth" xreflabel="stir_shaken_auth()">
	    <title>
		<function moreinfo="none">stir_shaken_auth(attest, origid, cert, pkey, x5u, [orig], [dest], [out])</function>
	    </title>
		<para>
		This function performs the steps of an authentication service. Before
		calling this function though, you must ensure:
		</para>
		<itemizedlist>
		<listitem>
		    <para>authority - the server is authoritative for the identity in question;</para>
		</listitem>
		<listitem>
		    <para>authentication - the originator is authorized to claim the given identity.</para>
		</listitem>
	    </itemizedlist>

	    <para>Meaning of the parameters is as follows:</para>
	    <itemizedlist>
	    <listitem>
			<para><emphasis>attest (string)</emphasis> - value of the 'attest' claim
			to be included in the PASSporT. The following values can be used:
			<itemizedlist>
				<listitem>
				<para><emphasis>A</emphasis> or <emphasis>full</emphasis></para>
				</listitem>
				<listitem>
				<para><emphasis>B</emphasis> or <emphasis>partial</emphasis></para>
				</listitem>
				<listitem>
				<para><emphasis>C</emphasis> or <emphasis>gateway</emphasis></para>
				</listitem>
			</itemizedlist>
			</para>
	    </listitem>
	    <listitem>
			<para><emphasis>origid (string)</emphasis> - value of the 'origid' claim
			to be included in the PASSporT. Treated by the module as an opaque string.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>cert (string)</emphasis> - the X.509 certificate used to
			compute the signature, in PEM format.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>pkey (string)</emphasis> - the private key used to
			compute the signature, in PEM format.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>x5u (string)</emphasis> - value of the 'x5u' claim to be
			included in the PASSporT. Treated by the module as an opaque string.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>orig (string, optional)</emphasis> - telephone number to
			be used as the originating identity in the PASSporT. If missing, this value
			will be derived from the SIP message.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>dest (string, optional)</emphasis> - telephone number to
			be used as the destination identity in the PASSporT. If missing, this value
			will be derived from the SIP message.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>out (string, no expand, optional)</emphasis> - name of an
			output variable to store the Identity header or the following flags:
			<itemizedlist>
			<listitem>
				<para><emphasis>req</emphasis> - the Identity header will be appended
				to the current request message;
				</para>
			</listitem>
			<listitem>
				<para><para><emphasis>rpl</emphasis> - the Identity header will be appended
				to all replies that will be generated by OpenSIPS for this request.
				</para>
				</para>
			</listitem>
			</itemizedlist>
			</para>
			<para>
			If this parameter is missing, the Identity header will be appended
			to the current request message.
			</para>
			<para>
			If an output variable is provided, it should be given as a quoted string,
			eg. <emphasis>"$var(identity_hdr)"</emphasis>.
			</para>
		</listitem>
		</itemizedlist>

	    <para>The function returns the following values:</para>
	    <itemizedlist>
	    <listitem>
		    <para>1: Success</para>
		</listitem>
		<listitem>
		    <para>-1: Internal error</para>
		</listitem>
		<listitem>
		    <para>-3: Failed to derive identity from SIP message because the
		    URI is not a telephone number</para>
		</listitem>
		<listitem>
		    <para>-4: Date header value is older than local policy for freshness</para>
		</listitem>
		<listitem>
		    <para>-5: The current time or Date header value does not fall within
		    the certificate validity</para>
		</listitem>
	    </itemizedlist>
	    <para>This function can be used from REQUEST_ROUTE.</para>
	    <example>
		<title><function>stir_shaken_auth()</function> usage</title>
		<programlisting format="linespecific">
...
stir_shaken_auth("A", "4437c7eb-8f7a-4f0e-a863-f53a0e60251a",
	$var(cert), $var(privKey), "https://certs.example.org/cert.pem");
...
</programlisting>
	    </example>
	</section>

	<section id="func_stir_shaken_verify" xreflabel="stir_shaken_verify()">
	    <title>
		<function moreinfo="none">stir_shaken_verify(cert, err_code, err_reason, [orig], [dest])</function>
	    </title>
	    <para>
		This function performs the steps of an verification service.
		</para>
		<para>Meaning of the parameters is as follows:</para>
	    <itemizedlist>
	    <listitem>
			<para><emphasis>cert (string)</emphasis> - the X.509 certificate used to
			verify the signature, in PEM format.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>err_code (var)</emphasis> - output variable that will
			store the SIP response code associated with an eventual error of the
			verification process.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>err_reason (var)</emphasis> - output variable that will
			store the SIP response reason phrase associated with an eventual error of the
			verification process.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>orig (string, optional)</emphasis> - telephone number to
			be used as the originating identity in the verification prcess. If missing,
			this value will be derived from the SIP message.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>dest (string, optional)</emphasis> - telephone number to
			be used as the destination identity in the verification process. If missing,
			this value will be derived from the SIP message.
			</para>
		</listitem>
	    </itemizedlist>
		<para>The function returns the following values:</para>
		<itemizedlist>
		<listitem>
		    <para>1: Success</para>
		</listitem>
		<listitem>
		    <para>-1: Internal error</para>
		</listitem>
		<listitem>
		    <para>-2: No Identity or Date header found</para>
		</listitem>
		<listitem>
		    <para>-3: Failed to derive identity from SIP message because the
		    URI is not a telephone number</para>
		</listitem>
		<listitem>
		    <para>-4: Invalid identity header</para>
		</listitem>
		<listitem>
		    <para>-5: Unsupported 'ppt' or 'alg' Identity header parameter</para>
		</listitem>
		<listitem>
		    <para>-6: Date header value is older than local policy for freshness</para>
		</listitem>
		<listitem>
		    <para>-7: The Date header value does not fall within the certificate validity</para>
		</listitem>
		<listitem>
		    <para>-8: Invalid certificate</para>
		</listitem>
		<listitem>
		    <para>-9: Signature does not verify successfully</para>
		</listitem>
	    </itemizedlist>
	    <para>This function can be used from REQUEST_ROUTE.</para>
	    <example>
		<title><function>stir_shaken_verify()</function> usage</title>
		<programlisting format="linespecific">
...
$var(rc) = stir_shaken_verify($var(cert), $var(err_code), $var(err_reason));
if ($var(rc) &lt; -1) {
	send_reply($var(err_sip_code), $var(err_sip_reason));
	exit;
}
...
</programlisting>
	    </example>
	</section>

	<section id="func_stir_shaken_check" xreflabel="stir_shaken_check()">
	    <title>
		<function moreinfo="none">stir_shaken_check()</function>
	    </title>
	    <para>
		This function checks the Identity header in order to validate the
		STIR/SHAKEN information in terms of format. It detects issues such as:
		missing or badly formated PASSporT claims, unsupported extensions etc.
		</para>
		<para>The function returns the following values:</para>
		<itemizedlist>
		<listitem>
		    <para>1: Success</para>
		</listitem>
		<listitem>
		    <para>-1: Internal error</para>
		</listitem>
		<listitem>
		    <para>-2: No Identity header found</para>
		</listitem>
		<listitem>
		    <para>-3: Invalid identity header</para>
		</listitem>
		<listitem>
		    <para>-4: Unsupported 'ppt' or 'alg' Identity header parameter</para>
		</listitem>
	    </itemizedlist>
	    <para>This function can be used from REQUEST_ROUTE.</para>
	    <example>
		<title><function>stir_shaken_check()</function> usage</title>
		<programlisting format="linespecific">
...
if (stir_shaken_check()) {
	xlog("forwarding call to stir/shaken verification service\n");
	...
}
...
</programlisting>
	    </example>
	</section>

	<section id="func_stir_shaken_check_cert" xreflabel="stir_shaken_check_cert()">
	    <title>
		<function moreinfo="none">stir_shaken_check_cert()</function>
	    </title>
	    <para>
		This function checks if the current time falls within the given
		certificate's validity period.
		</para>
		<para>The function returns the following values:</para>
		<itemizedlist>
		<listitem>
		    <para>1: Success</para>
		</listitem>
		<listitem>
		    <para>-1: Internal error</para>
		</listitem>
		<listitem>
		    <para>-2: Certificate is not valid</para>
		</listitem>
	    </itemizedlist>
	    <para>This function can be used from REQUEST_ROUTE.</para>
	    <example>
		<title><function>stir_shaken_check_cert()</function> usage</title>
		<programlisting format="linespecific">
...
# update expired cached certificates
cache_fetch("local", $identity(x5u), $var(cert));
if (!stir_shaken_check_cert($var(cert))) {
	rest_get($identity(x5u), $var(cert));
	cache_store("local", $identity(x5u), $var(cert));
}
...
</programlisting>
	    </example>
	</section>

	<section id="stir_shaken_disengagement" xreflabel="stir_shaken_disengagement()">
	    <title>
		<function moreinfo="none">stir_shaken_disengagement(token)</function>
	    </title>
	    <para>
		This function add P-Identity-Bypass header with token value at the end of SIP headers.
		</para>
		<para>Meaning of the parameters is as follows:</para>
	    <itemizedlist>
	    <listitem>
			<para><emphasis>token (string)</emphasis> - The token provided by the authority during outage.
			</para>
		</listitem>
	    </itemizedlist>
		<para>The function returns the following values:</para>
		<itemizedlist>
		<listitem>
		    <para>1: Success</para>
		</listitem>
		<listitem>
		    <para>0: Failed to add P-Identity-Bypass header</para>
		</listitem>
	    </itemizedlist>
	    <para>This function can be used from REQUEST_ROUTE.</para>
	    <example>
		<title><function>stir_shaken_disengagement()</function> usage</title>
		<programlisting format="linespecific">
...
if ( is_method("INVITE") &amp;&amp; !has_totag()) {
	# equivalent to sipmsgops module: append_hf("P-Identity-Bypass: OSIP99-1234567890ABCDEF\r\n");
	stir_shaken_disengagement("OSIP99-1234567890ABCDEF");
}
...
</programlisting>
	    </example>
	</section>

    </section>

    <section id="exported_pseudo_variables">
	<title>Exported Pseudo-Variables</title>
	<section id="pv_identity" xreflabel="$identity">
	<title>
		<varname>$identity(field)</varname>
	</title>
	<para>This is a read-only pseudo-variable that provides access to the
	parsed information from the Identity header, through the following
	subnames:</para>
	<itemizedlist>
	<listitem>
		<para><emphasis>header</emphasis> - the entire PASSporT header;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>x5u</emphasis> - the value of the 'x5u' PASSporT claim;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>payload</emphasis> - the entire PASSporT payload;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>attest</emphasis> - the value of the 'attest' PASSporT claim;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>dest</emphasis> - the value of the 'tn' member of the 'dest'
		PASSporT claim;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>iat</emphasis> - the value of the 'iat' PASSporT claim;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>orig</emphasis> - the value of the 'tn' member of the 'orig'
		PASSporT claim;
		</para>
	</listitem>
	<listitem>
		<para><emphasis>origid</emphasis> - the value of the 'origid' PASSporT claim;
		</para>
	</listitem>
	</itemizedlist>
	<example>
	<title><varname>identity</varname> usage</title>
	<programlisting format="linespecific">
...
	# acquire the certificate to use for the verification process
	$var(rc) = rest_get($identity(x5u), $var(cert));
	if ($var(rc) &lt; 0) {
		send_reply(436, "Bad Identity Info");
		exit;
	}
	...
	xlog("Verified caller:$identity(orig), attestation level: $identity(attest)\n");
...
	</programlisting>
	</example>
	</section>
	</section>

	<section id="exported_mi_functions" xreflabel="Exported MI Functions">
		<title>Exported MI Functions</title>

		<section id="mi_stir_shaken_ca_reload" xreflabel="stir_shaken_ca_reload">
			<title>
				<function moreinfo="none">stir_shaken_ca_reload</function>
			</title>

			<para>
				Reload the file containing trusted CA certificates for the verifier
				and the directory containing trusted CA certificates for the verifier.
			</para>

			<para>
				Name: <emphasis>stir_shaken_ca_reload</emphasis>
			</para>

			<para>Parameters: <emphasis>none</emphasis></para>

			<para>
				MI FIFO Command Format:
			</para>

<programlisting  format="linespecific">
...
opensips-cli -x mi stir_shaken_ca_reload
"OK"
...
</programlisting>

		</section>

		<section id="mi_stir_shaken_crl_reload" xreflabel="stir_shaken_crl_reload">
			<title>
				<function moreinfo="none">stir_shaken_crl_reload</function>
			</title>

			<para>
				Reload the file containing certificate revocation lists (CRLs) for the verifier
				and the directory containing certificate revocation lists for the verifier.
			</para>

			<para>
				Name: <emphasis>stir_shaken_crl_reload</emphasis>
			</para>

			<para>Parameters: <emphasis>none</emphasis></para>

			<para>
				MI FIFO Command Format:
			</para>

<programlisting  format="linespecific">
...
opensips-cli -x mi stir_shaken_crl_reload
"OK"
...
</programlisting>

	</section>

	</section>

</chapter>
