Browse code

stirshaken: Add PVs to allow access to x509 subject and ppt grants

- added vs_certsubject_pvname and vs_pptgrants_pvname config params
- adjusted log level of load/unload events

Trevor Peirce authored on 23/03/2022 02:53:54 • Daniel-Constantin Mierla committed on 25/03/2022 12:12:37
Showing 1 changed files
... ...
@@ -50,6 +50,11 @@ static int stirshaken_vs_cache_certificates = 0;
50 50
 static size_t stirshaken_vs_cache_expire_s = 120;
51 51
 static str stirshaken_vs_cache_dir = str_init("");
52 52
 
53
+static str stirshaken_vs_x509_pvname = STR_NULL;
54
+static pv_spec_t stirshaken_vs_x509_pv;
55
+static str stirshaken_vs_pptg_pvname = STR_NULL;
56
+static pv_spec_t stirshaken_vs_pptg_pv;
57
+
53 58
 static int mod_init(void);
54 59
 static int child_init(int);
55 60
 static void mod_destroy(void);
... ...
@@ -90,6 +95,8 @@ static param_export_t params[] = {
90 95
 	{"vs_cache_certificates",	PARAM_INT,   &stirshaken_vs_cache_certificates},
91 96
 	{"vs_cache_expire_s",		PARAM_INT,   &stirshaken_vs_cache_expire_s},
92 97
 	{"vs_cache_dir",			PARAM_STR,   &stirshaken_vs_cache_dir},
98
+	{"vs_certsubject_pvname",   PARAM_STR,   &stirshaken_vs_x509_pvname},
99
+	{"vs_pptgrants_pvname",     PARAM_STR,   &stirshaken_vs_pptg_pvname},
93 100
 	{0, 0, 0}
94 101
 };
95 102
 
... ...
@@ -356,6 +363,22 @@ static int mod_init(void)
356 363
 				"then please set @vs_verify_x509_cert_path param to 1 and configure @vs_ca_dir (and optionally @vs_crl_dir)\n");
357 364
 	}
358 365
 
366
+	if(stirshaken_vs_pptg_pvname.s != 0) {
367
+		if(pv_parse_spec(&stirshaken_vs_pptg_pvname, &stirshaken_vs_pptg_pv) == NULL
368
+				|| !pv_is_w(&stirshaken_vs_pptg_pv)) {
369
+			LM_ERR("Invalid vs_pptgrants_pvname '%s'\n", stirshaken_vs_pptg_pvname.s);
370
+			return -1;
371
+		}
372
+	}
373
+
374
+	if(stirshaken_vs_x509_pvname.s != 0) {
375
+		if(pv_parse_spec(&stirshaken_vs_x509_pvname, &stirshaken_vs_x509_pv) == NULL
376
+				|| !pv_is_w(&stirshaken_vs_x509_pv)) {
377
+			LM_ERR("Invalid vs_certsubject_pvname '%s'\n", stirshaken_vs_x509_pvname.s);
378
+			return -1;
379
+		}
380
+	}
381
+
359 382
 	return 0;
360 383
 }
361 384
 
... ...
@@ -364,13 +387,13 @@ static int mod_init(void)
364 387
  */
365 388
 static int child_init(int rank)
366 389
 {
367
-	LM_INFO("mod stirshaken child init\n");
390
+	LM_DBG("mod stirshaken child init\n");
368 391
 	return 0;
369 392
 }
370 393
 
371 394
 static void mod_destroy(void)
