Change in osmo-sgsn[master]: add support for multiple encryption algorithms and a5/4

laforge gerrit-no-reply at lists.osmocom.org
Fri Jun 18 12:30:16 UTC 2021


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-sgsn/+/24304 )

Change subject: add support for multiple encryption algorithms and a5/4
......................................................................

add support for multiple encryption algorithms and a5/4

Change-Id: Ie6700c4e9d2df1eb5fde1b971e287b62668cc2de
Related: SYS#5324
---
M include/osmocom/sgsn/gprs_sgsn.h
M include/osmocom/sgsn/sgsn.h
M src/sgsn/gprs_gmm.c
M src/sgsn/gprs_llc.c
M src/sgsn/gprs_sgsn.c
M src/sgsn/sgsn_vty.c
M tests/osmo-sgsn_test-nodes.vty
M tests/sgsn/sgsn_test.c
8 files changed, 118 insertions(+), 19 deletions(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved



diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h
index 289e0c4..c176494 100644
--- a/include/osmocom/sgsn/gprs_sgsn.h
+++ b/include/osmocom/sgsn/gprs_sgsn.h
@@ -175,6 +175,7 @@
 	/* Iu: CK, IK, KSI */
 	/* CKSN */
 	enum gprs_ciph_algo	ciph_algo;
+	uint8_t ue_cipher_mask;
 	/* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */
 	uint8_t ac_ref_nr_used;
 
diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h
index b686c7c..5b29873 100644
--- a/include/osmocom/sgsn/sgsn.h
+++ b/include/osmocom/sgsn/sgsn.h
@@ -76,7 +76,7 @@
 	struct gprs_ns2_inst *nsi;
 
 	enum sgsn_auth_policy auth_policy;
-	enum gprs_ciph_algo cipher;
+	uint8_t cipher_support_mask;
 	struct llist_head imsi_acl;
 
 	struct sockaddr_in gsup_server_addr;
diff --git a/src/sgsn/gprs_gmm.c b/src/sgsn/gprs_gmm.c
index c5e927a..1f68558 100644
--- a/src/sgsn/gprs_gmm.c
+++ b/src/sgsn/gprs_gmm.c
@@ -445,6 +445,17 @@
 	return false;
 }
 
+static enum gprs_ciph_algo gprs_ms_net_select_best_gea(uint8_t net_mask, uint8_t ms_mask) {
+	uint8_t common_mask = net_mask & ms_mask;
+	uint8_t r = 0;
+
+	while (common_mask >>= 1) {
+		r++;
+	}
+
+	return r;
+}
+
 /* 3GPP TS 24.008 § 9.4.9: Authentication and Ciphering Request */
 int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
 				      const struct osmo_auth_vector *vec,
@@ -1147,6 +1158,21 @@
 
 }
 
+static uint8_t gprs_ms_net_cap_gea_mask(const uint8_t *ms_net_cap, uint8_t cap_len)
+{
+	uint8_t mask = (1 << GPRS_ALGO_GEA0);
+	mask |= (0x80 & ms_net_cap[0]) ? (1 << GPRS_ALGO_GEA1) : 0;
+
+	if (cap_len < 2)
+		return mask;
+
+	/* extended GEA bits start from 2nd bit of the next byte */
+	mask |= (0x40 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA2) : 0;
+	mask |= (0x20 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA3) : 0;
+	mask |= (0x10 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA4) : 0;
+	return mask;
+}
+
 /* 3GPP TS 24.008 § 9.4.1 Attach request */
 static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
 				struct gprs_llc_llme *llme)
@@ -1290,15 +1316,27 @@
 		ctx->ms_radio_access_capa.len);
 	ctx->ms_network_capa.len = msnc_len;
 	memcpy(ctx->ms_network_capa.buf, msnc, msnc_len);
-	if (!gprs_ms_net_cap_gea_supported(ctx->ms_network_capa.buf, msnc_len,
-					   ctx->ciph_algo)) {
+
+	ctx->ue_cipher_mask = gprs_ms_net_cap_gea_mask(ctx->ms_network_capa.buf, msnc_len);
+
+	if (!(ctx->ue_cipher_mask & sgsn->cfg.cipher_support_mask)) {
 		reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
 		LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI "
-			  "%s because MS do not support required %s "
-			  "encryption\n", mi_log_string,
-			  get_value_string(gprs_cipher_names,ctx->ciph_algo));
+			  "%s because MS do not support required encryption, mask UE:0x%02x NW:0x%02x \n",
+				  mi_log_string, ctx->ue_cipher_mask, sgsn->cfg.cipher_support_mask);
 		goto rejected;
 	}
