aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc/gsm_04_08.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmsc/gsm_04_08.c')
-rw-r--r--src/libmsc/gsm_04_08.c134
1 files changed, 88 insertions, 46 deletions
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index b942a0391..564e90bff 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -119,13 +119,16 @@ static bool classmark_is_r99(struct gsm_classmark *cm)
return classmark2_is_r99(cm->classmark2, cm->classmark2_len);
}
-/* Determine if the given CLASSMARK (1/2/3) value permits a given A5/n cipher */
-static bool classmark_supports_a5(const struct gsm_classmark *cm, uint8_t a5)
+/* Determine if the given CLASSMARK (1/2/3) value permits a given A5/n cipher.
+ * Return 1 when the given A5/n is permitted, 0 when not, and negative if the respective MS CLASSMARK is
+ * not known, where the negative number indicates the classmark type: -2 means Classmark 2 is not
+ * available. */
+static int classmark_supports_a5(const struct gsm_classmark *cm, uint8_t a5)
{
switch (a5) {
case 0:
/* all phones must implement A5/0, see 3GPP TS 43.020 4.9 */
- return true;
+ return 1;
case 1:
/* 3GPP TS 43.020 4.9 requires A5/1 to be suppored by all phones and actually states:
* "The network shall not provide service to an MS which indicates that it does not
@@ -134,25 +137,24 @@ static bool classmark_supports_a5(const struct gsm_classmark *cm, uint8_t a5)
/* See 3GPP TS 24.008 10.5.1.7 */
if (!cm->classmark1_set) {
DEBUGP(DMSC, "CLASSMARK 1 unknown, assuming MS supports A5/1\n");
- return true;
+ return -1;
} else {
if (cm->classmark1.a5_1)
- return false; /* Inverted logic for this bit! */
+ return 0; /* Inverted logic for this bit! */
else
- return true;
+ return 1;
}
break;
case 2:
case 3:
/* See 3GPP TS 24.008 10.5.1.6 */
if (cm->classmark2_len < 3) {
- DEBUGP(DMSC, "CLASSMARK 2 unknown, assuming MS doesn't support A5/%u\n", a5);
- return false;
+ return -2;
} else {
if (cm->classmark2[2] & (1 << (a5-2)))
- return true;
+ return 1;
else
- return false;
+ return 0;
}
break;
case 4:
@@ -161,13 +163,12 @@ static bool classmark_supports_a5(const struct gsm_classmark *cm, uint8_t a5)
case 7:
/* See 3GPP TS 24.008 10.5.1.7 */
if (cm->classmark3_len < 1) {
- DEBUGP(DMSC, "CLASSMARK 3 unknown, assuming MS doesn't support A5/%u\n", a5);
- return false;
+ return -3;
} else {
if (cm->classmark3[0] & (1 << (a5-4)))
- return true;
+ return 1;
else
- return false;
+ return 0;
}
break;
default:
@@ -1592,6 +1593,78 @@ static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum gsm48_reject_value ca
osmo_static_assert(sizeof(((struct gsm0808_encrypt_info*)0)->key) >= sizeof(((struct osmo_auth_vector*)0)->kc),
gsm0808_encrypt_info_key_fits_osmo_auth_vec_kc);
+int msc_geran_set_cipher_mode(struct gsm_subscriber_connection *conn, bool umts_aka, bool retrieve_imeisv)
+{
+ struct gsm_network *net = conn->network;
+ struct gsm0808_encrypt_info ei;
+ int i, j = 0;
+ int request_classmark = 0;
+ int request_classmark_for_a5_n = 0;
+ struct gsm_auth_tuple *tuple = conn->vsub->last_tuple;
+
+ if (!conn || !conn->vsub || !conn->vsub->last_tuple) {
+ /* This should really never happen, because we checked this in msc_vlr_set_ciph_mode()
+ * already. */
+ LOGP(DMM, LOGL_ERROR, "Internal error: missing state during Ciphering Mode Command\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 8; i++) {
+ int supported;
+
+ /* A5/n permitted by osmo-msc.cfg? */
+ if (!(net->a5_encryption_mask & (1 << i)))
+ continue;
+
+ /* A5/n supported by MS? */
+ supported = classmark_supports_a5(&conn->vsub->classmark, i);
+ if (supported == 1) {
+ ei.perm_algo[j++] = vlr_ciph_to_gsm0808_alg_id(i);
+ /* A higher A5/n is supported, so no need to request a Classmark
+ * for support of a lesser A5/n. */
+ request_classmark = 0;
+ } else if (supported < 0) {
+ request_classmark = -supported;
+ request_classmark_for_a5_n = i;
+ }
+ }
+ ei.perm_algo_len = j;
+
+ if (request_classmark) {
+ /* The highest A5/n as from osmo-msc.cfg might be available, but we are
+ * still missing the Classmark information for that from the MS. First
+ * ask for that. */
+ LOGP(DMM, LOGL_DEBUG, "%s: to determine whether A5/%d is supported,"
+ " first ask for a Classmark Update to obtain Classmark %d\n",
+ vlr_subscr_name(conn->vsub), request_classmark_for_a5_n,
+ request_classmark);
+
+ return msc_classmark_request_then_cipher_mode_cmd(conn, umts_aka, retrieve_imeisv);
+ }
+
+ if (ei.perm_algo_len == 0) {
+ LOGP(DMM, LOGL_ERROR, "%s: cannot start ciphering, no intersection "
+ "between MSC-configured and MS-supported A5 algorithms\n",
+ vlr_subscr_name(conn->vsub));
+ return -ENOTSUP;
+ }
+
+ DEBUGP(DMM, "-> CIPHER MODE COMMAND %s\n", vlr_subscr_name(conn->vsub));
+
+ tuple = conn->vsub->last_tuple;
+
+ /* 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
+ * necessarily a match for the UMTS AKA tokens. */
+ if (umts_aka)
+ osmo_auth_c3(ei.key, tuple->vec.ck, tuple->vec.ik);
+ else
+ memcpy(ei.key, tuple->vec.kc, sizeof(tuple->vec.kc));
+ ei.key_len = sizeof(tuple->vec.kc);
+
+ return a_iface_tx_cipher_mode(conn, &ei, retrieve_imeisv);
+}
+
/* VLR asks us to start using ciphering.
* (Keep non-static to allow regression testing on this function.) */
int msc_vlr_set_ciph_mode(void *msc_conn_ref,
@@ -1620,38 +1693,7 @@ int msc_vlr_set_ciph_mode(void *msc_conn_ref,
switch (conn->via_ran) {
case RAN_GERAN_A:
- 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;
-
- for (i = 0; i < 8; i++) {
- if (net->a5_encryption_mask & (1 << i) &&
- classmark_supports_a5(&conn->vsub->classmark, i))
- ei.perm_algo[j++] = vlr_ciph_to_gsm0808_alg_id(i);
- }
- ei.perm_algo_len = j;
-
- if (ei.perm_algo_len == 0) {
- LOGP(DMM, LOGL_ERROR, "%s: cannot start ciphering, no intersection "
- "between MSC-configured and MS-supported A5 algorithms\n",
- vlr_subscr_name(conn->vsub));
- return -ENOTSUP;
- }
-
- /* 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
- * necessarily a match for the UMTS AKA tokens. */
- if (umts_aka)
- osmo_auth_c3(ei.key, tuple->vec.ck, tuple->vec.ik);
- else
- memcpy(ei.key, tuple->vec.kc, sizeof(tuple->vec.kc));
- ei.key_len = sizeof(tuple->vec.kc);
-
- return a_iface_tx_cipher_mode(conn, &ei, retrieve_imeisv);
- }
+ return msc_geran_set_cipher_mode(conn, umts_aka, retrieve_imeisv);
case RAN_UTRAN_IU:
#ifdef BUILD_IU