372 395
 {
373
-	LM_INFO("mod stirshaken destroy\n");
396
+	LM_DBG("mod stirshaken destroy\n");
374 397
 	stir_shaken_as_destroy(&as);
375 398
 	stir_shaken_vs_destroy(&vs);
376 399
 	stir_shaken_deinit();
... ...
@@ -391,7 +414,7 @@ static int stirshaken_handle_cache(stir_shaken_context_t *ss, stir_shaken_passpo
391 414
 
392 415
 	if (!ss->cert_fetched_from_cache) {
393 416
 
394
-		// save certificate to cache with url as a key 
417
+		// save certificate to cache with url as a key
395 418
 		char cert_full_path[STIR_SHAKEN_BUFLEN] = { 0 };
396 419
 		const char *x5u = stir_shaken_passport_get_header(ss, passport, "x5u");
397 420
 
... ...
@@ -445,6 +468,8 @@ static int ki_stirshaken_check_identity(sip_msg_t *msg)
445 468
 	stir_shaken_passport_t *passport_out = NULL;
446 469
 	stir_shaken_cert_t *cert_out = NULL;
447 470
 
471
+	pv_value_t val;
472
+
448 473
 	for (hf = msg->headers; hf; hf = hf->next) {
449 474
 		if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN
450 475
 				&& strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY,
... ...
@@ -503,6 +528,27 @@ static int ki_stirshaken_check_identity(sip_msg_t *msg)
503 528
 		}
504 529
 	}
505 530
 
531
+	if (stirshaken_vs_pptg_pvname.s != 0) {
532
+		memset(&val, 0, sizeof(pv_value_t));
533
+		val.flags = PV_VAL_STR;
534
+		val.rs.s = jwt_get_grants_json(passport_out->jwt, NULL);
535
+		val.rs.len = strlen(val.rs.s);
536
+		if (stirshaken_vs_pptg_pv.setf(msg, &stirshaken_vs_pptg_pv.pvp, (int)EQ_T, &val) < 0) {
537
+			LM_ERR("setting %s failed\n", stirshaken_vs_pptg_pvname.s);
538
+		}
539
+		stir_shaken_free_jwt_str(val.rs.s);
540
+	}
541
+
542
+	if (stirshaken_vs_x509_pvname.s != 0) {
543
+		memset(&val, 0, sizeof(pv_value_t));
544
+		val.flags = PV_VAL_STR;
545
+		val.rs.s = stir_shaken_cert_get_subject(cert_out);
546
+		val.rs.len = strlen(val.rs.s);
547
+		if (stirshaken_vs_x509_pv.setf(msg, &stirshaken_vs_x509_pv.pvp, (int)EQ_T, &val) < 0) {
548
+			LM_ERR("setting %s failed\n", stirshaken_vs_x509_pvname.s);
549
+		}
550
+	}
551
+
506 552
 	LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check");
507 553
 	stir_shaken_passport_destroy(&passport_out);
508 554
 	stir_shaken_cert_destroy(&cert_out);
... ...
@@ -512,6 +558,24 @@ fail:
512 558
 	stir_shaken_passport_destroy(&passport_out);
513 559
 	stir_shaken_cert_destroy(&cert_out);
514 560
 	LM_ERR("identity check: fail\n");
561
+	if (stirshaken_vs_pptg_pvname.s != 0) {
562
+		memset(&val, 0, sizeof(pv_value_t));
563
+		val.flags = PV_VAL_NULL;
564
+		//val.rs.s = NULL;
565
+		//val.rs.len = 0;
566
+		if (stirshaken_vs_pptg_pv.setf(msg, &stirshaken_vs_pptg_pv.pvp, (int)EQ_T, &val) < 0) {
567
+			LM_ERR("setting %s to null failed\n", stirshaken_vs_pptg_pvname.s);
568
+		}
569
+	}
570
+	if (stirshaken_vs_x509_pvname.s != 0) {
571
+		memset(&val, 0, sizeof(pv_value_t));
572
+		val.flags = PV_VAL_NULL;
573
+		//val.rs.s = NULL;
574
+		//val.rs.len = 0;
575
+		if (stirshaken_vs_x509_pv.setf(msg, &stirshaken_vs_x509_pv.pvp, (int)EQ_T, &val) < 0) {
576
+			LM_ERR("setting %s to null failed\n", stirshaken_vs_x509_pvname.s);
577
+		}
578
+	}
515 579
 	return -1;
516 580
 }
517 581
 
... ...
@@ -526,7 +590,7 @@ fail:
526 590
  * This method checks if PASSporT verifies successfully with a public key retrieved from obtained certificate.
527 591
  * Optionally (if Verification Service is configured to do so with stirshaken_vs_verify_x509_cert_path param set to 1)
528 592
  * this method checks if certificate is trusted, by execution of X509 certificate path check.
529
- * 
593
+ *
530 594
  * Optionally:
531 595
  * 		- retrieve PASSporT from SIP Identity Header
532 596
  * 		- retrieve certificate referenced in PASSporT's x5u header
Browse code

stirshaken: Turn on X509 cert path check as default

Piotr Gregor authored on 26/03/2021 13:24:13
Showing 1 changed files
... ...
@@ -40,7 +40,7 @@ MODULE_VERSION
40 40
 static str stirshaken_as_default_key = str_init("");
41 41
 
42 42
 // Verification service
43
-static int stirshaken_vs_verify_x509_cert_path = 0;
43
+static int stirshaken_vs_verify_x509_cert_path = 1;
44 44
 static str stirshaken_vs_ca_dir = str_init("");
45 45
 static str stirshaken_vs_crl_dir = str_init("");
46 46
 static int stirshaken_vs_identity_expire_s = 60;
Browse code

stirshaken: fixed kemi exports

Daniel-Constantin Mierla authored on 21/03/2021 10:40:59
Showing 1 changed files
... ...
@@ -551,7 +551,7 @@ static int w_stirshaken_check_identity(sip_msg_t *msg, char *str1, char *str2)
551 551
 	return ki_stirshaken_check_identity(msg);
552 552
 }
553 553
 
554
-static int ki_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path)
554
+static int ki_stirshaken_check_identity_with_cert(sip_msg_t *msg, str *cert_path)
555 555
 {
556 556
 	str ibody = STR_NULL;
557 557
 	hdr_field_t *hf = NULL;
... ...
@@ -574,7 +574,7 @@ static int ki_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_pat
574 574
 
575 575
 	ibody = hf->body;
576 576
 
577
-	if (!(cert.x = stir_shaken_load_x509_from_file(&ss, cert_path))) {
577
+	if (!(cert.x = stir_shaken_load_x509_from_file(&ss, cert_path->s))) {
578 578
 		LM_DBG("Cannot load X509 from file\n");
579 579
 		stirshaken_print_error_details(&ss);
580 580
 		goto fail;
... ...
@@ -641,7 +641,7 @@ static int w_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path
641 641
 		return -1;
642 642
 	}
643 643
 
644
-	return ki_stirshaken_check_identity_with_cert(msg, cert_path);
644
+	return ki_stirshaken_check_identity_with_cert(msg, &keyval);
645 645
 }
646 646
 
647 647
 static int ki_stirshaken_check_identity_with_key(sip_msg_t *msg, str *keypath)
... ...
@@ -954,8 +954,8 @@ static sr_kemi_t sr_kemi_stirshaken_exports[] = {
954 954
 	},
955 955
 	{ str_init("stirshaken"), str_init("stirshaken_check_identity_with_key"),
956 956
 		SR_KEMIP_INT, ki_stirshaken_check_identity_with_key,
957
-		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
958
-			SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR }
957
+		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
958
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
959 959
 	},