+
+	/* just assume that everythig is fine if the phone offers a5/4:
+	 * it requires a valid umts security context which we can only have after
+	 * 1) IDENTITY REQUEST to know what to ask the HLR for
+	 * 2) and AUTHENTICATION AND CIPHERING REQUEST
+	 * ... but 2) already requires selecting a cipher mode.
+	 * So let's just assume we will have the auth data required to make it work.
+	 */
+
+	ctx->ciph_algo = gprs_ms_net_select_best_gea(ctx->ue_cipher_mask, sgsn->cfg.cipher_support_mask);
+
 #ifdef PTMSI_ALLOC
 	/* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */
 	ptmsi_update(ctx);
diff --git a/src/sgsn/gprs_llc.c b/src/sgsn/gprs_llc.c
index 4fbf211..eea1cec 100644
--- a/src/sgsn/gprs_llc.c
+++ b/src/sgsn/gprs_llc.c
@@ -42,6 +42,8 @@
 #include <osmocom/sgsn/gprs_sndcp_comp.h>
 #include <osmocom/sgsn/gprs_sndcp.h>
 
+#include <osmocom/crypt/kdf.h>
+
 const struct value_string gprs_llc_llme_state_names[] = {
 	{ GPRS_LLMS_UNASSIGNED,	"UNASSIGNED" },
 	{ GPRS_LLMS_ASSIGNED,	"ASSIGNED" },
@@ -1042,8 +1044,13 @@
 		llme->algo = mm->ciph_algo;
 		if (llme->cksn != mm->auth_triplet.key_seq &&
 		    mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) {
-			memcpy(llme->kc, mm->auth_triplet.vec.kc,
-			       gprs_cipher_key_length(mm->ciph_algo));
+
+			/* gea4 needs kc128 */
+			if (mm->ciph_algo == GPRS_ALGO_GEA4)
+				osmo_kdf_kc128(mm->auth_triplet.vec.ck, mm->auth_triplet.vec.ik, llme->kc);
+			else
+				memcpy(llme->kc, mm->auth_triplet.vec.kc, gprs_cipher_key_length(mm->ciph_algo));
+
 			llme->cksn = mm->auth_triplet.key_seq;
 		}
 	} else
diff --git a/src/sgsn/gprs_sgsn.c b/src/sgsn/gprs_sgsn.c
index f744257..304ecc5 100644
--- a/src/sgsn/gprs_sgsn.c
+++ b/src/sgsn/gprs_sgsn.c
@@ -293,11 +293,8 @@
 	memcpy(&ctx->ra, raid, sizeof(ctx->ra));
 	ctx->ran_type = MM_CTX_T_GERAN_Gb;
 	ctx->gb.tlli = tlli;
-	ctx->ciph_algo = sgsn->cfg.cipher;
 	osmo_fsm_inst_update_id_f(ctx->gb.mm_state_fsm, "%" PRIu32, tlli);
 
-	LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n",
-		  get_value_string(gprs_cipher_names, ctx->ciph_algo));
 	return ctx;
 }
 
diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c
index 3b2a04a..30bd864 100644
--- a/src/sgsn/sgsn_vty.c
+++ b/src/sgsn/sgsn_vty.c
@@ -206,6 +206,7 @@
 	struct apn_ctx *actx;
 	struct ares_addr_node *server;
 	struct sgsn_mme_ctx *mme;
+	int i;
 
 	vty_out(vty, "sgsn%s", VTY_NEWLINE);
 
@@ -236,10 +237,15 @@
 	for (server = sgsn->ares_servers; server; server = server->next)
 		vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
 
-	if (g_cfg->cipher != GPRS_ALGO_GEA0)
-		vty_out(vty, " encryption %s%s",
-			get_value_string(gprs_cipher_names, g_cfg->cipher),
-			VTY_NEWLINE);
+	if (g_cfg->cipher_support_mask != 0) {
+		vty_out(vty, " encryption gea");
+
+		for (i = 0; i < _GPRS_ALGO_NUM; i++)
+			if (g_cfg->cipher_support_mask >> i & 1)
+				vty_out(vty, " %u", i);
+
+		vty_out(vty, "%s", VTY_NEWLINE);
+	}
 	if (g_cfg->sgsn_ipa_name)
 		vty_out(vty, " gsup ipa-name %s%s", g_cfg->sgsn_ipa_name, VTY_NEWLINE);
 	if (g_cfg->gsup_server_addr.sin_addr.s_addr)
@@ -721,15 +727,19 @@
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_encrypt, cfg_encrypt_cmd,
+DEFUN_DEPRECATED(cfg_encrypt, cfg_encrypt_cmd,
       "encryption (GEA0|GEA1|GEA2|GEA3|GEA4)",
       "Set encryption algorithm for SGSN\n"
       "Use GEA0 (no encryption)\n"
       "Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n")
 {
 	enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]);
