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.c57
1 files changed, 38 insertions, 19 deletions
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index 280c341d9..418ee2ab7 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -700,28 +700,47 @@ accept_reuse:
int gsm48_rx_mm_serv_req(struct msc_a *msc_a, struct msgb *msg)
{
struct gsm_network *net = msc_a_net(msc_a);
- uint8_t mi_type;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_service_request *req =
- (struct gsm48_service_request *)gh->data;
- /* unfortunately in Phase1 the classmark2 length is variable */
- uint8_t classmark2_len = gh->data[1];
- uint8_t *classmark2_buf = gh->data+2;
- struct gsm48_classmark2 *cm2 = (void*)classmark2_buf;
- uint8_t *mi_p = classmark2_buf + classmark2_len;
- uint8_t mi_len = *mi_p;
- uint8_t *mi = mi_p + 1;
+ struct gsm48_hdr *gh;
+ struct gsm48_service_request *req;
+ struct gsm48_classmark2 *cm2;
+ uint8_t *cm2_buf, cm2_len;
+ uint8_t *mi_buf, mi_len;
+ uint8_t *mi, mi_type;
bool is_utran;
struct vlr_subscr *vsub;
- if (msg->data_len < sizeof(struct gsm48_service_request*)) {
+ /* Make sure that both header and CM Service Request fit into the buffer */
+ if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*req)) {
LOG_MSC_A(msc_a, LOGL_ERROR, "Rx CM SERVICE REQUEST: wrong message size (%u < %zu)\n",
- msg->data_len, sizeof(struct gsm48_service_request*));
+ msgb_l3len(msg), sizeof(*gh) + sizeof(*req));
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_INCORRECT_MESSAGE);
}
- if (msg->data_len < req->mi_len + 6) {
- LOG_MSC_A(msc_a, LOGL_ERROR, "Rx CM SERVICE REQUEST: message does not fit in packet\n");
+ gh = (struct gsm48_hdr *) msgb_l3(msg);
+ req = (struct gsm48_service_request *) gh->data;
+
+ /* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot
+ * just use gsm48_service_request struct, and need to parse it manually. */
+ cm2_len = gh->data[1];
+ cm2_buf = gh->data + 2;
+ cm2 = (struct gsm48_classmark2 *) cm2_buf;
+
+ /* Prevent buffer overrun: check the length of Classmark2 */
+ if (cm2_buf + cm2_len > msg->tail) {
+ LOG_MSC_A(msc_a, LOGL_ERROR, "Rx CM SERVICE REQUEST: Classmark2 "
+ "length=%u is too big\n", cm2_len);
+ return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_INCORRECT_MESSAGE);
+ }
+
+ /* MI (Mobile Identity) LV follows the Classmark2 */
+ mi_buf = cm2_buf + cm2_len;
+ mi_len = mi_buf[0];
+ mi = mi_buf + 1;
+
+ /* Prevent buffer overrun: check the length of MI */
+ if (mi_buf + mi_len > msg->tail) {
+ LOG_MSC_A(msc_a, LOGL_ERROR, "Rx CM SERVICE REQUEST: Mobile Identity "
+ "length=%u is too big\n", cm2_len);
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_INCORRECT_MESSAGE);
}
@@ -759,9 +778,9 @@ int gsm48_rx_mm_serv_req(struct msc_a *msc_a, struct msgb *msg)
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_SRV_OPT_NOT_SUPPORTED);
if (msc_a_is_accepted(msc_a))
- return cm_serv_reuse_conn(msc_a, mi_p, req->cm_service_type);
+ return cm_serv_reuse_conn(msc_a, mi_buf, req->cm_service_type);
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, mi_p);
+ osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, mi_buf);
msc_a_get(msc_a, msc_a_cm_service_type_to_use(req->cm_service_type));
@@ -775,7 +794,7 @@ int gsm48_rx_mm_serv_req(struct msc_a *msc_a, struct msgb *msg)
is_utran || net->authentication_required,
is_utran || net->a5_encryption_mask > 0x01,
req->cipher_key_seq,
- osmo_gsm48_classmark2_is_r99(cm2, classmark2_len),
+ osmo_gsm48_classmark2_is_r99(cm2, cm2_len),
is_utran);
/* From vlr_proc_acc_req() we expect an implicit dispatch of PR_ARQ_E_START we expect
@@ -787,7 +806,7 @@ int gsm48_rx_mm_serv_req(struct msc_a *msc_a, struct msgb *msg)
}
vsub->classmark.classmark2 = *cm2;
- vsub->classmark.classmark2_len = classmark2_len;
+ vsub->classmark.classmark2_len = cm2_len;
return 0;
}