/* * (C) 2010 by Andreas Eversberg * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include /* enable to get an empty list of forbidden PLMNs, even if stored on SIM. * if list is changed, the result is not written back to SIM */ //#define TEST_EMPTY_FPLMN static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg); static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg); static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg); /* * support */ char *gsm_check_imsi(const char *imsi) { int i; if (!imsi || strlen(imsi) != 15) return "IMSI must have 15 digits!"; for (i = 0; i < strlen(imsi); i++) { if (imsi[i] < '0' || imsi[i] > '9') return "IMSI must have digits 0 to 9 only!"; } return NULL; } static char *sim_decode_bcd(uint8_t *data, uint8_t length) { int i, j = 0; static char result[32], c; for (i = 0; i < (length << 1); i++) { if ((i & 1)) c = (data[i >> 1] >> 4); else c = (data[i >> 1] & 0xf); if (c == 0xf) break; result[j++] = c + '0'; if (j == sizeof(result) - 1) break; } result[j] = '\0'; return result; } /* * init/exit */ int gsm_subscr_init(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; memset(subscr, 0, sizeof(*subscr)); subscr->ms = ms; /* set TMSI / LAC invalid */ subscr->tmsi = 0xffffffff; subscr->lac = 0x0000; /* set key invalid */ subscr->key_seq = 7; /* any cell selection timer timeout */ subscr->any_timeout = ms->settings.any_timeout; /* init lists */ INIT_LLIST_HEAD(&subscr->plmn_list); INIT_LLIST_HEAD(&subscr->plmn_na); /* open SIM */ subscr->sim_handle_query = sim_open(ms, subscr_sim_query_cb); subscr->sim_handle_update = sim_open(ms, subscr_sim_update_cb); subscr->sim_handle_key = sim_open(ms, subscr_sim_key_cb); return 0; } int gsm_subscr_exit(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct llist_head *lh, *lh2; if (subscr->sim_handle_query) { sim_close(ms, subscr->sim_handle_query); subscr->sim_handle_query = 0; } if (subscr->sim_handle_update) { sim_close(ms, subscr->sim_handle_update); subscr->sim_handle_update = 0; } if (subscr->sim_handle_key) { sim_close(ms, subscr->sim_handle_key); subscr->sim_handle_key = 0; } /* flush lists */ llist_for_each_safe(lh, lh2, &subscr->plmn_list) { llist_del(lh); talloc_free(lh); } llist_for_each_safe(lh, lh2, &subscr->plmn_na) { llist_del(lh); talloc_free(lh); } return 0; } /* * test card */ /* Attach test card, no SIM must be currently attached */ int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, uint16_t lac, uint32_t tmsi, uint8_t imsi_attached) { struct gsm_settings *set = &ms->settings; struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; char *error; if (subscr->sim_valid) { LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " "is detached.\n"); return -EBUSY; } error = gsm_check_imsi(set->test_imsi); if (error) { LOGP(DMM, LOGL_ERROR, "%s\n", error); return -EINVAL; } /* reset subscriber */ gsm_subscr_exit(ms); gsm_subscr_init(ms); subscr->sim_type = GSM_SIM_TYPE_TEST; sprintf(subscr->sim_name, "test"); subscr->sim_valid = 1; if (imsi_attached && set->test_rplmn_valid) { subscr->imsi_attached = imsi_attached; subscr->ustate = GSM_SIM_U1_UPDATED; } else subscr->ustate = GSM_SIM_U2_NOT_UPDATED; subscr->acc_barr = set->test_barr; /* we may access barred cell */ subscr->acc_class = 0xffff; /* we have any access class */ subscr->plmn_valid = set->test_rplmn_valid; subscr->plmn_mcc = mcc; subscr->plmn_mnc = mnc; subscr->mcc = mcc; subscr->mnc = mnc; subscr->lac = lac; subscr->tmsi = tmsi; subscr->always_search_hplmn = set->test_always; subscr->t6m_hplmn = 1; /* try to find home network every 6 min */ strcpy(subscr->imsi, set->test_imsi); LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n", ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); if (subscr->plmn_valid) LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s 0x%04x" "(%s, %s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac, gsm_get_mcc(mcc), gsm_get_mnc(mcc, mnc)); else LOGP(DMM, LOGL_INFO, "-> Test card not registered\n"); if (subscr->imsi_attached) LOGP(DMM, LOGL_INFO, "-> Test card attached\n"); /* insert card */ nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); if (!nmsg) return -ENOMEM; gsm48_mmr_downmsg(ms, nmsg); return 0; } /* * sim card */ static int subscr_sim_iccid(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; strcpy(subscr->iccid, sim_decode_bcd(data, length)); sprintf(subscr->sim_name, "sim-%s", subscr->iccid); LOGP(DMM, LOGL_INFO, "received ICCID %s from SIM\n", subscr->iccid); return 0; } static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; char *imsi; /* get actual length */ if (length < 1) return -EINVAL; if (data[0] + 1 < length) { LOGP(DMM, LOGL_NOTICE, "invalid length = %d\n", length); return -EINVAL; } length = data[0]; /* decode IMSI, skip first digit (parity) */ imsi = sim_decode_bcd(data + 1, length); if (strlen(imsi) - 1 > GSM_IMSI_LENGTH - 1 || strlen(imsi) - 1 < 6) { LOGP(DMM, LOGL_NOTICE, "IMSI invalid length = %zu\n", strlen(imsi) - 1); return -EINVAL; } OSMO_STRLCPY_ARRAY(subscr->imsi, imsi + 1); LOGP(DMM, LOGL_INFO, "received IMSI %s from SIM\n", subscr->imsi); return 0; } static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm1111_ef_loci *loci; if (length < 11) return -EINVAL; loci = (struct gsm1111_ef_loci *) data; /* TMSI */ subscr->tmsi = ntohl(loci->tmsi); /* LAI */ gsm48_decode_lai_hex(&loci->lai, &subscr->mcc, &subscr->mnc, &subscr->lac); /* location update status */ switch (loci->lupd_status & 0x07) { case 0x00: subscr->ustate = GSM_SIM_U1_UPDATED; break; case 0x02: case 0x03: subscr->ustate = GSM_SIM_U3_ROAMING_NA; break; default: subscr->ustate = GSM_SIM_U2_NOT_UPDATED; } LOGP(DMM, LOGL_INFO, "received LOCI from SIM (mcc=%s mnc=%s lac=0x%04x " "U%d)\n", gsm_print_mcc(subscr->mcc), gsm_print_mnc(subscr->mnc), subscr->lac, subscr->ustate); return 0; } static int subscr_sim_msisdn(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm1111_ef_adn *adn; if (length < sizeof(*adn)) return -EINVAL; adn = (struct gsm1111_ef_adn *) (data + length - sizeof(*adn)); /* empty */ subscr->msisdn[0] = '\0'; if (adn->len_bcd <= 1) return 0; /* number */ if (((adn->ton_npi & 0x70) >> 4) == 1) strcpy(subscr->msisdn, "+"); if (((adn->ton_npi & 0x70) >> 4) == 2) strcpy(subscr->msisdn, "0"); strncat(subscr->msisdn, sim_decode_bcd(adn->number, adn->len_bcd - 1), sizeof(subscr->msisdn) - 2); LOGP(DMM, LOGL_INFO, "received MSISDN %s from SIM\n", subscr->msisdn); return 0; } static int subscr_sim_smsp(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm1111_ef_smsp *smsp; if (length < sizeof(*smsp)) return -EINVAL; smsp = (struct gsm1111_ef_smsp *) (data + length - sizeof(*smsp)); /* empty */ subscr->sms_sca[0] = '\0'; /* TS-Service Centre Address */ if (!(smsp->par_ind & 0x02) && smsp->ts_sca[0] <= 11) { if (((smsp->ts_sca[1] & 0x70) >> 4) == 1) strcpy(subscr->sms_sca, "+"); if (((smsp->ts_sca[1] & 0x70) >> 4) == 2) strcpy(subscr->sms_sca, "0"); gsm48_decode_bcd_number2(subscr->sms_sca + strlen(subscr->sms_sca), sizeof(subscr->sms_sca) - strlen(subscr->sms_sca), smsp->ts_sca, sizeof(smsp->ts_sca), 1); } LOGP(DMM, LOGL_INFO, "received SMSP from SIM (sca=%s)\n", subscr->sms_sca); return 0; } static int subscr_sim_kc(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; if (length < 9) return -EINVAL; /* key */ memcpy(subscr->key, data, 8); /* key sequence */ subscr->key_seq = data[8] & 0x07; LOGP(DMM, LOGL_INFO, "received KEY from SIM\n"); return 0; } static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm_sub_plmn_list *plmn; struct llist_head *lh, *lh2; uint8_t lai[5]; uint16_t dummy_lac; /* flush list */ llist_for_each_safe(lh, lh2, &subscr->plmn_list) { llist_del(lh); talloc_free(lh); } while(length >= 3) { /* end of list inside mandatory fields */ if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) break; /* add to list */ plmn = talloc_zero(ms, struct gsm_sub_plmn_list); if (!plmn) return -ENOMEM; lai[0] = data[0]; lai[1] = data[1]; lai[2] = data[2]; gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, &plmn->mcc, &plmn->mnc, &dummy_lac); llist_add_tail(&plmn->entry, &subscr->plmn_list); LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc=%s mnc=%s) " "from SIM\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc)); data += 3; length -= 3; } return 0; } static int subscr_sim_hpplmn(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; if (length < 1) return -EINVAL; /* HPLMN search interval */ subscr->t6m_hplmn = *data; /* multiple of 6 minutes */ LOGP(DMM, LOGL_INFO, "received HPPLMN %d (%d mins) from SIM\n", subscr->t6m_hplmn, subscr->t6m_hplmn * 6); return 0; } static int subscr_sim_spn(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; int i; /* UCS2 code not supported */ if (length < 17 || data[1] >= 0x80) return -ENOTSUP; data++; for (i = 0; i < 16; i++) { if (*data == 0xff) break; subscr->sim_spn[i] = *data++; } subscr->sim_spn[i] = '\0'; LOGP(DMM, LOGL_INFO, "received SPN %s from SIM\n", subscr->sim_spn); return 0; } static int subscr_sim_acc(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; uint16_t ac; if (length < 2) return -EINVAL; /* cell access */ memcpy(&ac, data, sizeof(ac)); subscr->acc_class = ntohs(ac); LOGP(DMM, LOGL_INFO, "received ACC %04x from SIM\n", subscr->acc_class); return 0; } static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, uint8_t length) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm_sub_plmn_na *na; struct llist_head *lh, *lh2; uint8_t lai[5]; uint16_t dummy_lac; #ifdef TEST_EMPTY_FPLMN return 0; #endif /* flush list */ llist_for_each_safe(lh, lh2, &subscr->plmn_na) { llist_del(lh); talloc_free(lh); } while (length >= 3) { /* end of list inside mandatory fields */ if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) break; /* add to list */ na = talloc_zero(ms, struct gsm_sub_plmn_na); if (!na) return -ENOMEM; lai[0] = data[0]; lai[1] = data[1]; lai[2] = data[2]; gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, &na->mcc, &na->mnc, &dummy_lac); LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s %s from SIM\n", gsm_print_mcc(na->mcc), gsm_print_mnc(na->mnc)); na->cause = -1; /* must have a value, but SIM stores no cause */ llist_add_tail(&na->entry, &subscr->plmn_na); data += 3; length -= 3; } return 0; } static struct subscr_sim_file { uint8_t mandatory; uint16_t path[MAX_SIM_PATH_LENGTH]; uint16_t file; uint8_t sim_job; int (*func)(struct osmocom_ms *ms, uint8_t *data, uint8_t length); } subscr_sim_files[] = { { 1, { 0 }, 0x2fe2, SIM_JOB_READ_BINARY, subscr_sim_iccid }, { 1, { 0x7f20, 0 }, 0x6f07, SIM_JOB_READ_BINARY, subscr_sim_imsi }, { 1, { 0x7f20, 0 }, 0x6f7e, SIM_JOB_READ_BINARY, subscr_sim_loci }, { 0, { 0x7f20, 0 }, 0x6f20, SIM_JOB_READ_BINARY, subscr_sim_kc }, { 0, { 0x7f20, 0 }, 0x6f30, SIM_JOB_READ_BINARY, subscr_sim_plmnsel }, { 0, { 0x7f20, 0 }, 0x6f31, SIM_JOB_READ_BINARY, subscr_sim_hpplmn }, { 0, { 0x7f20, 0 }, 0x6f46, SIM_JOB_READ_BINARY, subscr_sim_spn }, { 0, { 0x7f20, 0 }, 0x6f78, SIM_JOB_READ_BINARY, subscr_sim_acc }, { 0, { 0x7f20, 0 }, 0x6f7b, SIM_JOB_READ_BINARY, subscr_sim_fplmn }, { 0, { 0x7f10, 0 }, 0x6f40, SIM_JOB_READ_RECORD, subscr_sim_msisdn }, { 0, { 0x7f10, 0 }, 0x6f42, SIM_JOB_READ_RECORD, subscr_sim_smsp }, { 0, { 0 }, 0, 0, NULL } }; /* request file from SIM */ static int subscr_sim_request(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; struct msgb *nmsg; struct sim_hdr *nsh; int i; /* we are done, fire up PLMN and cell selection process */ if (!sf->func) { LOGP(DMM, LOGL_INFO, "(ms %s) Done reading SIM card " "(IMSI=%s %s, %s)\n", ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); /* if LAI is valid, set RPLMN */ if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { subscr->plmn_valid = 1; subscr->plmn_mcc = subscr->mcc; subscr->plmn_mnc = subscr->mnc; LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s %s " "(%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc), gsm_print_mnc(subscr->plmn_mnc), gsm_get_mcc(subscr->plmn_mcc), gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); } else LOGP(DMM, LOGL_INFO, "-> SIM card not registered\n"); /* insert card */ nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); if (!nmsg) return -ENOMEM; gsm48_mmr_downmsg(ms, nmsg); return 0; } /* trigger SIM reading */ nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, sf->sim_job); if (!nmsg) return -ENOMEM; nsh = (struct sim_hdr *) nmsg->data; i = 0; while (sf->path[i]) { nsh->path[i] = sf->path[i]; i++; } nsh->path[i] = 0; /* end of path */ nsh->file = sf->file; nsh->rec_no = 1; nsh->rec_mode = 0x04; LOGP(DMM, LOGL_INFO, "Requesting SIM file 0x%04x\n", nsh->file); sim_job(ms, nmsg); return 0; } static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) { struct gsm_subscriber *subscr = &ms->subscr; struct sim_hdr *sh = (struct sim_hdr *) msg->data; uint8_t *payload = msg->data + sizeof(*sh); uint16_t payload_len = msg->len - sizeof(*sh); int rc; struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; struct msgb *nmsg; /* error handling */ if (sh->job_type == SIM_JOB_ERROR) { uint8_t cause = payload[0]; switch (cause) { /* unlocking required */ case SIM_CAUSE_PIN1_REQUIRED: LOGP(DMM, LOGL_INFO, "PIN is required, %d tries left\n", payload[1]); vty_notify(ms, NULL); vty_notify(ms, "Please give PIN for ICCID %s (you have " "%d tries left)\n", subscr->iccid, payload[1]); subscr->sim_pin_required = 1; break; case SIM_CAUSE_PIN1_BLOCKED: LOGP(DMM, LOGL_NOTICE, "PIN is blocked\n"); vty_notify(ms, NULL); vty_notify(ms, "PIN is blocked\n"); if (payload[1]) { vty_notify(ms, "Please give PUC for ICCID %s " "(you have %d tries left)\n", subscr->iccid, payload[1]); } subscr->sim_pin_required = 1; break; case SIM_CAUSE_PUC_BLOCKED: LOGP(DMM, LOGL_NOTICE, "PUC is blocked\n"); vty_notify(ms, NULL); vty_notify(ms, "PUC is blocked\n"); subscr->sim_pin_required = 1; break; default: if (sf->func && !sf->mandatory) { LOGP(DMM, LOGL_NOTICE, "SIM reading failed, " "ignoring!\n"); goto ignore; } LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n"); vty_notify(ms, NULL); vty_notify(ms, "SIM failed, replace SIM!\n"); /* detach simcard */ subscr->sim_valid = 0; nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); if (!nmsg) return; gsm48_mmr_downmsg(ms, nmsg); } msgb_free(msg); return; } /* if pin was successfully unlocked, then resend request */ if (subscr->sim_pin_required) { subscr->sim_pin_required = 0; subscr_sim_request(ms); return; } /* done when nothing more to read. this happens on PIN requests */ if (!sf->func) return; /* call function do decode SIM reply */ rc = sf->func(ms, payload, payload_len); if (rc) { LOGP(DMM, LOGL_NOTICE, "SIM reading failed, file invalid\n"); if (subscr_sim_files[subscr->sim_file_index].mandatory) { vty_notify(ms, NULL); vty_notify(ms, "SIM failed, data invalid, replace " "SIM!\n"); msgb_free(msg); return; } } ignore: msgb_free(msg); /* trigger next file */ subscr->sim_file_index++; subscr_sim_request(ms); } /* enter PIN */ void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, int8_t mode) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; uint8_t job; /* skip, if no real valid SIM */ if (!GSM_SIM_IS_READER(subscr->sim_type)) return; switch (mode) { case -1: job = SIM_JOB_PIN1_DISABLE; LOGP(DMM, LOGL_INFO, "disabling PIN %s\n", pin1); break; case 1: job = SIM_JOB_PIN1_ENABLE; LOGP(DMM, LOGL_INFO, "enabling PIN %s\n", pin1); break; case 2: job = SIM_JOB_PIN1_CHANGE; LOGP(DMM, LOGL_INFO, "changing PIN %s to %s\n", pin1, pin2); break; case 99: job = SIM_JOB_PIN1_UNBLOCK; LOGP(DMM, LOGL_INFO, "unblocking PIN %s with PUC %s\n", pin1, pin2); break; default: if (!subscr->sim_pin_required) { LOGP(DMM, LOGL_ERROR, "No PIN required now\n"); return; } LOGP(DMM, LOGL_INFO, "entering PIN %s\n", pin1); job = SIM_JOB_PIN1_UNLOCK; } nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job); if (!nmsg) return; memcpy(msgb_put(nmsg, strlen(pin1) + 1), pin1, strlen(pin1) + 1); memcpy(msgb_put(nmsg, strlen(pin2) + 1), pin2, strlen(pin2) + 1); sim_job(ms, nmsg); } /* Attach SIM reader, no SIM must be currently attached */ int gsm_subscr_simcard(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; if (subscr->sim_valid) { LOGP(DMM, LOGL_ERROR, "Cannot attach card, until current card " "is detached.\n"); return -EBUSY; } /* reset subscriber */ gsm_subscr_exit(ms); gsm_subscr_init(ms); subscr->sim_type = GSM_SIM_TYPE_L1PHY; sprintf(subscr->sim_name, "sim"); subscr->sim_valid = 1; subscr->ustate = GSM_SIM_U2_NOT_UPDATED; /* start with first index */ subscr->sim_file_index = 0; return subscr_sim_request(ms); } /* update plmn not allowed list on SIM */ static int subscr_write_plmn_na(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; struct sim_hdr *nsh; struct gsm_sub_plmn_na *na, *nas[4] = { NULL, NULL, NULL, NULL }; int count = 0, i; uint8_t *data; uint8_t lai[5]; #ifdef TEST_EMPTY_FPLMN return 0; #endif /* skip, if no real valid SIM */ if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) return 0; /* get tail list from "PLMN not allowed" */ llist_for_each_entry(na, &subscr->plmn_na, entry) { if (count < 4) nas[count] = na; else { nas[0] = nas[1]; nas[1] = nas[2]; nas[2] = nas[3]; nas[3] = na; } count++; } /* write to SIM */ LOGP(DMM, LOGL_INFO, "Updating FPLMN on SIM\n"); nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, SIM_JOB_UPDATE_BINARY); if (!nmsg) return -ENOMEM; nsh = (struct sim_hdr *) nmsg->data; data = msgb_put(nmsg, 12); nsh->path[0] = 0x7f20; nsh->path[1] = 0; nsh->file = 0x6f7b; for (i = 0; i < 4; i++) { if (nas[i]) { gsm48_encode_lai_hex((struct gsm48_loc_area_id *)lai, nas[i]->mcc, nas[i]->mnc, 0); *data++ = lai[0]; *data++ = lai[1]; *data++ = lai[2]; } else { *data++ = 0xff; *data++ = 0xff; *data++ = 0xff; } } sim_job(ms, nmsg); return 0; } /* update LOCI on SIM */ int gsm_subscr_write_loci(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; struct sim_hdr *nsh; struct gsm1111_ef_loci *loci; /* skip, if no real valid SIM */ if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) return 0; LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); /* write to SIM */ nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, SIM_JOB_UPDATE_BINARY); if (!nmsg) return -ENOMEM; nsh = (struct sim_hdr *) nmsg->data; nsh->path[0] = 0x7f20; nsh->path[1] = 0; nsh->file = 0x6f7e; loci = (struct gsm1111_ef_loci *)msgb_put(nmsg, sizeof(*loci)); /* TMSI */ loci->tmsi = htonl(subscr->tmsi); /* LAI */ gsm48_encode_lai_hex(&loci->lai, subscr->mcc, subscr->mnc, subscr->lac); /* TMSI time */ loci->tmsi_time = 0xff; /* location update status */ switch (subscr->ustate) { case GSM_SIM_U1_UPDATED: loci->lupd_status = 0x00; break; case GSM_SIM_U3_ROAMING_NA: loci->lupd_status = 0x03; break; default: loci->lupd_status = 0x01; } sim_job(ms, nmsg); return 0; } static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg) { struct sim_hdr *sh = (struct sim_hdr *) msg->data; uint8_t *payload = msg->data + sizeof(*sh); /* error handling */ if (sh->job_type == SIM_JOB_ERROR) LOGP(DMM, LOGL_NOTICE, "SIM update failed (cause %d)\n", *payload); msgb_free(msg); } int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, uint8_t *rand, uint8_t no_sim) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; struct sim_hdr *nsh; /* not a SIM */ if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid || no_sim) { struct gsm48_mm_event *nmme; LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n"); nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); if (!nmsg) return -ENOMEM; nmme = (struct gsm48_mm_event *) nmsg->data; nmme->sres[0] = 0x12; nmme->sres[1] = 0x34; nmme->sres[2] = 0x56; nmme->sres[3] = 0x78; gsm48_mmevent_msg(ms, nmsg); return 0; } /* test SIM */ if (subscr->sim_type == GSM_SIM_TYPE_TEST) { struct gsm48_mm_event *nmme; struct gsm_settings *set = &ms->settings; static struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_GSM }; struct osmo_auth_vector _vec; struct osmo_auth_vector *vec = &_vec; auth.algo = set->test_ki_type; memcpy(auth.u.gsm.ki, set->test_ki, sizeof(auth.u.gsm.ki)); int ret = osmo_auth_gen_vec(vec, &auth, rand); if (ret < 0) return ret; /* store sequence */ subscr->key_seq = key_seq; memcpy(subscr->key, vec->kc, 8); LOGP(DMM, LOGL_INFO, "Sending authentication response\n"); nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); if (!nmsg) return -ENOMEM; nmme = (struct gsm48_mm_event *) nmsg->data; memcpy(nmme->sres, vec->sres, 4); gsm48_mmevent_msg(ms, nmsg); return 0; } LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n"); /* command to SIM */ nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_key, SIM_JOB_RUN_GSM_ALGO); if (!nmsg) return -ENOMEM; nsh = (struct sim_hdr *) nmsg->data; nsh->path[0] = 0x7f20; nsh->path[1] = 0; /* random */ memcpy(msgb_put(nmsg, 16), rand, 16); /* store sequence */ subscr->key_seq = key_seq; sim_job(ms, nmsg); return 0; } static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg) { struct gsm_subscriber *subscr = &ms->subscr; struct sim_hdr *sh = (struct sim_hdr *) msg->data; uint8_t *payload = msg->data + sizeof(*sh); uint16_t payload_len = msg->len - sizeof(*sh); struct msgb *nmsg; struct sim_hdr *nsh; struct gsm48_mm_event *nmme; uint8_t *data; /* error handling */ if (sh->job_type == SIM_JOB_ERROR) { LOGP(DMM, LOGL_NOTICE, "key generation on SIM failed " "(cause %d)\n", *payload); msgb_free(msg); return; } if (payload_len < 12) { LOGP(DMM, LOGL_NOTICE, "response from SIM too short\n"); return; } /* store key */ memcpy(subscr->key, payload + 4, 8); /* write to SIM */ LOGP(DMM, LOGL_INFO, "Updating KC on SIM\n"); nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, SIM_JOB_UPDATE_BINARY); if (!nmsg) return; nsh = (struct sim_hdr *) nmsg->data; nsh->path[0] = 0x7f20; nsh->path[1] = 0; nsh->file = 0x6f20; data = msgb_put(nmsg, 9); memcpy(data, subscr->key, 8); data[8] = subscr->key_seq; sim_job(ms, nmsg); /* return signed response */ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); if (!nmsg) return; nmme = (struct gsm48_mm_event *) nmsg->data; memcpy(nmme->sres, payload, 4); gsm48_mmevent_msg(ms, nmsg); msgb_free(msg); } /* * detach */ /* Detach card */ int gsm_subscr_remove(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; if (!subscr->sim_valid) { LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); return -EINVAL; } /* remove card */ nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); if (!nmsg) return -ENOMEM; gsm48_mmr_downmsg(ms, nmsg); return 0; } /* * state and lists */ static const char *subscr_ustate_names[] = { "U0_NULL", "U1_UPDATED", "U2_NOT_UPDATED", "U3_ROAMING_NA" }; /* change to new U state */ void new_sim_ustate(struct gsm_subscriber *subscr, int state) { LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, subscr_ustate_names[subscr->ustate], subscr_ustate_names[state]); subscr->ustate = state; } /* del forbidden PLMN. if MCC==0, flush complete list */ int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, uint16_t mnc) { struct gsm_sub_plmn_na *na, *na2; int deleted = 0; llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) { if (!mcc || (na->mcc == mcc && na->mnc == mnc)) { LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " "PLMNs (mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); llist_del(&na->entry); talloc_free(na); deleted = 1; if (mcc) break; } } if (deleted) { /* update plmn not allowed list on SIM */ subscr_write_plmn_na(subscr->ms); } return -EINVAL; } /* add forbidden PLMN */ int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, uint16_t mnc, uint8_t cause) { struct gsm_sub_plmn_na *na; /* if already in the list, remove and add to tail */ gsm_subscr_del_forbidden_plmn(subscr, mcc, mnc); LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na); if (!na) return -ENOMEM; na->mcc = mcc; na->mnc = mnc; na->cause = cause ? : -1; /* cause 0 is not allowed */ llist_add_tail(&na->entry, &subscr->plmn_na); /* don't add Home PLMN to SIM */ if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi)) return -EINVAL; /* update plmn not allowed list on SIM */ subscr_write_plmn_na(subscr->ms); return 0; } /* search forbidden PLMN */ int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, uint16_t mnc) { struct gsm_sub_plmn_na *na; llist_for_each_entry(na, &subscr->plmn_na, entry) { if (na->mcc == mcc && na->mnc == mnc) return 1; } return 0; } int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr) { if (ms->settings.force_rekey) return 7; else return subscr->key_seq; } int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm_sub_plmn_na *temp; print(priv, "MCC |MNC |cause\n"); print(priv, "-------+-------+-------\n"); llist_for_each_entry(temp, &subscr->plmn_na, entry) print(priv, "%s |%s%s |#%d\n", gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause); return 0; } /* dump subscriber */ void gsm_subscr_dump(struct gsm_subscriber *subscr, void (*print)(void *, const char *, ...), void *priv) { int i; struct gsm_sub_plmn_list *plmn_list; struct gsm_sub_plmn_na *plmn_na; print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); if (!subscr->sim_valid) { print(priv, " No SIM present.\n"); return; } print(priv, " IMSI: %s\n", subscr->imsi); if (subscr->iccid[0]) print(priv, " ICCID: %s\n", subscr->iccid); if (subscr->sim_spn[0]) print(priv, " Service Provider Name: %s\n", subscr->sim_spn); if (subscr->msisdn[0]) print(priv, " MSISDN: %s\n", subscr->msisdn); if (subscr->sms_sca[0]) print(priv, " SMS Service Center Address: %s\n", subscr->sms_sca); print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate], (subscr->imsi_attached) ? "attached" : "detached"); if (subscr->tmsi != 0xffffffff) print(priv, " TMSI 0x%08x", subscr->tmsi); if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { print(priv, "\n"); print(priv, " LAI: MCC %s MNC %s LAC 0x%04x " "(%s, %s)\n", gsm_print_mcc(subscr->mcc), gsm_print_mnc(subscr->mnc), subscr->lac, gsm_get_mcc(subscr->mcc), gsm_get_mnc(subscr->mcc, subscr->mnc)); } else print(priv, " LAI: invalid\n"); if (subscr->key_seq != 7) { print(priv, " Key: sequence %d ", subscr->key_seq); for (i = 0; i < sizeof(subscr->key); i++) print(priv, " %02x", subscr->key[i]); print(priv, "\n"); } if (subscr->plmn_valid) print(priv, " Registered PLMN: MCC %s MNC %s (%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc), gsm_print_mnc(subscr->plmn_mnc), gsm_get_mcc(subscr->plmn_mcc), gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); print(priv, " Access barred cells: %s\n", (subscr->acc_barr) ? "yes" : "no"); print(priv, " Access classes:"); for (i = 0; i < 16; i++) if ((subscr->acc_class & (1 << i))) print(priv, " C%d", i); print(priv, "\n"); if (!llist_empty(&subscr->plmn_list)) { print(priv, " List of preferred PLMNs:\n"); print(priv, " MCC |MNC\n"); print(priv, " -------+-------\n"); llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) print(priv, " %s |%s (%s, %s)\n", gsm_print_mcc(plmn_list->mcc), gsm_print_mnc(plmn_list->mnc), gsm_get_mcc(plmn_list->mcc), gsm_get_mnc(plmn_list->mcc, plmn_list->mnc)); } if (!llist_empty(&subscr->plmn_na)) { print(priv, " List of forbidden PLMNs:\n"); print(priv, " MCC |MNC |cause\n"); print(priv, " -------+-------+-------\n"); llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) print(priv, " %s |%s%s |#%d " "(%s, %s)\n", gsm_print_mcc(plmn_na->mcc), gsm_print_mnc(plmn_na->mnc), ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"", plmn_na->cause, gsm_get_mcc(plmn_na->mcc), gsm_get_mnc(plmn_na->mcc, plmn_na->mnc)); } } /* * SAP interface integration */ /* Attach SIM card over SAP */ int gsm_subscr_sapcard(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; int rc; if (subscr->sim_valid) { LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " "is detached.\n"); return -EBUSY; } /* reset subscriber */ gsm_subscr_exit(ms); gsm_subscr_init(ms); subscr->sim_type = GSM_SIM_TYPE_SAP; sprintf(subscr->sim_name, "sap"); subscr->sim_valid = 1; /* Try to connect to the SAP interface */ vty_notify(ms, NULL); vty_notify(ms, "Connecting to the SAP interface...\n"); rc = sap_open(ms); if (rc < 0) { LOGP(DSAP, LOGL_ERROR, "Failed during sap_open(), no SAP based SIM reader\n"); vty_notify(ms, "SAP connection error!\n"); ms->sap_wq.bfd.fd = -1; /* Detach SIM */ subscr->sim_valid = 0; nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); if (!nmsg) return -ENOMEM; gsm48_mmr_downmsg(ms, nmsg); return rc; } return 0; } /* Deattach sapcard */ int gsm_subscr_remove_sapcard(struct osmocom_ms *ms) { return sap_close(ms); } int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code, uint8_t res_type, uint16_t param_len, const uint8_t *param_val) { struct msgb *msg; int rc = 0; /* Response parameter is not encoded in case of error */ if (res_code != SAP_RESULT_OK_REQ_PROC_CORR) goto ignore_rsp; switch (res_type) { case SAP_TRANSFER_APDU_RESP: /* Prevent NULL-pointer dereference */ if (!param_len || !param_val) { rc = -EINVAL; goto ignore_rsp; } /* FIXME: why do we use this length? */ msg = msgb_alloc(GSM_SAP_LENGTH, "sap_apdu"); if (!msg) { rc = -ENOMEM; goto ignore_rsp; } msg->data = msgb_put(msg, param_len); memcpy(msg->data, param_val, param_len); return sim_apdu_resp(ms, msg); case SAP_TRANSFER_ATR_RESP: /* TODO: don't read SIM again (if already) */ LOGP(DSAP, LOGL_INFO, "SAP card is ready, start reading...\n"); return subscr_sim_request(ms); default: rc = -ENOTSUP; goto ignore_rsp; } return 0; ignore_rsp: LOGP(DSAP, LOGL_NOTICE, "Ignored SAP response '%s' (code=%d)\n", get_value_string(sap_msg_names, res_type), res_code); return rc; }