[layer23] Fixed and completed SIM's PIN handling
authorAndreas.Eversberg <jolly@eversberg.eu>
Sun, 19 Sep 2010 10:52:42 +0000 (10:52 +0000)
committerAndreas.Eversberg <jolly@eversberg.eu>
Sun, 19 Sep 2010 10:52:42 +0000 (10:52 +0000)
Use VTY to handle PIN:

enable
sim pin 1 xxxx
sim change-pin 1 xxxx yyyy
sim disable-pin 1 xxxx
sim enable-pin 1 xxxx
sim unlock-pin 1 uuuuuuuu yyyy

1 = mobile station "1"
xxxx = current PIN
yyyy = new PIN
uuuuuuuu = unlock key (PuK)

src/host/layer23/include/osmocom/bb/common/sim.h
src/host/layer23/include/osmocom/bb/mobile/subscriber.h
src/host/layer23/src/common/sim.c
src/host/layer23/src/mobile/subscriber.c
src/host/layer23/src/mobile/vty_interface.c

index c1e6087..a676b92 100644 (file)
@@ -174,8 +174,8 @@ struct gsm_sim {
        int                     job_state;
 
        uint8_t                 reset;
-       uint8_t                 pin1[8], pin2[8];
-       uint8_t                 pin1_len, pin2_len;
+       uint8_t                 chv1_remain, chv2_remain;
+       uint8_t                 unblk1_remain, unblk2_remain;
 };
 
 struct sim_hdr {
@@ -215,16 +215,16 @@ struct gsm1111_response_mfdf_gsm {
        uint8_t rfu1;
        uint8_t chv1_remain:4,
                 rfu2:3,
-                chv1_init;
+                chv1_init:1;
        uint8_t unblk1_remain:4,
                 rfu3:3,
-                unblk1_init;
+                unblk1_init:1;
        uint8_t chv2_remain:4,
                 rfu4:3,
-                chv2_init;
+                chv2_init:1;
        uint8_t unblk2_remain:4,
                 rfu5:3,
-                unblk2_init;
+                unblk2_init:1;
        uint8_t more_data[0];
 } __attribute__ ((packed));
 
index 93e7052..15af5d0 100644 (file)
@@ -81,7 +81,8 @@ int gsm_subscr_init(struct osmocom_ms *ms);
 int gsm_subscr_exit(struct osmocom_ms *ms);
 int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc);
 int gsm_subscr_simcard(struct osmocom_ms *ms);
-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);
 int gsm_subscr_write_loci(struct osmocom_ms *ms);
 int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq,
        uint8_t *rand, uint8_t no_sim);
