From: Alexander Couzens Date: Thu, 31 Oct 2024 13:17:03 +0000 (+0100) Subject: uqmid: sim_fsm: implement PIN/PUK checking set by configuration X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=71f9c9435b22b1ffe4085ce2bae99fbe413ca03a;p=project%2Fuqmi.git uqmid: sim_fsm: implement PIN/PUK checking set by configuration Signed-off-by: Alexander Couzens --- diff --git a/uqmid/modem.h b/uqmid/modem.h index 12c0b41..0cb2c76 100644 --- a/uqmid/modem.h +++ b/uqmid/modem.h @@ -35,6 +35,7 @@ struct modem_config { char *username; char *password; char *pin; + char *puk; bool roaming; uint8_t pdp_type; }; @@ -123,6 +124,9 @@ struct modem { bool use_upin; bool use_uim; bool requires_unlock; + /* when we tried the configured pin once */ + bool pin_tried; + bool puk_tried; int pin_retries; int puk_retries; } sim; diff --git a/uqmid/modem_tx.c b/uqmid/modem_tx.c index 9ebd4a1..0438dc6 100644 --- a/uqmid/modem_tx.c +++ b/uqmid/modem_tx.c @@ -271,3 +271,63 @@ int tx_uim_read_transparent_file(struct modem *modem, struct qmi_service *uim, r req->cb_data = modem; return uqmi_service_send_msg(uim, req); } + +int tx_uim_unblock_pin(struct modem *modem, struct qmi_service *uim, request_cb cb, + QmiUimPinId pin_id, char *new_pin_value, char *puk_value) +{ + struct qmi_request *req = talloc_zero(uim, struct qmi_request); + struct qmi_msg *msg = talloc_zero_size(req, 1024); + + struct qmi_uim_unblock_pin_request puk_request = {}; + puk_request.set.info = true; + puk_request.data.info.pin_id = pin_id; + puk_request.data.info.new_pin = new_pin_value; + puk_request.data.info.puk = puk_value; + + /* FIXME: test if this is ok and not QMI_UIM_SESSION_TYPE_CARD_SLOT_1 */ + puk_request.data.session.session_type = 0; + puk_request.data.session.application_identifier_n = 0; + puk_request.data.session.application_identifier = NULL; + + int ret = qmi_set_uim_unblock_pin_request(msg, &puk_request); + if (ret) { + LOG_ERROR("Failed to encode unblock_pin_request"); + return 1; + } + + req->msg = msg; + req->cb = cb; + req->cb_data = modem; + return uqmi_service_send_msg(uim, req); +} + + +int tx_uim_verify_pin(struct modem *modem, struct qmi_service *uim, request_cb cb, + QmiUimPinId pin_id, char *pin_value) +{ + struct qmi_request *req = talloc_zero(uim, struct qmi_request); + struct qmi_msg *msg = talloc_zero_size(req, 1024); + + struct qmi_uim_verify_pin_request pin_request = {}; + pin_request.set.info = true; + pin_request.data.info.pin_id = pin_id; + pin_request.data.info.pin_value = pin_value; + + + pin_request.set.session = 1; + /* FIXME: test if this is ok and not QMI_UIM_SESSION_TYPE_CARD_SLOT_1 */ + pin_request.data.session.session_type = 0; + pin_request.data.session.application_identifier_n = 0; + pin_request.data.session.application_identifier = NULL; + + int ret = qmi_set_uim_verify_pin_request(msg, &pin_request); + if (ret) { + LOG_ERROR("Failed to encode verify_pin_request"); + return 1; + } + + req->msg = msg; + req->cb = cb; + req->cb_data = modem; + return uqmi_service_send_msg(uim, req); +} diff --git a/uqmid/modem_tx.h b/uqmid/modem_tx.h index 872ebe6..aa9f3f6 100644 --- a/uqmid/modem_tx.h +++ b/uqmid/modem_tx.h @@ -1,6 +1,7 @@ #ifndef __UQMID_MODEM_TX_H #define __UQMID_MODEM_TX_H +#include "qmi-enums-uim.h" #include "uqmid.h" #include @@ -10,8 +11,15 @@ struct qmi_service; int tx_dms_set_operating_mode(struct modem *modem, struct qmi_service *dms, uint8_t operating_mode, request_cb cb); int tx_nas_subscribe_nas_events(struct modem *modem, struct qmi_service *nas, bool action, request_cb cb); + int tx_uim_read_transparent_file(struct modem *modem, struct qmi_service *wds, request_cb cb, uint16_t file_id, uint8_t *filepath, unsigned int filepath_n); +int tx_uim_verify_pin(struct modem *modem, struct qmi_service *uim, request_cb cb, + QmiUimPinId pin_id, char *pin_value); + +int tx_uim_unblock_pin(struct modem *modem, struct qmi_service *uim, request_cb cb, + QmiUimPinId pin_id, char *new_pin_value, char *puk_value); + int tx_wda_set_data_format(struct modem *modem, struct qmi_service *wda, request_cb cb); int tx_wds_get_profile_list(struct modem *modem, struct qmi_service *wds, request_cb cb); int tx_wds_modify_profile(struct modem *modem, struct qmi_service *wds, request_cb cb, uint8_t profile, const char *apn, diff --git a/uqmid/sim_fsm.c b/uqmid/sim_fsm.c index 6bad1ca..12d2334 100644 --- a/uqmid/sim_fsm.c +++ b/uqmid/sim_fsm.c @@ -19,6 +19,24 @@ * * tracks the state of the sim and notifies the parent on hard events. * e.g. removal of the sim + * + * The general concept is: + * Get information about (IMSI should not be available _before_ PIN/PUK valildation if enabled: + * - ICCID + * - PIN/PUK state + * - SPN, PNN (provider name) + * + * Unlock if locked by PIN if enough tries are available (more than 1 try). + * Enter a failed state if not enough PIN tries are available + * Enter a failed state if locked by PUK + * If unlocked, read IMSI. + * + * Further add user interface to: + * - unlock PIN/PUK via ubus + * - read/write transparent files + * - rx/tx APDU (e.g. for eSIM interface) + * + * See 3GPP TS 31.102 for USIM */ #include "logging.h" @@ -157,6 +175,7 @@ static void uim_get_card_status_cb(struct qmi_service *service, struct qmi_reque { struct modem *modem = req->cb_data; int ret = 0, i = 0, found = 0; + bool found_card_state = false; if (req->ret) { modem_log(modem, LOGL_INFO, "Failed to get card status %d/%s.", req->ret, qmi_get_error_str(req->ret)); @@ -173,6 +192,7 @@ static void uim_get_card_status_cb(struct qmi_service *service, struct qmi_reque } for (i = 0; i < res.data.card_status.cards_n; ++i) { + found_card_state = true; if (res.data.card_status.cards[i].card_state != QMI_UIM_CARD_STATE_PRESENT) continue; @@ -207,8 +227,12 @@ static void uim_get_card_status_cb(struct qmi_service *service, struct qmi_reque } } - modem_log(modem, LOGL_INFO, "Failed to find a valid UIM in get_status. Failing UIM."); - osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_FAILED, NULL); + if (found_card_state) { + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_NO_UIM_FOUND, (void *) 1); + } else { + modem_log(modem, LOGL_INFO, "Failed to find a valid UIM in get_status. Failing UIM."); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_FAILED, NULL); + } } static void uim_read_imsi_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) @@ -268,26 +292,37 @@ static void sim_st_get_info_on_enter(struct osmo_fsm_inst *fi, uint32_t old_stat } } -static void sim_st_get_info(struct osmo_fsm_inst *fi, uint32_t event, void *data) +static void sim_st_get_info(struct osmo_fsm_inst *fi, uint32_t event, void *_data) { struct modem *modem = fi->priv; + long data = (long) _data; struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); switch (event) { case SIM_EV_RX_UIM_GET_SLOT_FAILED: case SIM_EV_RX_UIM_VALID_ICCID: - case SIM_EV_RX_UIM_NO_UIM_FOUND: /* Get Slot Status succeeded? */ if (uim && modem->sim.use_uim) uqmi_service_send_simple(uim, qmi_set_uim_get_card_status_request, uim_get_card_status_cb, modem); break; - case SIM_EV_RX_UIM_CARD_STATUS_VALID: + case SIM_EV_RX_UIM_NO_UIM_FOUND: if (uim && modem->sim.use_uim) { - _tx_uim_read_transparent_file(modem, uim, uim_read_imsi_cb, UIM_FILE_IMSI); + /* Get Slot Status failed, try Get Card Status */ + if (data == 0) + uqmi_service_send_simple(uim, qmi_set_uim_get_card_status_request, uim_get_card_status_cb, + modem); + /* Get Card Status also failed to get one */ + else if (data == 1) + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_NO_SIM_PRESENT, 0, 0); } break; + case SIM_EV_RX_UIM_CARD_STATUS_VALID: + /* We found a valid SIM card via card status */ + if (uim && modem->sim.use_uim) + _tx_uim_read_transparent_file(modem, uim, uim_read_imsi_cb, UIM_FILE_IMSI); + break; case SIM_EV_RX_UIM_GET_IMSI_FAILED: case SIM_EV_RX_UIM_GET_IMSI_SUCCESS: switch (modem->sim.state) { @@ -301,10 +336,12 @@ static void sim_st_get_info(struct osmo_fsm_inst *fi, uint32_t event, void *data osmo_fsm_inst_state_chg(fi, SIM_ST_READY, 0, 0); break; case UQMI_SIM_UNKNOWN: - /* FIXME: what to do when unknown? */ + modem_log(modem, LOGL_ERROR, "Got unknown SIM state by UIM. Is a SIM present?"); + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_NO_SIM_PRESENT, 0, 0); break; case UQMI_SIM_BLOCKED: - osmo_fsm_inst_state_chg(fi, SIM_ST_FAILED, SIM_DEFAULT_TIMEOUT_S, 0); + modem_log(modem, LOGL_ERROR, "SIM is blocked. Can't do anythings"); + osmo_fsm_inst_state_chg(fi, SIM_ST_FAILED, 0, 0); break; default: /* FIXME: default case */ @@ -314,22 +351,163 @@ static void sim_st_get_info(struct osmo_fsm_inst *fi, uint32_t event, void *data } } +static void uim_verify_pin_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) +{ + struct modem *modem = req->cb_data; + int ret = 0; + + struct qmi_uim_verify_pin_response res = {}; + ret = qmi_parse_uim_verify_pin_response(msg, &res); + + if (req->ret) { + modem_log(modem, LOGL_INFO, "Failed to verify PIN. Qmi Ret %d/%s.", req->ret, + qmi_get_error_str(req->ret)); + if (!ret && res.set.retries_remaining) { + modem->sim.pin_retries = res.data.retries_remaining.verify_retries_left; + modem->sim.puk_retries = res.data.retries_remaining.unblock_retries_left; + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_PIN_INVALID, (void *) 1); + } else + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_PIN_INVALID, (void *) 0); + return; + } + + if (ret) { + modem_log(modem, LOGL_INFO, "Failed to verify pin. Decoder failed. %d", ret); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_REFRESH, NULL); + return; + } + + if (res.set.retries_remaining) { + modem->sim.pin_retries = res.data.retries_remaining.verify_retries_left; + modem->sim.puk_retries = res.data.retries_remaining.unblock_retries_left; + } + + if (res.set.card_result && res.data.card_result.sw1 == 0x63) { + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_PIN_VERIFIED, NULL); + return; + } + + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_REFRESH, NULL); +} + +static void pin_verify(struct modem *modem, char *pin) +{ + QmiUimPinId pin_id; + struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); + + modem->sim.pin_tried = true; + + if (modem->sim.use_upin) + pin_id = QMI_UIM_PIN_ID_UPIN; + else + pin_id = QMI_UIM_PIN_ID_PIN1; + + tx_uim_verify_pin(modem, uim, &uim_verify_pin_cb, pin_id, pin); +} + static void sim_st_chv_pin_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) { - // FIXME: try to unlock if enough tries are there + struct modem *modem = fi->priv; + + if (modem->sim.pin_retries < 2 || modem->sim.pin_tried || !modem->config.pin) { + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_PIN_REQUIRED, SIM_DEFAULT_TIMEOUT_S, 0); + return; + } + + pin_verify(modem, modem->config.pin); } static void sim_st_chv_pin(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + switch (event) { + case SIM_EV_RX_UIM_REFRESH: + case SIM_EV_RX_UIM_PIN_VERIFIED: + osmo_fsm_inst_state_chg(fi, SIM_ST_GET_INFO, SIM_DEFAULT_TIMEOUT_S, 0); + break; + case SIM_EV_RX_UIM_PIN_INVALID: + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_PIN_REQUIRED, SIM_DEFAULT_TIMEOUT_S, 0); + break; + } +} + +static void uim_unblock_pin_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) +{ + struct modem *modem = req->cb_data; + int ret = 0; + + struct qmi_uim_unblock_pin_response res = {}; + ret = qmi_parse_uim_unblock_pin_response(msg, &res); + + if (req->ret) { + modem_log(modem, LOGL_INFO, "Failed to unblock PIN by PUK. Qmi Ret %d/%s.", req->ret, + qmi_get_error_str(req->ret)); + if (!ret && res.set.retries_remaining) { + modem->sim.pin_retries = res.data.retries_remaining.verify_retries_left; + modem->sim.puk_retries = res.data.retries_remaining.unblock_retries_left; + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_PUK_INVALID, (void *) 1); + } else + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_PUK_INVALID, (void *) 0); + return; + } + + if (ret) { + modem_log(modem, LOGL_INFO, "Failed to unblock pin/puk. Decoder failed. %d", ret); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_REFRESH, NULL); + return; + } + + if (res.set.retries_remaining) { + modem->sim.pin_retries = res.data.retries_remaining.verify_retries_left; + modem->sim.puk_retries = res.data.retries_remaining.unblock_retries_left; + } + + if (res.set.card_result && res.data.card_result.sw1 == 0x63) { + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_PUK_VERIFIED, NULL); + return; + } + + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_REFRESH, NULL); +} + +static int pin_unblock(struct modem *modem, char *new_pin, char *puk) +{ + QmiUimPinId pin_id; + struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); + + modem->sim.puk_tried = true; + if (modem->sim.use_upin) + pin_id = QMI_UIM_PIN_ID_UPIN; + else + pin_id = QMI_UIM_PIN_ID_PIN1; + + return tx_uim_unblock_pin(modem, uim, &uim_unblock_pin_cb, pin_id, new_pin, puk); } static void sim_st_chv_puk_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) { - // FIXME: try to unlock if enough tries are there + struct modem *modem = fi->priv; + + if (modem->sim.puk_retries < 2 || modem->sim.puk_tried || !modem->config.puk || !modem->config.pin) { + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_PUK_REQUIRED, SIM_DEFAULT_TIMEOUT_S, 0); + return; + } + + + if (pin_unblock(modem, modem->config.pin, modem->config.puk)) + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_PUK_REQUIRED, 0, 0); } static void sim_st_chv_puk(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + switch (event) { + case SIM_EV_RX_UIM_REFRESH: + case SIM_EV_RX_UIM_PIN_VERIFIED: + osmo_fsm_inst_state_chg(fi, SIM_ST_GET_INFO, SIM_DEFAULT_TIMEOUT_S, 0); + break; + case SIM_EV_RX_UIM_PIN_INVALID: + osmo_fsm_inst_state_chg(fi, SIM_ST_FAIL_PIN_REQUIRED, SIM_DEFAULT_TIMEOUT_S, 0); + break; + } } static void sim_st_ready_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) @@ -343,6 +521,18 @@ static void sim_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data) { } +static void sim_st_fail_pin_required(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void sim_st_fail_puk_required(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void sim_st_fail_no_sim_present(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + static void sim_st_failed(struct osmo_fsm_inst *fi, uint32_t event, void *data) { } @@ -433,6 +623,7 @@ static const struct osmo_fsm_state sim_states[] = { .out_state_mask = S(SIM_ST_READY) | S(SIM_ST_CHV_PIN) | S(SIM_ST_CHV_PUK) | + S(SIM_ST_FAIL_NO_SIM_PRESENT) | S(SIM_ST_DESTROY), .name = "GET_INFO", .onenter = sim_st_get_info_on_enter, @@ -459,6 +650,24 @@ static const struct osmo_fsm_state sim_states[] = { .onenter = sim_st_chv_puk_on_enter, .action = sim_st_chv_puk, }, + [SIM_ST_FAIL_PIN_REQUIRED] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), + .name = "FAIL_PIN_REQUIRED", + .action = sim_st_fail_pin_required, + }, + [SIM_ST_FAIL_PUK_REQUIRED] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), + .name = "FAIL_PUK_REQUIRED", + .action = sim_st_fail_puk_required, + }, + [SIM_ST_FAIL_NO_SIM_PRESENT] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), + .name = "NO_SIM_PRESENT", + .action = sim_st_fail_no_sim_present, + }, [SIM_ST_FAILED] = { .in_event_mask = 0, .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), diff --git a/uqmid/sim_fsm.h b/uqmid/sim_fsm.h index 4577cab..94ef38e 100644 --- a/uqmid/sim_fsm.h +++ b/uqmid/sim_fsm.h @@ -2,6 +2,7 @@ #define __UQMID_SIM_FSM_H #include + enum sim_fsm_state { SIM_ST_IDLE, SIM_ST_WAIT_UIM_PRESENT, @@ -10,7 +11,10 @@ enum sim_fsm_state { SIM_ST_CHV_PUK, SIM_ST_READY, SIM_ST_FAILED, - SIM_ST_REMOVED, + SIM_ST_NO_PIN_TRIES_LEFT, + SIM_ST_FAIL_PUK_REQUIRED, + SIM_ST_FAIL_PIN_REQUIRED, + SIM_ST_FAIL_NO_SIM_PRESENT, SIM_ST_DESTROY, }; @@ -38,7 +42,14 @@ enum sim_fsm_event { SIM_EV_RX_UIM_GET_IMSI_FAILED, SIM_EV_RX_UIM_PIN_REQUIRED, + SIM_EV_RX_UIM_PIN_INVALID, + SIM_EV_RX_UIM_PIN_VERIFIED, + SIM_EV_RX_UIM_PUK_REQUIRED, + SIM_EV_RX_UIM_PUK_INVALID, + SIM_EV_RX_UIM_PUK_VERIFIED, + + SIM_EV_RX_UIM_REFRESH, /* we need to re-check the UIM. E.g. this can happen when pin verified or puk applied, but with a result which we can't parse */ SIM_EV_RX_UIM_READY, SIM_EV_RX_FAILED, diff --git a/uqmid/ubus.c b/uqmid/ubus.c index 6e6ad5f..077b4af 100644 --- a/uqmid/ubus.c +++ b/uqmid/ubus.c @@ -272,12 +272,13 @@ static int modem_remove(struct ubus_context *ctx, struct ubus_object *obj, struc return UBUS_STATUS_OK; } -enum { CFG_APN, CFG_PIN, CFG_ROAMING, CFG_USERNAME, CFG_PASSWORD, __CFG_MAX }; +enum { CFG_APN, CFG_PIN, CFG_PUK, CFG_ROAMING, CFG_USERNAME, CFG_PASSWORD, __CFG_MAX }; /** ubus call modem_configure{apn: internet, pin: 2342, roaming: false}` */ static const struct blobmsg_policy modem_configure_policy[__CFG_MAX] = { [CFG_APN] = { .name = "apn", .type = BLOBMSG_TYPE_STRING }, [CFG_PIN] = { .name = "pin", .type = BLOBMSG_TYPE_STRING }, + [CFG_PUK] = { .name = "puk", .type = BLOBMSG_TYPE_STRING }, [CFG_ROAMING] = { .name = "roaming", .type = BLOBMSG_TYPE_BOOL }, [CFG_USERNAME] = { .name = "username", .type = BLOBMSG_TYPE_STRING }, [CFG_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, @@ -308,6 +309,13 @@ static int modem_configure(struct ubus_context *ctx, struct ubus_object *obj, st modem->config.pin = talloc_strdup(modem, blobmsg_get_string(tb[CFG_PIN])); } + if (tb[CFG_PUK]) { + TALLOC_FREE(modem->config.puk); + value = blobmsg_get_string(tb[CFG_PUK]); + if (value && strlen(value)) + modem->config.puk = talloc_strdup(modem, blobmsg_get_string(tb[CFG_PUK])); + } + if (tb[CFG_ROAMING]) { modem->config.roaming = blobmsg_get_bool(tb[CFG_ROAMING]); }