Permit a set of multiple different A5 ciphers

So far, the administrator had to pick one particular cipher which
would then be used throughout all subscribers/phones. This is a bit
impractical, as e.g. not all phones support A5/3.  Extend the VTY
command syntax in a backwards-compatible way to permit for multiple
ciphers.

NOTE: Like the previous code, OsmoMSC does *not yet check* whether
the configured cipher is compatible with the MS capabilities as
reported in CLASSMARK!  The network hence might choose an algorithm
not supported by the phone.  Fixing this is subject to another patch.

Closes: OS#2460
Change-Id: I79a4e2892eb5fbecc3d84e11dceffb7149db264b
diff --git a/src/libcommon-cs/common_cs.c b/src/libcommon-cs/common_cs.c
index 41c456e..170b62a 100644
--- a/src/libcommon-cs/common_cs.c
+++ b/src/libcommon-cs/common_cs.c
@@ -56,6 +56,8 @@
 
 	net->country_code = country_code;
 	net->network_code = network_code;
+	/* Permit a compile-time default of A5/3 and A5/1 */
+	net->a5_encryption_mask = (1 << 3) | (1 << 1);
 
 	/* Use 30 min periodic update interval as sane default */
 	net->t3212 = 5;
diff --git a/src/libcommon-cs/common_cs_vty.c b/src/libcommon-cs/common_cs_vty.c
index 4754531..8c9f127 100644
--- a/src/libcommon-cs/common_cs_vty.c
+++ b/src/libcommon-cs/common_cs_vty.c
@@ -155,15 +155,20 @@
 
 DEFUN(cfg_net_encryption,
       cfg_net_encryption_cmd,
-      "encryption a5 (0|1|2|3)",
+      "encryption a5 <0-3> [<0-3>] [<0-3>] [<0-3>]",
 	"Encryption options\n"
-	"A5 encryption\n" "A5/0: No encryption\n"
-	"A5/1: Encryption\n" "A5/2: Export-grade Encryption\n"
-	"A5/3: 'New' Secure Encryption\n")
+	"GSM A5 Air Interface Encryption\n"
+	"A5/n Algorithm Number\n"
+	"A5/n Algorithm Number\n"
+	"A5/n Algorithm Number\n"
+	"A5/n Algorithm Number\n")
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+	unsigned int i;
 
-	gsmnet->a5_encryption = atoi(argv[0]);
+	gsmnet->a5_encryption_mask = 0;
+	for (i = 0; i < argc; i++)
+		gsmnet->a5_encryption_mask |= (1 << atoi(argv[i]));
 
 	return CMD_SUCCESS;
 }
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index c37aeb7..3574583 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -339,7 +339,7 @@
 				net->vlr, conn, vlr_lu_type, tmsi, imsi,
 				&old_lai, &new_lai,
 				is_utran || conn->network->authentication_required,
-				is_utran || conn->network->a5_encryption,
+				is_utran || conn->network->a5_encryption_mask > 0x01,
 				classmark_is_r99(&conn->classmark),
 				is_utran,
 				net->vlr->cfg.assign_tmsi);
@@ -723,7 +723,7 @@
 			 net->vlr, conn,
 			 VLR_PR_ARQ_T_CM_SERV_REQ, mi-1, &lai,
 			 is_utran || conn->network->authentication_required,
-			 is_utran || conn->network->a5_encryption,
+			 is_utran || conn->network->a5_encryption_mask > 0x01,
 			 classmark_is_r99(&conn->classmark),
 			 is_utran);
 
@@ -1127,7 +1127,7 @@
 			 net->vlr, conn,
 			 VLR_PR_ARQ_T_PAGING_RESP, mi_lv, &lai,
 			 is_utran || conn->network->authentication_required,
-			 is_utran || conn->network->a5_encryption,
+			 is_utran || conn->network->a5_encryption_mask > 0x01,
 			 classmark_is_r99(&conn->classmark),
 			 is_utran);
 
@@ -3447,10 +3447,15 @@
 		DEBUGP(DMM, "-> CIPHER MODE COMMAND %s\n",
 		       vlr_subscr_name(conn->vsub));
 		{
+			struct gsm_network *net = conn->network;
 			struct gsm0808_encrypt_info ei;
+			int i, j = 0;
 
-			ei.perm_algo[0] = vlr_ciph_to_gsm0808_alg_id(conn->network->a5_encryption);
-			ei.perm_algo_len = 1;
+			for (i = 0; i < 8; i++) {
+				if (net->a5_encryption_mask & (1 << i))
+					ei.perm_algo[j++] = vlr_ciph_to_gsm0808_alg_id(i);
+			}
+			ei.perm_algo_len = j;
 
 			/* In case of UMTS AKA, the Kc for ciphering must be derived from the 3G auth
 			 * tokens.  tuple->vec.kc was calculated from the GSM algorithm and is not
diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c
index 82608c6..faf17ec 100644
--- a/src/libmsc/msc_vty.c
+++ b/src/libmsc/msc_vty.c
@@ -1,7 +1,7 @@
 /* MSC interface to quagga VTY */
 /* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
  * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2017 by Harald Welte <laforge@gnumonks.org>
  * (C) 2009-2011 by Holger Hans Peter Freyther
  * All Rights Reserved
  *
@@ -160,6 +160,7 @@
 static int config_write_net(struct vty *vty)
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+	int i;
 
 	vty_out(vty, "network%s", VTY_NEWLINE);
 	vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE);
@@ -169,7 +170,12 @@
 	vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
 	vty_out(vty, " location updating reject cause %u%s",
 		gsmnet->reject_cause, VTY_NEWLINE);
-	vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
+	vty_out(vty, " encryption a5");
+	for (i = 0; i < 8; i++) {
+		if (gsmnet->a5_encryption_mask & (1 << i))
+			vty_out(vty, " %u", i);
+	}
+	vty_out(vty, "%s", VTY_NEWLINE);
 	vty_out(vty, " authentication %s%s",
 		gsmnet->authentication_required ? "required" : "optional", VTY_NEWLINE);
 	vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),