index 543292e..7c4183f 100644 (file)
@@ -411,7 +411,7 @@ static int gsm1111_tx_change_chv(struct osmocom_ms *ms, uint8_t chv_no,
        buffer[1] = GSM1111_INST_CHANGE_CHV;
        buffer[2] = 0x00;
        buffer[3] = chv_no;
-       buffer[4] = 8;
+       buffer[4] = 16;
        for (i = 0; i < 8; i++) {
                if (i < length_old)
                        buffer[5 + i] = chv_old[i];
@@ -485,7 +485,7 @@ static int gsm1111_tx_unblock_chv(struct osmocom_ms *ms, uint8_t chv_no,
        buffer[1] = GSM1111_INST_UNBLOCK_CHV;
        buffer[2] = 0x00;
        buffer[3] = (chv_no == 1) ? 0 : chv_no;
-       buffer[4] = 8;
+       buffer[4] = 16;
        for (i = 0; i < 8; i++) {
                if (i < length_unblk)
                        buffer[5 + i] = chv_unblk[i];
@@ -654,8 +654,8 @@ static int gsm1111_tx_terminal_response(struct osmocom_ms *ms, uint8_t *data,
 static int sim_process_job(struct osmocom_ms *ms)
 {
        struct gsm_sim *sim = &ms->sim;
-       uint8_t *payload;
-       uint16_t payload_len;
+       uint8_t *payload, *payload2;
+       uint16_t payload_len, payload_len2;
        struct sim_hdr *sh;
        uint8_t cause;
        int i;
@@ -734,82 +734,98 @@ static int sim_process_job(struct osmocom_ms *ms)
                sim->job_state = SIM_JST_RUN_GSM_ALGO;
                return gsm1111_tx_run_gsm_algo(ms, payload);
        case SIM_JOB_PIN1_UNLOCK:
+               payload_len = strlen((char *)payload);
                if (payload_len < 4 || payload_len > 8) {
                        LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
                        break;
                }
                sim->job_state = SIM_JST_PIN1_UNLOCK;
-               memcpy(sim->pin1, payload, payload_len);
-               sim->pin1_len = payload_len;
                return gsm1111_tx_verify_chv(ms, 0x01, payload, payload_len);
        case SIM_JOB_PIN2_UNLOCK:
+               payload_len = strlen((char *)payload);
                if (payload_len < 4 || payload_len > 8) {
                        LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
                        break;
                }
                sim->job_state = SIM_JST_PIN2_UNLOCK;
-               memcpy(sim->pin2, payload, payload_len);
-               sim->pin2_len = payload_len;
                return gsm1111_tx_verify_chv(ms, 0x02, payload, payload_len);
        case SIM_JOB_PIN1_CHANGE:
-               if (!sim->pin1_len) {
-                       LOGP(DSIM, LOGL_ERROR, "no pin set\n");
+               payload_len = strlen((char *)payload);
+               payload2 = payload + payload_len + 1;
+               payload_len2 = strlen((char *)payload2);
+               if (payload_len < 4 || payload_len > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key1 not in range 4..8\n");
                        break;
                }
-               if (payload_len < 4 || payload_len > 8 || !sim->pin1_len) {
-                       LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
+               if (payload_len2 < 4 || payload_len2 > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n");
                        break;
                }
                sim->job_state = SIM_JST_PIN1_CHANGE;
-               return gsm1111_tx_change_chv(ms, 0x01, sim->pin1, sim->pin1_len,
-                       payload, payload_len);
+               return gsm1111_tx_change_chv(ms, 0x01, payload, payload_len,
+                       payload2, payload_len2);
        case SIM_JOB_PIN2_CHANGE:
-               if (!sim->pin2_len) {
-                       LOGP(DSIM, LOGL_ERROR, "no pin set\n");
+               payload_len = strlen((char *)payload);
+               payload2 = payload + payload_len + 1;
+               payload_len2 = strlen((char *)payload2);
+               if (payload_len < 4 || payload_len > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key1 not in range 4..8\n");
                        break;
                }
-               if (payload_len < 4 || payload_len > 8) {
-                       LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
+               if (payload_len2 < 4 || payload_len2 > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n");
                        break;
                }
                sim->job_state = SIM_JST_PIN2_CHANGE;
-               return gsm1111_tx_change_chv(ms, 0x02, sim->pin1, sim->pin1_len,
-                       payload, payload_len);
+               return gsm1111_tx_change_chv(ms, 0x02, payload, payload_len,
+                       payload2, payload_len2);
        case SIM_JOB_PIN1_DISABLE:
-               if (!sim->pin1_len) {
-                       LOGP(DSIM, LOGL_ERROR, "no pin set\n");
+               payload_len = strlen((char *)payload);
+               if (payload_len < 4 || payload_len > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
                        break;
                }
                sim->job_state = SIM_JST_PIN1_DISABLE;
-               return gsm1111_tx_disable_chv(ms, sim->pin1, sim->pin1_len);
+               return gsm1111_tx_disable_chv(ms, payload, payload_len);
        case SIM_JOB_PIN1_ENABLE:
-               if (!sim->pin1_len) {
-                       LOGP(DSIM, LOGL_ERROR, "no pin set\n");
+               payload_len = strlen((char *)payload);
+               if (payload_len < 4 || payload_len > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
                        break;
                }
                sim->job_state = SIM_JST_PIN1_ENABLE;
-               return gsm1111_tx_enable_chv(ms, sim->pin1, sim->pin1_len);
+               return gsm1111_tx_enable_chv(ms, payload, payload_len);
        case SIM_JOB_PIN1_UNBLOCK:
-               if (payload_len < 12 || payload_len > 16) {
-                       LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
+               payload_len = strlen((char *)payload);
+               payload2 = payload + payload_len + 1;
+               payload_len2 = strlen((char *)payload2);
+               if (payload_len !=  8) {
+                       LOGP(DSIM, LOGL_ERROR, "key1 not 8 digits\n");
                        break;
                }
-               sim->job_state = SIM_JST_PIN1_UNLOCK;
-               memcpy(sim->pin1, payload + 8, payload_len - 8);
-               sim->pin1_len = payload_len;
+               if (payload_len2 < 4 || payload_len2 > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n");
+                       break;
+               }
+               sim->job_state = SIM_JST_PIN1_UNBLOCK;
                /* NOTE: CHV1 is coded 0x00 here */
-               return gsm1111_tx_unblock_chv(ms, 0x00, payload, 8, payload + 8,
-                       payload_len - 8);
+               return gsm1111_tx_unblock_chv(ms, 0x00, payload, payload_len,
+                       payload2, payload_len2);
        case SIM_JOB_PIN2_UNBLOCK:
-               if (payload_len < 12 || payload_len > 16) {
-                       LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
+               payload_len = strlen((char *)payload);
+               payload2 = payload + payload_len + 1;
+               payload_len2 = strlen((char *)payload2);
+               if (payload_len !=  8) {
+                       LOGP(DSIM, LOGL_ERROR, "key1 not 8 digits\n");
                        break;
                }
-               sim->job_state = SIM_JST_PIN2_UNLOCK;
-               memcpy(sim->pin2, payload + 8, payload_len - 8);
-               sim->pin2_len = payload_len;
-               return gsm1111_tx_unblock_chv(ms, 0x02, payload, 8, payload + 8,
-                       payload_len - 8);
+               if (payload_len2 < 4 || payload_len2 > 8) {
+                       LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n");
+                       break;
+               }
+               sim->job_state = SIM_JST_PIN2_UNBLOCK;
+               return gsm1111_tx_unblock_chv(ms, 0x02, payload, payload_len,
+                       payload2, payload_len2);
        }
 
        LOGP(DSIM, LOGL_ERROR, "unknown job %x, please fix\n", sh->job_type);
@@ -861,12 +877,58 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
 
        switch (sw1) {
        case GSM1111_STAT_SECURITY:
-                       LOGP(DSIM, LOGL_NOTICE, "SIM Security\n");
-                       pin_cause[0] = SIM_CAUSE_PIN1_REQUIRED;
-                       pin_cause[1] = 1; /* PIN retries left */
-                       gsm_sim_reply(ms, SIM_JOB_ERROR, pin_cause, 2);
-                       msgb_free(msg);
-                       return 0;
+               LOGP(DSIM, LOGL_NOTICE, "SIM Security\n");
+               /* error */
+               if (sw2 != GSM1111_SEC_NO_ACCESS && sw2 != GSM1111_SEC_BLOCKED)
+                       goto sim_error;
+
+               /* select the right remaining counter an cause */
+               // FIXME: read status to replace "*_remain"-counters
+               switch (sim->job_state) {
+               case SIM_JST_PIN1_UNBLOCK:
+                       if (sw2 == GSM1111_SEC_NO_ACCESS) {
+                               pin_cause[0] = SIM_CAUSE_PIN1_BLOCKED;
+                               pin_cause[1] = --sim->unblk1_remain;
+                       } else {
+                               pin_cause[0] = SIM_CAUSE_PUC_BLOCKED;
+                               pin_cause[1] = 0;
+                       }
+                       break;
+               case SIM_JST_PIN2_UNLOCK:
+               case SIM_JST_PIN2_CHANGE:
+                       if (sw2 == GSM1111_SEC_NO_ACCESS && sim->chv2_remain) {
+                               pin_cause[0] = SIM_CAUSE_PIN2_REQUIRED;
+                               pin_cause[1] = sim->chv2_remain--;
+                       } else {
+                               pin_cause[0] = SIM_CAUSE_PIN2_BLOCKED;
+                               pin_cause[1] = sim->unblk2_remain;
+                       }
+                       break;
+               case SIM_JST_PIN2_UNBLOCK:
+                       if (sw2 == GSM1111_SEC_NO_ACCESS) {
+                               pin_cause[0] = SIM_CAUSE_PIN2_BLOCKED;
+                               pin_cause[1] = --sim->unblk2_remain;
+                       } else {
+                               pin_cause[0] = SIM_CAUSE_PUC_BLOCKED;
+                               pin_cause[1] = 0;
+                       }
+               case SIM_JST_PIN1_UNLOCK:
+               case SIM_JST_PIN1_CHANGE:
+               case SIM_JST_PIN1_DISABLE:
+               case SIM_JST_PIN1_ENABLE:
+               default:
+                       if (sw2 == GSM1111_SEC_NO_ACCESS && sim->chv1_remain) {
+                               pin_cause[0] = SIM_CAUSE_PIN1_REQUIRED;
+                               pin_cause[1] = sim->chv1_remain--;
+                       } else {
+                               pin_cause[0] = SIM_CAUSE_PIN1_BLOCKED;
+                               pin_cause[1] = sim->unblk1_remain;
+                       }
+                       break;
+               }
+               gsm_sim_reply(ms, SIM_JOB_ERROR, pin_cause, 2);
+               msgb_free(msg);
+               return 0;
        case GSM1111_STAT_MEM_PROBLEM:
                if (sw2 >= 0x40) {
                        LOGP(DSIM, LOGL_NOTICE, "memory of SIM failed\n");
@@ -885,29 +947,9 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
        case GSM1111_STAT_RESPONSE:
        case GSM1111_STAT_RESPONSE_TOO:
                LOGP(DSIM, LOGL_INFO, "command successfull\n");
-               switch (sh->job_type) {
-               case SIM_JOB_PIN1_CHANGE:
-                       memcpy(sim->pin1, payload, payload_len);
-                       sim->pin1_len = payload_len;
-                       break;
-               case SIM_JOB_PIN2_CHANGE:
-                       memcpy(sim->pin2, payload, payload_len);
-                       sim->pin2_len = payload_len;
-                       break;
-               }
                break;
        default:
                LOGP(DSIM, LOGL_INFO, "command failed\n");
-               switch (sh->job_type) {
-               case SIM_JOB_PIN1_UNLOCK:
-               case SIM_JOB_PIN1_UNBLOCK:
-                       sim->pin1_len = 0;
-                       break;
-               case SIM_JOB_PIN2_UNLOCK:
-               case SIM_JOB_PIN2_UNBLOCK:
-                       sim->pin2_len = 0;
-                       break;
-               }
                request_error:
                cause = SIM_CAUSE_REQUEST_ERROR;
                gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1);
@@ -939,6 +981,10 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
                }
                mfdf = (struct gsm1111_response_mfdf *)data;
                mfdf_gsm = (struct gsm1111_response_mfdf_gsm *)(data + 13);
+               sim->chv1_remain = mfdf_gsm->chv1_remain;
+               sim->chv2_remain = mfdf_gsm->chv2_remain;
+               sim->unblk1_remain = mfdf_gsm->unblk1_remain;
+               sim->unblk2_remain = mfdf_gsm->unblk2_remain;
                /* if MF was selected */
                if (sim->path[0] == 0) {
                        /* if MF was selected, but MF is not indicated */
index b95858f..41ee783 100644 (file)
@@ -560,6 +560,7 @@ 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];
 
        /* error handling */
        if (sh->job_type == SIM_JOB_ERROR) {
@@ -581,10 +582,22 @@ 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 (!subscr_sim_files[subscr->sim_file_index].
-                                                               mandatory) {
+                       if (sf->func && !sf->mandatory) {
                                LOGP(DMM, LOGL_NOTICE, "SIM reading failed, "
                                        "ignoring!\n");
                                goto ignore;
@@ -599,10 +612,19 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *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) {
@@ -624,23 +646,45 @@ ignore:
 }
 
 /* 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");
-               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);
 }
 
index 929529c..6b6e09e 100644 (file)
@@ -423,12 +423,105 @@ DEFUN(sim_pin, sim_pin_cmd, "sim pin MS_NAME PIN",
        if (!ms)
                return CMD_WARNING;
 
+       if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+               vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
        if (!ms->subscr.sim_pin_required) {
                vty_out(vty, "No PIN is required at this time!%s", VTY_NEWLINE);
                return CMD_WARNING;
        }
 
-       gsm_subscr_sim_pin(ms, (char *)argv[1]);
+       gsm_subscr_sim_pin(ms, (char *)argv[1], "", 0);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(sim_disable_pin, sim_disable_pin_cmd, "sim disable-pin MS_NAME PIN",
+       "SIM actions\nDisable PIN of SIM card\nName of MS (see \"show ms\")\n"
+       "PIN number")
+{
+       struct osmocom_ms *ms;
+
+       ms = get_ms(argv[0], vty);
+       if (!ms)
+               return CMD_WARNING;
+
+       if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+               vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       gsm_subscr_sim_pin(ms, (char *)argv[1], "", -1);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(sim_enable_pin, sim_enable_pin_cmd, "sim enable-pin MS_NAME PIN",
+       "SIM actions\nEnable PIN of SIM card\nName of MS (see \"show ms\")\n"
+       "PIN number")
+{
+       struct osmocom_ms *ms;
+
+       ms = get_ms(argv[0], vty);
+       if (!ms)
+               return CMD_WARNING;
+
+       if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+               vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       gsm_subscr_sim_pin(ms, (char *)argv[1], "", 1);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(sim_change_pin, sim_change_pin_cmd, "sim change-pin MS_NAME OLD NEW",
+       "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n"
+       "Old PIN number\nNew PIN number")
+{
+       struct osmocom_ms *ms;
+
+       ms = get_ms(argv[0], vty);
+       if (!ms)
+               return CMD_WARNING;
+
+       if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+               vty_out(vty, "Old PIN must be in range 4..8!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) {
+               vty_out(vty, "New PIN must be in range 4..8!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 2);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW",
+       "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n"
+       "Personal Unblock Key\nNew PIN number")
+{
+       struct osmocom_ms *ms;
+
+       ms = get_ms(argv[0], vty);
+       if (!ms)
+               return CMD_WARNING;
+
+       if (strlen(argv[1]) != 8) {
+               vty_out(vty, "PUC must be 8 digits!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) {
+               vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99);
 
        return CMD_SUCCESS;
 }
@@ -1258,6 +1351,10 @@ int ms_vty_init(void)
        install_element(ENABLE_NODE, &sim_reader_cmd);
        install_element(ENABLE_NODE, &sim_remove_cmd);
        install_element(ENABLE_NODE, &sim_pin_cmd);
+       install_element(ENABLE_NODE, &sim_disable_pin_cmd);
+       install_element(ENABLE_NODE, &sim_enable_pin_cmd);
+       install_element(ENABLE_NODE, &sim_change_pin_cmd);
+       install_element(ENABLE_NODE, &sim_unblock_pin_cmd);
        install_element(ENABLE_NODE, &network_search_cmd);
        install_element(ENABLE_NODE, &network_show_cmd);
        install_element(ENABLE_NODE, &network_select_cmd);