+
+	if (strcmp(argv[0], "gea") == 0)
+		return CMD_SUCCESS;
+
 	if (c != GPRS_ALGO_GEA0) {
-		if (!gprs_cipher_supported(c)) {
+		if (gprs_cipher_supported(c) <= 0) {
 			vty_out(vty, "%% cipher %s is unsupported in current version%s", argv[0], VTY_NEWLINE);
 			return CMD_WARNING;
 		}
@@ -741,7 +751,46 @@
 		}
 	}
 
-	g_cfg->cipher = c;
+	g_cfg->cipher_support_mask |= (1 << c);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_encrypt2, cfg_encrypt2_cmd,
+	"encryption gea <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]",
+	"Set encryption algorithms for SGSN\n"
+	"GPRS Encryption Algorithm\n"
+	"GEAn Algorithm Number\n"
+	"GEAn Algorithm Number\n"
+	"GEAn Algorithm Number\n"
+	"GEAn Algorithm Number\n"
+	"GEAn Algorithm Number\n")
+{
+	int i = 0;
+
+	g_cfg->cipher_support_mask = 0;
+	for (i = 0; i < argc; i++)
+		g_cfg->cipher_support_mask |= (1 << atoi(argv[i]));
+
+	for (i = 0; i < _GPRS_ALGO_NUM; i++) {
+		if (g_cfg->cipher_support_mask >> i & 1) {
+
+			if (i == GPRS_ALGO_GEA0)
+				continue;
+
+			if (gprs_cipher_supported(i) <= 0) {
+				vty_out(vty, "%% cipher %d is unsupported in current version%s", i, VTY_NEWLINE);
+				return CMD_ERR_INCOMPLETE;
+			}
+
+			if (!g_cfg->require_authentication) {
+				vty_out(vty, "%% unable to use encryption %s without authentication: please adjust auth-policy%s",
+					argv[i], VTY_NEWLINE);
+				return CMD_ERR_INCOMPLETE;
+			}
+
+		}
+	}
 
 	return CMD_SUCCESS;
 }
@@ -1640,7 +1689,11 @@
 	install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
 	install_element(SGSN_NODE, &cfg_auth_policy_cmd);
 	install_element(SGSN_NODE, &cfg_authentication_cmd);
+
+	/* order matters here: ensure we attempt to parse our new command first! */
+	install_element(SGSN_NODE, &cfg_encrypt2_cmd);
 	install_element(SGSN_NODE, &cfg_encrypt_cmd);
+
 	install_element(SGSN_NODE, &cfg_gsup_ipa_name_cmd);
 	install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
 	install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
@@ -1691,6 +1744,8 @@
 	/* make sure sgsn_vty_init() was called before this */
 	OSMO_ASSERT(g_cfg);
 
+	g_cfg->cipher_support_mask = 0x1; /* support GEA0 by default unless specific encryption config exists */
+
 	rc = vty_read_config_file(config_file, NULL);
 	if (rc < 0) {
 		fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
diff --git a/tests/osmo-sgsn_test-nodes.vty b/tests/osmo-sgsn_test-nodes.vty
index f889541..f2ed2dc 100644
--- a/tests/osmo-sgsn_test-nodes.vty
+++ b/tests/osmo-sgsn_test-nodes.vty
@@ -35,7 +35,7 @@
   imsi-acl (add|del) IMSI
   auth-policy (accept-all|closed|acl-only|remote)
   authentication (optional|required)
-  encryption (GEA0|GEA1|GEA2|GEA3|GEA4)
+  encryption gea <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]
   gsup ipa-name NAME
   gsup remote-ip A.B.C.D
   gsup remote-port <0-65535>
diff --git a/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c
index 63a7f3e..562ae84 100644
--- a/tests/sgsn/sgsn_test.c
+++ b/tests/sgsn/sgsn_test.c
@@ -48,6 +48,7 @@
 	.cfg = {
 		.gtp_statedir = "./",
 		.auth_policy = SGSN_AUTH_POLICY_CLOSED,
+		.cipher_support_mask = 0x1,
 	},
 };
 struct sgsn_instance *sgsn = &sgsn_inst;

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/24304
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-sgsn
Gerrit-Branch: master
Gerrit-Change-Id: Ie6700c4e9d2df1eb5fde1b971e287b62668cc2de
Gerrit-Change-Number: 24304
Gerrit-PatchSet: 7
Gerrit-Owner: Hoernchen <ewild at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-CC: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210618/307226a2/attachment.htm>


More information about the gerrit-log mailing list