libmsc/gsm_04_08.c: fix: verify MI before calling vlr_subscr_rx_id_resp()

During the last congress, we have noticed that OsmoMSC crashes
on receipt of malformed MM Identity Response messages:

  BSSAP
      Message Type: Direct Transfer (0x01)
      Data Link Connection Identifier
          00.. .... = Control Channel: not further specified (0x0)
          ..00 0... = Spare: 0x0
          .... .000 = SAPI: RR/MM/CC (0x0)
      Length: 11
  GSM A-I/F DTAP - Identity Response
      Protocol Discriminator: Mobility Management messages (5)
          .... 0101 = Protocol discriminator: Mobility Management messages (0x5)
          0000 .... = Skip Indicator: No indication of selected PLMN (0)
      01.. .... = Sequence number: 1
      ..01 1001 = DTAP Mobility Management Message Type: Identity Response (0x19)
      Mobile Identity - Format Unknown
          Length: 8
          .... 1... = Odd/even indication: Odd number of identity digits
          .... .111 = Mobile Identity Type: Unknown (7)  <-- This makes OsmoMSC crash
              [Expert Info (Warning/Protocol): Unknown format 7]
                  [Unknown format 7]
                  [Severity level: Warning]
                  [Group: Protocol]

The value '111'B is not a valid Mobile Identity type, and shall be
considered as reserved according to 3GPP TS 24.008, section 10.5.1.4.
Later on it was discovered that '000'B also crashes OsmoMSC in the same way.

The crash itself is provoked by OSMO_ASSERT(0) in vlr_subscr_rx_id_resp().
Let's keep that assert in there, and make sure that:

  - on receipt of MM Identity Response, Mobile Identity type
    matches the one in MM Identity Request;

  - on receipt of RR Ciphering Mode Complete, Mobile Identity
    contains IMEI(SV) if present.

Change-Id: Ica4c90b8eb4d90325313c6eb400fa4a6bc5df825
TTCN-3 test case: I62f23355eb91df2edf9dc837c928cb86b530b743
Fixes: OS#4340
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index 750c766..b284ccd 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -182,6 +182,7 @@
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	uint8_t *mi = gh->data+1;
 	uint8_t mi_len = gh->data[0];
+	uint8_t mi_type;
 	struct vlr_subscr *vsub = msc_a_vsub(msc_a);
 
 	if (!vsub) {
@@ -190,6 +191,28 @@
 		return -EINVAL;
 	}
 
+	/* There muct be at least one octet with MI type */
+	if (!mi_len) {
+		LOGP(DMM, LOGL_NOTICE, "MM Identity Response contains "
+				       "malformed Mobile Identity\n");
+		return -EINVAL;
+	}
+
+	/* Make sure we got what we expected */
+	mi_type = mi[0] & GSM_MI_TYPE_MASK;
+	if (mi_type == GSM_MI_TYPE_NONE) {
+		LOGP(DMM, LOGL_NOTICE, "MM Identity Response contains no identity, "
+				       "perhaps the MS has no Mobile Identity type %s?\n",
+				       gsm48_mi_type_name(msc_a->mm_id_req_type));
+		return -EINVAL;
+	} else if (mi_type != msc_a->mm_id_req_type) {
+		LOGP(DMM, LOGL_NOTICE, "MM Identity Response contains unexpected "
+				       "Mobile Identity type %s (extected %s)\n",
+				       gsm48_mi_type_name(mi_type),
+				       gsm48_mi_type_name(msc_a->mm_id_req_type));
+		return -EINVAL;
+	}
+
 	DEBUGP(DMM, "IDENTITY RESPONSE: MI=%s\n", osmo_mi_name(mi, mi_len));
 
 	osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
@@ -1182,8 +1205,17 @@
 	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	mi = TLVP_GET(&tp, GSM48_IE_MOBILE_ID);
 
+	/* IMEI(SV) is optional for this message */
 	if (!mi)
 		return 0;
+	if (!mi->len)
+		return -EINVAL;
+	if ((mi->val[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMEISV) {
+		LOGP(DMM, LOGL_ERROR, "RR Ciphering Mode Complete contains "
+				      "unexpected Mobile Identity type %s\n",
+				      gsm48_mi_type_name(mi->val[0] & GSM_MI_TYPE_MASK));
+		return -EINVAL;
+	}
 
 	LOG_MSC_A(msc_a, LOGL_DEBUG, "RR Ciphering Mode Complete contains Mobile Identity: %s\n",
 		  osmo_mi_name(mi->val, mi->len));
@@ -1287,6 +1319,10 @@
 static int msc_vlr_tx_id_req(void *msc_conn_ref, uint8_t mi_type)
 {
 	struct msc_a *msc_a = msc_conn_ref;
+
+	/* Store requested MI type, so we can check the response */
+	msc_a->mm_id_req_type = mi_type;
+
 	return mm_tx_identity_req(msc_a, mi_type);
 }