[layer23] Fixed and completed SIM's PIN handling

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)
This commit is contained in:
Andreas.Eversberg 2010-09-19 10:52:42 +00:00
parent 0856c8a6a5
commit 5e1cf12d67
5 changed files with 278 additions and 90 deletions

View 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));

View 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);

View 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_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, payload, payload_len,
payload2, payload_len2);
case SIM_JOB_PIN1_DISABLE:
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_CHANGE;
return gsm1111_tx_change_chv(ms, 0x02, sim->pin1, sim->pin1_len,
payload, payload_len);
case SIM_JOB_PIN1_DISABLE:
if (!sim->pin1_len) {
LOGP(DSIM, LOGL_ERROR, "no pin set\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 */

View 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);
}

View 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);