960 960
 	{ str_init("stirshaken"), str_init("stirshaken_add_identity"),
961 961
 		SR_KEMIP_INT, ki_stirshaken_add_identity,
Browse code

stirshaken: handle NULL or empty origid by generating random uuid

Piotr Gregor authored on 18/03/2021 17:28:41
Showing 1 changed files
... ...
@@ -741,7 +741,16 @@ static int ki_stirshaken_add_identity_with_key(sip_msg_t *msg, str *x5u, str *at
741 741
 		.origtn_val = origtn_val ? origtn_val->s : NULL,
742 742
 		.origid = origid ? origid->s : NULL
743 743
 	};
744
+	char uuid_str[37] = { 0 };
744 745
 
746
+	if (!params.origid || !strlen(params.origid)) {
747
+
748
+		uuid_t uuid;
749
+
750
+		uuid_generate(uuid);
751
+		uuid_unparse_lower(uuid, uuid_str);
752
+		params.origid = uuid_str;
753
+	}
745 754
 
746 755
 	if (keypath && keypath->s) {
747 756
 
Browse code

modules: Add stirshaken module

This module uses libstirshaken to implement STIR-Shaken authentication and verification functions (STI-AS/VS).
It allows for easy verification of a SIP call containing PASSporT wrapped into SIP Identity Header using a specific certificate,
or a specific key, or by performing complete check on PASSporT including downloading certificate referenced in it's x5u header,
and optionally checking it against trusted root certificates (X509 cert path check). Certificates may be cached and loaded from disk.

Example usage:

loadmodule "stirshaken"
modparam("stirshaken", "as_default_key", "/path/to/key")

modparam("stirshaken", "vs_verify_x509_cert_path", 1)
modparam("stirshaken", "vs_ca_dir", "/path/to/ca")
modparam("stirshaken", "vs_cache_certificates", 1)
modparam("stirshaken", "vs_cache_dir", "/tmp/cert_cache")
modparam("stirshaken", "vs_cache_expire_s", 90)

request_route {
(...)
stirshaken_add_identity("https://sp.com/sp.pem", "B", "+44100", "+44200", "ref");
(...)

request_route {
(...)
stirshaken_check_identity();
(...)

Piotr Gregor authored on 05/02/2021 18:23:11 • Daniel-Constantin Mierla committed on 18/03/2021 07:43:26
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,972 @@
1
+/**
2
+ * Copyright (C) 2021 kamailio.org
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * This file is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ *
12
+ * This file is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20
+ *
21
+ */
22
+
23
+#include <stdio.h>
24
+#include <unistd.h>
25
+#include <stdlib.h>
26
+#include <string.h>
27
+
28
+#include <stir_shaken.h>
29
+
30
+#include "../../core/sr_module.h"
31
+#include "../../core/dprint.h"
32
+#include "../../core/mod_fix.h"
33
+#include "../../core/data_lump.h"
34
+#include "../../core/lvalue.h"
35
+#include "../../core/kemi.h"
36
+
37
+MODULE_VERSION
38
+
39
+// Authentication service
40
+static str stirshaken_as_default_key = str_init("");
41
+
42
+// Verification service
43
+static int stirshaken_vs_verify_x509_cert_path = 0;
44
+static str stirshaken_vs_ca_dir = str_init("");
45
+static str stirshaken_vs_crl_dir = str_init("");
46
+static int stirshaken_vs_identity_expire_s = 60;
47
+static int stirshaken_vs_connect_timeout_s = 5;
48
+
49
+static int stirshaken_vs_cache_certificates = 0;
50
+static size_t stirshaken_vs_cache_expire_s = 120;
51
+static str stirshaken_vs_cache_dir = str_init("");
52
+
53
+static int mod_init(void);
54
+static int child_init(int);
55
+static void mod_destroy(void);
56
+
57
+static int w_stirshaken_check_identity(sip_msg_t *msg, char *str1, char *str2);
58
+static int w_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path, char *str2);
59
+static int w_stirshaken_check_identity_with_key(sip_msg_t *msg, char *pkey_path, char *str2);
60
+static int w_stirshaken_add_identity(sip_msg_t *msg, str *px5u, str *pattest, str *porigtn_val, str *pdesttn_val, str *porigid);
61
+static int w_stirshaken_add_identity_with_key(sip_msg_t *msg, str *px5u, str *pattest, str *porigtn_val, str *pdesttn_val, str *porigid, str *pkeypath);
62
+
63
+
64
+/* clang-format off */
65
+static cmd_export_t cmds[]={
66
+	{"stirshaken_check_identity", (cmd_function)w_stirshaken_check_identity, 0,
67
+		0, 0, ANY_ROUTE},
68
+	{"stirshaken_check_identity_with_cert", (cmd_function)w_stirshaken_check_identity_with_cert, 1,
69
+		fixup_spve_null, fixup_free_spve_null, ANY_ROUTE},
70
+	{"stirshaken_check_identity_with_key", (cmd_function)w_stirshaken_check_identity_with_key, 1,
71
+		fixup_spve_null, fixup_free_spve_null, ANY_ROUTE},
72
+	{"stirshaken_add_identity", (cmd_function)w_stirshaken_add_identity, 5,
73
+		fixup_spve_all, fixup_free_spve_all, ANY_ROUTE},
74
+	{"stirshaken_add_identity_with_key", (cmd_function)w_stirshaken_add_identity_with_key, 6,
75
+		fixup_spve_all, fixup_free_spve_all, ANY_ROUTE},
76
+	{0, 0, 0, 0, 0, 0}
77
+};
78
+
79
+static param_export_t params[] = {
80
+
81
+	// Authentication service
82
+	{"as_default_key",			PARAM_STR,   &stirshaken_as_default_key},
83
+
84
+	// Verification service
85
+	{"vs_verify_x509_cert_path",PARAM_INT,   &stirshaken_vs_verify_x509_cert_path},
86
+	{"vs_ca_dir",				PARAM_STR,   &stirshaken_vs_ca_dir},
87
+	{"vs_crl_dir",				PARAM_STR,   &stirshaken_vs_crl_dir},
88
+	{"vs_identity_expire_s",	PARAM_INT,   &stirshaken_vs_identity_expire_s},
89
+	{"vs_connect_timeout_s",	PARAM_INT,   &stirshaken_vs_connect_timeout_s},
90
+	{"vs_cache_certificates",	PARAM_INT,   &stirshaken_vs_cache_certificates},
91
+	{"vs_cache_expire_s",		PARAM_INT,   &stirshaken_vs_cache_expire_s},
92
+	{"vs_cache_dir",			PARAM_STR,   &stirshaken_vs_cache_dir},
93
+	{0, 0, 0}
94
+};
95
+
96
+struct module_exports exports = {
97
+	"stirshaken",
98
+	DEFAULT_DLFLAGS, /* dlopen flags */
99
+	cmds,
100
+	params,
101
+	0,              /* exported RPC methods */
102
+	0,              /* exported pseudo-variables */
103
+	0,              /* response function */
104
+	mod_init,       /* module initialization function */
105
+	child_init,     /* per child init function */
106
+	mod_destroy    	/* destroy function */
107
+};
108
+/* clang-format on */
109
+
110
+static void stirshaken_print_error_details(void *context)
111
+{
112
+	const char				*error_description = NULL;
113
+	stir_shaken_error_t		error_code = STIR_SHAKEN_ERROR_GENERAL;
114
+
115
+	if (stir_shaken_is_error_set(context)) {
116
+		error_description = stir_shaken_get_error(context, &error_code);
117
+		LM_DBG("failure details:\n");
118
+		LM_DBG("failure reason is: %s\n", error_description);
119
+		LM_DBG("failure error code is: %d\n", error_code);
120
+	}
121
+}
122
+
123
+static unsigned long hash_to_long(const char *str)
124
+{
125
+	unsigned long hash = 5381;
126
+	int c;
127
+
128
+	while ((c = *str++))
129
+		hash = ((hash << 5) + hash) + c;
130
+
131
+	return hash;
132
+}
133
+
134
+static void hash_to_string(const char *url, char *buf, int buf_len)
135
+{
136
+	unsigned long hash_long = hash_to_long(url);
137
+	snprintf(buf, buf_len, "%lu.pem", hash_long);
138
+}
139
+
140
+static int get_cert_name_hashed(const char *name, char *buf, int buf_len)
141
+{
142
+	char cert_hash[64] = { 0 };
143
+
144
+	hash_to_string(name, cert_hash, sizeof(cert_hash));
145
+
146
+	if (!stir_shaken_make_complete_path(buf, buf_len, stirshaken_vs_cache_dir.s, cert_hash, "/")) {
147
+		LM_ERR("Cannot create cert name hashed\n");
148
+		return -1;
149
+	}
150
+
151
+	return 0;
152
+}
153
+
154
+static stir_shaken_status_t shaken_callback(stir_shaken_callback_arg_t *arg)
155
+{
156
+	stir_shaken_context_t ss = { 0 };
157
+	stir_shaken_cert_t cache_copy = { 0 };
158
+	char cert_full_path[STIR_SHAKEN_BUFLEN] = { 0 };
159
+
160
+	switch (arg->action) {
161
+
162
+		case STIR_SHAKEN_CALLBACK_ACTION_CERT_FETCH_ENQUIRY:
163
+
164
+			// Default behaviour for certificate fetch enquiry is to request downloading, but in some cases it would be useful to avoid that and use pre-cached certificate.
165
+			// Here, we supply libstirshaken with certificate we cached earlier, avoiding HTTP(S) download.
166
+			// We must return STIR_SHAKEN_STATUS_HANDLED to signal this to the library, otherwise it would execute HTTP(S) download
167
+
168
+			if (!stirshaken_vs_cache_certificates) {
169
+				LM_DBG("Certificate caching is turned off - requesting certificate %s to be downloaded...\n", arg->cert.public_url);
170
+				return STIR_SHAKEN_STATUS_NOT_HANDLED;
171
+			}
172
+
173
+			if (-1 == get_cert_name_hashed(arg->cert.public_url, cert_full_path, STIR_SHAKEN_BUFLEN)) {
174
+				LM_ERR("Cannot get cert name hashed\n");
175
+				goto exit;
176
+			}
177
+
178
+			LM_DBG("Checking for certificate %s in cache (looking for name: %s)\n", arg->cert.public_url, cert_full_path);
179
+
180
+			if (STIR_SHAKEN_STATUS_OK == stir_shaken_file_exists(cert_full_path)) {
181
+
182
+				LM_DBG("Certificate %s found in cache\n", arg->cert.public_url);
183
+
184
+				if (stirshaken_vs_cache_expire_s) {
185
+
186
+					struct stat attr = { 0 };
187
+					time_t now_s = time(NULL), diff = 0;
188
+
189
+					LM_DBG("Checking cached certificate against expiration setting of %zus\n", stirshaken_vs_cache_expire_s);
190
+
191
+					if (-1 == stat(cert_full_path, &attr)) {
192
+						LM_ERR("Cannot get modification timestamp on certificate %s. Error code is %d (%s)\n", cert_full_path, errno, strerror(errno));
193
+						goto exit;
194
+					}
195
+
196
+					if (now_s < attr.st_mtime) {
197
+						LM_ERR("Modification timestamp on certificate %s is invalid\n", cert_full_path);
198
+						goto exit;
199
+					}
200
+
201
+					diff = now_s - attr.st_mtime;
202
+
203
+					LM_DBG("Checking cached certificate against expiration setting of %zus (now is: %zu, file modification timestamp is: %zu, difference is: %zu)\n",
204
+						stirshaken_vs_cache_expire_s, now_s, attr.st_mtime, diff);
205
+
206
+					if (diff > stirshaken_vs_cache_expire_s) {
207
+						LM_WARN("Cached certificate %s is behind expiration threshold (%zu > %zu). Need to download new certificate...\n", cert_full_path, diff, stirshaken_vs_cache_expire_s);
208
+						goto exit;
209
+					} else {
210
+						LM_WARN("Cached certificate %s is valid for next %zus\n", cert_full_path, stirshaken_vs_cache_expire_s - diff);
211
+					}
212
+				}
213
+
214
+				if (!(cache_copy.x = stir_shaken_load_x509_from_file(&ss, cert_full_path))) {
215
+					LM_ERR("Cannot load X509 from file %s\n", cert_full_path);
216
+					goto exit;
217
+				}
218
+
219
+				if (STIR_SHAKEN_STATUS_OK != stir_shaken_cert_copy(&ss, &arg->cert, &cache_copy)) {
220
+					LM_ERR("Cannot copy certificate %s\n", cert_full_path);
221
+					stir_shaken_cert_deinit(&cache_copy);
222
+					goto exit;
223
+				}
224
+
225
+				stir_shaken_cert_deinit(&cache_copy);
226
+
227
+				return STIR_SHAKEN_STATUS_HANDLED;
228
+			}
229
+
230
+		default:
231
+			LM_DBG("Certificate %s not found in cache\n", arg->cert.public_url);
232
+			return STIR_SHAKEN_STATUS_NOT_HANDLED;
233
+	}
234
+
235
+exit:
236
+
237
+	return STIR_SHAKEN_STATUS_NOT_HANDLED;
238
+}
239
+
240
+stir_shaken_as_t *as = NULL;
241
+stir_shaken_vs_t *vs = NULL;
242
+
243
+static int start_as(stir_shaken_context_t *ss)
244
+{
245
+	as = stir_shaken_as_create(ss);
246
+	if (!as) {
247
+		LM_ERR("Cannot create Authentication Service\n");
248
+		stirshaken_print_error_details(ss);
249
+		return -1;
250
+	}
251
+
252
+	if (stirshaken_as_default_key.len > 0) {
253
+
254
+		if (STIR_SHAKEN_STATUS_OK != stir_shaken_as_load_private_key(ss, as, stirshaken_as_default_key.s)) {
255
+			LM_ERR("Failed to load private key (%s). Please check @as_default_key param\n", stirshaken_as_default_key.s);
256
+			stirshaken_print_error_details(ss);
257
+			return -1;
258
+		}
259
+	}
260
+
261
+	return 0;
262
+}
263
+
264
+static int start_vs(stir_shaken_context_t *ss)
265
+{
266
+	vs = stir_shaken_vs_create(ss);
267
+	if (!vs) {
268
+		LM_ERR("Cannot create Verification Service\n");
269
+		stirshaken_print_error_details(ss);
270
+		return -1;
271
+	}
272
+
273
+	// Handle settings
274
+
275
+	stir_shaken_vs_set_connect_timeout(ss, vs, stirshaken_vs_connect_timeout_s);
276
+	stir_shaken_vs_set_callback(ss, vs, shaken_callback);
277
+
278
+	if (stirshaken_vs_cache_certificates) {
279
+
280
+		if (!stirshaken_vs_cache_dir.len) {
281
+			LM_ERR("Certificate caching is turned on but cache dir is not set. Please set cache dir\n");
282
+			return -1;
283
+		}
284
+
285
+		if (STIR_SHAKEN_STATUS_OK != stir_shaken_dir_exists(stirshaken_vs_cache_dir.s)) {
286
+			LM_ERR("Certificate caching is turned on but cache dir %s does not exist. Please check cache dir name\n", stirshaken_vs_cache_dir.s);
287
+			return -1;
288
+		}
289
+	}
290
+
291
+	if (stirshaken_vs_verify_x509_cert_path) {
292
+
293
+		stir_shaken_vs_set_x509_cert_path_check(ss, vs, 1);
294
+
295
+		if (stirshaken_vs_ca_dir.len > 0) {
296
+			if (STIR_SHAKEN_STATUS_OK != stir_shaken_vs_load_ca_dir(ss, vs, stirshaken_vs_ca_dir.s)) {
297
+				LM_ERR("Failed to init X509 cert store with CA dir\n");
298
+				stirshaken_print_error_details(ss);
299
+				return -1;
300
+			}
301
+		} else {
302
+			LM_WARN("Cert path check is turned on, but CA dir is not set. No end entity certificate will ever pass this check. Did you forget to set CA dir?\n");
303
+		}
304
+
305
+		if (stirshaken_vs_crl_dir.len > 0) {
306
+			if (STIR_SHAKEN_STATUS_OK != stir_shaken_vs_load_crl_dir(ss, vs, stirshaken_vs_crl_dir.s)) {
307
+				LM_ERR("Failed to init X509 cert store with CRL dir\n");
308
+				stirshaken_print_error_details(ss);
309
+				return -1;
310
+			}
311
+		} else {
312
+			LM_WARN("Cert path check is turned on, but CRL dir is not set (it's not mandatory). Did you forget to set CRL dir?\n");
313
+		}
314
+	}
315
+
316
+	return 0;
317
+}
318
+
319
+static int mod_init(void)
320
+{
321
+	stir_shaken_context_t	ss = { 0 };
322
+
323
+	LM_INFO("Initialising STIR-Shaken\n");
324
+
325
+	if (STIR_SHAKEN_STATUS_OK != stir_shaken_init(&ss, STIR_SHAKEN_LOGLEVEL_NOTHING)) {
326
+		LM_ERR("Cannot init libstirshaken\n");
327
+		stirshaken_print_error_details(&ss);
328
+		return -1;
329
+	}
330
+
331
+	if (0 == start_as(&ss)) {
332
+		if (stirshaken_as_default_key.len > 0) {
333
+			LM_INFO("Authentication Service ready (with default key %s)\n", stirshaken_as_default_key.s);
334
+		} else {
335
+			LM_INFO("Authentication Service ready (without default key)\n");
336
+		}
337
+	} else {
338
+		LM_WARN("Cannot start Authentication Service (%s)\n", stirshaken_as_default_key.len > 0 ? "with default key" : "without default key");
339
+	}
340
+
341
+	if (stirshaken_as_default_key.len == 0) {
342
+		LM_WARN("Authentication Service using default key will not be available, because 'as_default_key' is not set (only authentication through w_stirshaken_add_identity_with_key is possible using a specific key)\n");
343
+	}
344
+
345
+	if (0 == start_vs(&ss)) {
346
+		LM_INFO("Verification Service ready (%s)\n", stirshaken_vs_verify_x509_cert_path ? "with X509 cert path check" : "without X509 cert path check");
347
+	} else {
348
+		LM_WARN("Cannot start Verification Service (%s)\n", stirshaken_vs_verify_x509_cert_path ? "with X509 cert path check" : "without X509 cert path check");
349
+		stir_shaken_vs_destroy(&vs);
350
+	}
351
+
352
+	if ((vs && !stirshaken_vs_verify_x509_cert_path) || !vs) {
353
+		LM_WARN("A complete Shaken check with X509 certificate path verification will not be available (stirshaken_check_identity). "
354
+				"Only checks of PASSporT decoding will be performed via stirshaken_check_identity_with_key() "
355
+				"and stirshaken_check_identity_with_cert(). If you want complete check with downloaded certificate and/or X509 cert path verification "
356
+				"then please set @vs_verify_x509_cert_path param to 1 and configure @vs_ca_dir (and optionally @vs_crl_dir)\n");
357
+	}
358
+
359
+	return 0;
360
+}
361
+
362
+/**
363
+ * @brief Initialize async module children
364
+ */
365
+static int child_init(int rank)
366
+{
367
+	LM_INFO("mod stirshaken child init\n");
368
+	return 0;
369
+}
370
+
371
+static void mod_destroy(void)
372
+{
373
+	LM_INFO("mod stirshaken destroy\n");
374
+	stir_shaken_as_destroy(&as);
375
+	stir_shaken_vs_destroy(&vs);
376
+	stir_shaken_deinit();
377
+	return;
378
+}
379
+
380
+static int stirshaken_handle_cache(stir_shaken_context_t *ss, stir_shaken_passport_t *passport, stir_shaken_cert_t *cert)
381
+{
382
+	if (!passport || !cert)
383
+		return -1;
384
+
385
+	LM_DBG("Handling certificate cache...\n");
386
+
387
+	if (!stirshaken_vs_cache_dir.len) {
388
+		LM_ERR("Cache dir not set\n");
389
+		return -1;
390
+	}
391
+
392
+	if (!ss->cert_fetched_from_cache) {
393
+
394
+		// save certificate to cache with url as a key 
395
+		char cert_full_path[STIR_SHAKEN_BUFLEN] = { 0 };
396
+		const char *x5u = stir_shaken_passport_get_header(ss, passport, "x5u");
397
+
398
+		if (stir_shaken_zstr(x5u)) {
399
+
400
+			// This should never happen as stir_shaken_sih_verify returns error in such case
401
+
402
+			LM_ERR("PASSporT has no x5u\n");
403
+			return -1;
404
+		}
405
+
406
+		if (-1 == get_cert_name_hashed(x5u, cert_full_path, STIR_SHAKEN_BUFLEN)) {
407
+			LM_ERR("Cannot get cert name hashed\n");
408
+			return -1;
409
+		}
410
+
411
+		LM_DBG("Checking for presence of expired version of freshly downloaded certificate %s in cache (looking for name: %s)\n", x5u, cert_full_path);
412
+
413
+		if (STIR_SHAKEN_STATUS_OK == stir_shaken_file_exists(cert_full_path)) {
414
+
415
+			LM_DBG("Expired version of certificate %s found in cache (with name: %s). Removing it...\n", x5u, cert_full_path);
416
+
417
+			if (STIR_SHAKEN_STATUS_OK != stir_shaken_file_remove(cert_full_path)) {
418
+				LM_ERR("Couldn't remove certificate %s from cache\n", cert_full_path);
419
+				return -1;
420
+			}
421
+		}
422
+
423
+		LM_DBG("Saving fresh certificate %s in cache (with name: %s)...\n", x5u, cert_full_path);
424
+
425
+		if (STIR_SHAKEN_STATUS_OK != stir_shaken_x509_to_disk(ss, cert->x, cert_full_path)) {
426
+			LM_ERR("Failed to write cert %s to disk (as: %s)", x5u, cert_full_path);
427
+		}
428
+
429
+	} else {
430
+		LM_DBG("Certificate was fetched from cache, so skipping saving it\n");
431
+	}
432
+
433
+	return 0;
434
+}
435
+
436
+#define STIRSHAKEN_HDR_IDENTITY "Identity"
437
+#define STIRSHAKEN_HDR_IDENTITY_LEN (sizeof(STIRSHAKEN_HDR_IDENTITY) - 1)
438
+
439
+static int ki_stirshaken_check_identity(sip_msg_t *msg)
440
+{
441
+	str ibody = STR_NULL;
442
+	hdr_field_t *hf = NULL;
443
+
444
+	stir_shaken_context_t ss = { 0 };
445
+	stir_shaken_passport_t *passport_out = NULL;
446
+	stir_shaken_cert_t *cert_out = NULL;
447
+
448
+	for (hf = msg->headers; hf; hf = hf->next) {
449
+		if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN
450
+				&& strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY,
451
+					STIRSHAKEN_HDR_IDENTITY_LEN) == 0)
452
+			break;
453
+	}
454
+
455
+	if (hf == NULL) {
456
+		LM_DBG("no identity header\n");
457
+		goto fail;
458
+	}
459
+
460
+	ibody = hf->body;
461
+
462
+	if (STIR_SHAKEN_STATUS_OK != stir_shaken_vs_sih_verify(&ss, vs, ibody.s, &cert_out, &passport_out)) {
463
+		LM_ERR("SIP Identity Header did not pass verification\n");
464
+		stirshaken_print_error_details(&ss);
465
+		goto fail;
466
+	}
467
+
468
+	if (stirshaken_vs_cache_certificates) {
469
+		stirshaken_handle_cache(&ss, passport_out, cert_out);
470
+	}
471
+
472
+	// Check that PASSporT applies to the current moment in time
473
+	if (STIR_SHAKEN_STATUS_OK != stir_shaken_passport_validate_iat_against_freshness(&ss, passport_out, stirshaken_vs_identity_expire_s)) {
474
+
475
+		stir_shaken_error_t error_code = 0;
476
+		stir_shaken_get_error(&ss, &error_code);
477
+
478
+		if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_FUTURE) {
479
+			LM_ERR("PASSporT not valid yet\n");
480
+		} else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_EXPIRED) {
481
+			LM_ERR("PASSporT expired\n");
482
+		} else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT) {
483
+			LM_ERR("PASSporT is missing @iat grant\n");
484
+		}
485
+
486
+		LM_ERR("PASSporT doesn't apply to the current moment in time\n");
487
+		goto fail;
488
+	}
489
+
490
+	if (stirshaken_vs_verify_x509_cert_path) {
491
+
492
+		LM_DBG("Running X509 certificate path verification\n");
493
+
494
+		if (!vs) {
495
+			LM_ERR("Verification Service not started\n");
496
+			goto fail;
497
+		}
498
+
499
+		if (STIR_SHAKEN_STATUS_OK != stir_shaken_verify_cert_path(&ss, cert_out, vs->store)) {
500
+			LM_ERR("Cert did not pass X509 path validation\n");
501
+			stirshaken_print_error_details(&ss);
502
+			goto fail;
503
+		}
504
+	}
505
+
506
+	LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check");
507
+	stir_shaken_passport_destroy(&passport_out);
508
+	stir_shaken_cert_destroy(&cert_out);
509
+	return 1;
510
+
511
+fail:
512
+	stir_shaken_passport_destroy(&passport_out);
513
+	stir_shaken_cert_destroy(&cert_out);
514
+	LM_ERR("identity check: fail\n");
515
+	return -1;
516
+}
517
+
518
+/**
519
+ * Verify SIP Identity Header (involves call from libstirshaken to cache_callback,
520
+ * wich will supply requested certificate from cache [if configured to do so]
521
+ * or will let libstirshaken to perform HTTP(s) GET request to download certificate).
522
+ * Verify a call with STIR-Shaken.
523
+ *
524
+ * This method checks if SIP Identity Header covers semantically valid PASSporT.
525
+ * This method consults cache_callback in an attempt to obtain certificate that is referenced in PASSporT's x5u header.
526
+ * This method checks if PASSporT verifies successfully with a public key retrieved from obtained certificate.
527
+ * Optionally (if Verification Service is configured to do so with stirshaken_vs_verify_x509_cert_path param set to 1)
528
+ * this method checks if certificate is trusted, by execution of X509 certificate path check.
529
+ * 
530
+ * Optionally:
531
+ * 		- retrieve PASSporT from SIP Identity Header
532
+ * 		- retrieve certificate referenced in PASSporT's x5u header
533
+ * 		- cache certificate
534
+ *
535
+ * Kamailio config usage example:
536
+ *
537
+ *		stirshaken_check_identity();
538
+ */
539
+static int w_stirshaken_check_identity(sip_msg_t *msg, char *str1, char *str2)
540
+{
541
+	LM_INFO("identity check\n");
542
+
543
+	if (!vs) {
544
+		LM_ERR("Cannot perform identity check involving certificate downloading, caching, or X509 cert pach checking, "
545
+				"because Verification Service is not running. Please turn on and configure Verification Service, "
546
+				"otherwise only stirshaken_check_identity_with_cert() and stirshaken_check_identity_with_key() "
547
+				"methods will be available. Check log file for module's initialisation errors\n");
548
+		return -1;
549
+	}
550
+
551
+	return ki_stirshaken_check_identity(msg);
552
+}
553
+
554
+static int ki_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path)
555
+{
556
+	str ibody = STR_NULL;
557
+	hdr_field_t *hf = NULL;
558
+
559
+	stir_shaken_context_t ss = { 0 };
560
+	stir_shaken_passport_t *passport_out = NULL;
561
+	stir_shaken_cert_t cert = { 0 };
562
+
563
+	for (hf = msg->headers; hf; hf = hf->next) {
564
+		if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN
565
+				&& strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY,
566
+					STIRSHAKEN_HDR_IDENTITY_LEN) == 0)
567
+			break;
568
+	}
569
+
570
+	if (hf == NULL) {
571
+		LM_DBG("no identity header\n");
572
+		goto fail;
573
+	}
574
+
575
+	ibody = hf->body;
576
+
577
+	if (!(cert.x = stir_shaken_load_x509_from_file(&ss, cert_path))) {
578
+		LM_DBG("Cannot load X509 from file\n");
579
+		stirshaken_print_error_details(&ss);
580
+		goto fail;
581
+	}
582
+
583
+	if (STIR_SHAKEN_STATUS_OK != stir_shaken_sih_verify_with_cert(&ss, ibody.s, &cert, &passport_out)) {
584
+		LM_ERR("SIP Identity Header did not pass verification against certificate\n");
585
+		stirshaken_print_error_details(&ss);
586
+		goto fail;
587
+	}
588
+
589
+	// Check that PASSporT applies to the current moment in time
590
+	if (STIR_SHAKEN_STATUS_OK != stir_shaken_passport_validate_iat_against_freshness(&ss, passport_out, stirshaken_vs_identity_expire_s)) {
591
+
592
+		stir_shaken_error_t error_code = 0;
593
+		stir_shaken_get_error(&ss, &error_code);
594
+
595
+		if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_FUTURE) {
596
+			LM_ERR("PASSporT not valid yet\n");
597
+		} else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_EXPIRED) {
598
+			LM_ERR("PASSporT expired\n");
599
+		} else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT) {
600
+			LM_ERR("PASSporT is missing @iat grant\n");
601
+		}
602
+
603
+		LM_ERR("PASSporT doesn't apply to the current moment in time\n");
604
+		goto fail;
605
+	}
606
+
607
+	LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check");
608
+
609
+	stir_shaken_passport_destroy(&passport_out);
610
+	stir_shaken_cert_deinit(&cert);
611
+	return 1;
612
+
613
+fail:
614
+	stir_shaken_passport_destroy(&passport_out);
615
+	stir_shaken_cert_deinit(&cert);
616
+	LM_ERR("identity check: fail\n");
617
+	return -1;
618
+}
619
+
620
+/**
621
+ * Verify SIP Identity Header against specified certificate (does not involve HTTP(s) GET request).
622
+ * WARNING:
623
+ * This method only checks if SIP Identity Header was signed by a key from certificate given as argument.
624
+ * This method doesn't attempt to obtain certificate referenced in PASSporT (but PASSporT should be checked with key corresponding to that certificate).
625
+ * Therefore it is possible that this check will be successful, while PASSporT is not valid (could be signed with key that doesn't match certificate referenced in x5u header).
626
+ * If you want a complete Shaken check or if you are not sure what you're doing, then you should execute w_stirshaken_check_identity() instead
627
+ * (and configure Verification Service to perform X509 certificate path verification with stirshaken_vs_verify_x509_cert_path param set to 1).
628
+ *
629
+ * Kamailio config usage example:
630
+ *
631
+ *		stirshaken_check_identity_with_cert("/path/to/cert");
632
+ */
633
+static int w_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path, char *str2)
634
+{
635
+	str keyval = STR_NULL;
636
+
637
+	LM_INFO("identity check using certificate\n");
638
+
639
+	if(fixup_get_svalue(msg, (gparam_t*)cert_path, &keyval)<0) {
640
+		LM_ERR("failed to get certificate path parameter\n");
641
+		return -1;
642
+	}
643
+
644
+	return ki_stirshaken_check_identity_with_cert(msg, cert_path);
645
+}
646
+
647
+static int ki_stirshaken_check_identity_with_key(sip_msg_t *msg, str *keypath)
648
+{
649
+	str ibody = STR_NULL;
650
+	hdr_field_t *hf = NULL;
651
+
652
+	stir_shaken_context_t ss = { 0 };
653
+	stir_shaken_passport_t *passport_out = NULL;
654
+	unsigned char key[STIR_SHAKEN_PUB_KEY_RAW_BUF_LEN] = { 0 };
655
+	uint32_t key_len = STIR_SHAKEN_PUB_KEY_RAW_BUF_LEN;
656
+
657
+	for (hf = msg->headers; hf; hf = hf->next) {
658
+		if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN
659
+				&& strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY,
660
+					STIRSHAKEN_HDR_IDENTITY_LEN) == 0)
661
+			break;
662
+	}
663
+
664
+	if (hf == NULL) {
665
+		LM_DBG("no identity header\n");
666
+		goto fail;
667
+	}
668
+
669
+	ibody = hf->body;
670
+
671
+	if (keypath && keypath->s) {
672
+
673
+		if (STIR_SHAKEN_STATUS_OK != stir_shaken_load_key_raw(&ss, keypath->s, key, &key_len)) {
674
+			LM_ERR("Failed to load private key\n");
675
+			stirshaken_print_error_details(&ss);
676
+			goto fail;
677
+		}
678
+	}
679
+
680
+	if (STIR_SHAKEN_STATUS_OK != stir_shaken_sih_verify_with_key(&ss, ibody.s, key, key_len, &passport_out)) {
681
+		LM_ERR("SIP Identity Header did not pass verification against key\n");
682
+		stirshaken_print_error_details(&ss);
683
+		goto fail;
684
+	}
685
+
686
+	// We can do something with PASSporT here or just realease it
687
+	stir_shaken_passport_destroy(&passport_out);
688
+
689
+	LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check");
690
+	return 1;
691
+
692
+fail:
<