[layer23] Detach SIM, if reading fails
[osmocom-bb.git] / src / host / layer23 / src / mobile / subscriber.c
index a316c8e..544d53f 100644 (file)
@@ -23,7 +23,8 @@
 #include <errno.h>
 #include <string.h>
 #include <arpa/inet.h>
-#include <osmocore/talloc.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/comp128.h>
 
 #include <osmocom/bb/common/logging.h>
 #include <osmocom/bb/common/osmocom_data.h>
@@ -62,12 +63,12 @@ static char *sim_decode_bcd(uint8_t *data, uint8_t length)
 
        for (i = 0; i < (length << 1); i++) {
                if ((i & 1))
-                       c = (data[i >> 1] >> 4) + '0';
+                       c = (data[i >> 1] >> 4);
                else
-                       c = (data[i >> 1] & 0xf) + '0';
+                       c = (data[i >> 1] & 0xf);
                if (c == 0xf)
                        break;
-               result[j++] = c;
+               result[j++] = c + '0';
                if (j == sizeof(result) - 1)
                        break;
        }
@@ -76,6 +77,16 @@ static char *sim_decode_bcd(uint8_t *data, uint8_t length)
        return result;
 }
 
+static void xor96(uint8_t *ki, uint8_t *rand, uint8_t *sres, uint8_t *kc)
+{
+        int i;
+
+        for (i=0; i < 4; i++)
+                sres[i] = rand[i] ^ ki[i];
+        for (i=0; i < 8; i++)
+                kc[i] = rand[i] ^ ki[i+4];
+}
+
 /*
  * init/exit
  */
@@ -102,7 +113,6 @@ int gsm_subscr_init(struct osmocom_ms *ms)
        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);
-       subscr->sim_state = SUBSCR_SIM_NULL;
 
        return 0;
 }
@@ -143,7 +153,8 @@ int gsm_subscr_exit(struct osmocom_ms *ms)
  */
 
 /* Attach test card, no SIM must be currently attached */
-int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc)
+int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
+       uint16_t lac, uint32_t tmsi)
 {
        struct gsm_settings *set = &ms->settings;
        struct gsm_subscriber *subscr = &ms->subscr;
@@ -175,6 +186,10 @@ int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc)
        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);
@@ -184,9 +199,10 @@ int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc)
                gsm_imsi_mnc(subscr->imsi));
 
        if (subscr->plmn_valid)
-               LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s "
-                       "(%s, %s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc),
-                       gsm_get_mcc(mcc), gsm_get_mnc(mcc, mnc));
+               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");
 
@@ -219,6 +235,7 @@ 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)
@@ -228,14 +245,16 @@ static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data,
                return -EINVAL;
        }
        length = data[0];
-       if ((length << 1) > GSM_IMSI_LENGTH - 1 || (length << 1) < 6) {
+
+       /* 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 = %d\n",
-                       length << 1);
+                       strlen(imsi) - 1);
                return -EINVAL;
        }
 
-       strncpy(subscr->imsi, sim_decode_bcd(data + 1, length),
-               sizeof(subscr->imsi - 1));
+       strncpy(subscr->imsi, imsi + 1, sizeof(subscr->imsi) - 1);
 
        LOGP(DMM, LOGL_INFO, "received IMSI %s from SIM\n", subscr->imsi);
 
@@ -271,7 +290,9 @@ static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data,
                subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
        }
 
-       LOGP(DMM, LOGL_INFO, "received LOCI from SIM\n");
+       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;
 }
@@ -376,8 +397,8 @@ static int subscr_sim_hpplmn(struct osmocom_ms *ms, uint8_t *data,
        /* HPLMN search interval */
        subscr->t6m_hplmn = *data; /* multiple of 6 minutes */
 
-       LOGP(DMM, LOGL_INFO, "received HPPLMN %d from SIM\n",
-               subscr->t6m_hplmn);
+       LOGP(DMM, LOGL_INFO, "received HPPLMN %d (%d mins) from SIM\n",
+               subscr->t6m_hplmn, subscr->t6m_hplmn * 6);
 
        return 0;
 }
@@ -471,7 +492,7 @@ static struct subscr_sim_file {
        { 1, { 0 },         0x2fe2, subscr_sim_iccid },
        { 1, { 0x7f20, 0 }, 0x6f07, subscr_sim_imsi },
        { 1, { 0x7f20, 0 }, 0x6f7e, subscr_sim_loci },
-       { 0, { 0x7f20, 0 }, 0x6f40, subscr_sim_msisdn },
+       { 0, { 0x7f10, 0 }, 0x6f40, subscr_sim_msisdn },
        { 0, { 0x7f20, 0 }, 0x6f20, subscr_sim_kc },
        { 0, { 0x7f20, 0 }, 0x6f30, subscr_sim_plmnsel },
        { 0, { 0x7f20, 0 }, 0x6f31, subscr_sim_hpplmn },
@@ -545,6 +566,8 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
        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) {
@@ -566,22 +589,56 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
 
                        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;
        }
 
-       /* call function do decode SIM reply */
-       rc = subscr_sim_files[subscr->sim_file_index].func(ms, payload,
-               payload_len);
+       /* 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) {
@@ -594,6 +651,7 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
                }
        }
 
+ignore:
        msgb_free(msg);
 
        /* trigger next file */
@@ -602,23 +660,49 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
 }
 
 /* enter PIN */
-void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *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;
 
-       if (!subscr->sim_pin_required) {
-               LOGP(DMM, LOGL_ERROR, "No PIN required now\n");
+       /* skip, if no real valid SIM */
+       if (subscr->sim_type != GSM_SIM_TYPE_READER)
                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;
        }
 
-       subscr->sim_pin_required = 0;
-       LOGP(DMM, LOGL_INFO, "Unlocking PIN %s\n", pin);
-       nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update,
-               SIM_JOB_PIN1_UNLOCK);
+       nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job);
        if (!nmsg)
                return;
-       memcpy(msgb_put(nmsg, strlen(pin)), pin, strlen(pin));
+       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);
 }
 
@@ -769,26 +853,54 @@ static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg)
 }
 
 int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq,
-       uint8_t *rand)
+       uint8_t *rand, uint8_t no_sim)
 {
        struct gsm_subscriber *subscr = &ms->subscr;
        struct msgb *nmsg;
        struct sim_hdr *nsh;
 
        /* not a SIM */
-       if (subscr->sim_type != GSM_SIM_TYPE_READER || !subscr->sim_valid) {
+       if ((subscr->sim_type != GSM_SIM_TYPE_READER
+         && subscr->sim_type != GSM_SIM_TYPE_TEST)
+        || !subscr->sim_valid || no_sim) {
                struct gsm48_mm_event *nmme;
 
-               LOGP(DMM, LOGL_INFO, "Sending fake authentication response\n");
+               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 *)msgb_put(nmsg, sizeof(*nmme));
+               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;
+               uint8_t sres[4];
+               struct gsm_settings *set = &ms->settings;
+
+               if (set->test_ki_type == GSM_SIM_KEY_COMP128)
+                       comp128(set->test_ki, rand, sres, subscr->key);
+               else
+                       xor96(set->test_ki, rand, sres, subscr->key);
+               /* store sequence */
+               subscr->key_seq = key_seq;
+
+               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, sres, 4);
+               gsm48_mmevent_msg(ms, nmsg);
+
+               return 0;
        }
 
        LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n");
@@ -860,11 +972,8 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg)
        nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE);
        if (!nmsg)
                return;
-       nmme = (struct gsm48_mm_event *)msgb_put(nmsg, sizeof(*nmme));
-       nmme->sres[0] = 0x12;
-       nmme->sres[1] = 0x34;
-       nmme->sres[2] = 0x56;
-       nmme->sres[3] = 0x78;
+       nmme = (struct gsm48_mm_event *) nmsg->data;
+       memcpy(nmme->sres, payload, 4);
        gsm48_mmevent_msg(ms, nmsg);
 
        msgb_free(msg);
@@ -1012,22 +1121,27 @@ void gsm_subscr_dump(struct gsm_subscriber *subscr,
        }
 
        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);
        print(priv, " Status: %s  IMSI %s", subscr_ustate_names[subscr->ustate],
                (subscr->imsi_attached) ? "attached" : "detached");
        if (subscr->tmsi != 0xffffffff)
-               print(priv, "  TSMI  %08x", subscr->tmsi);
-       if (subscr->lac > 0x0000 && subscr->lac < 0xfffe)
-               print(priv, "  LAI: MCC %s  MNC %s  LAC 0x%04x  (%s, %s)\n",
-                       gsm_print_mcc(subscr->mcc),
+               print(priv, "  TSMI 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
+       else
                print(priv, "  LAI: invalid\n");
        if (subscr->key_seq != 7) {
-               print(priv, " Key: sequence %d ");
+               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");
@@ -1050,20 +1164,23 @@ void gsm_subscr_dump(struct gsm_subscriber *subscr,
                print(priv, "        MCC    |MNC\n");
                print(priv, "        -------+-------\n");
                llist_for_each_entry(plmn_list, &subscr->plmn_list, entry)
-                       print(priv, "        %s    |%s\n",
+                       print(priv, "        %s    |%s        (%s, %s)\n",
                        gsm_print_mcc(plmn_list->mcc),
-                       gsm_print_mnc(plmn_list->mnc));
+                       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\n",
-                               gsm_print_mcc(plmn_na->mcc),
+                       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);
+                               plmn_na->cause, gsm_get_mcc(plmn_na->mcc),
+                               gsm_get_mnc(plmn_na->mcc, plmn_na->mnc));
        }
 }