L1CTL_PARAM_REQ,
L1CTL_DM_FREQ_REQ,
L1CTL_CRYPTO_REQ,
+ L1CTL_SIM_REQ,
+ L1CTL_SIM_CONF,
};
enum ccch_mode {
int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from,
uint16_t arfcm_to);
+int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length);
+
#endif
#include <osmocom/bb/mobile/gsm322.h>
#include <osmocom/bb/mobile/gsm48_mm.h>
#include <osmocom/bb/mobile/gsm48_cc.h>
-#include <osmocom/bb/mobile/sim.h>
+#include <osmocom/bb/common/sim.h>
/* A layer2 entity */
struct osmol2_entity {
--- /dev/null
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+/* 9.2 commands */
+#define GSM1111_CLASS_GSM 0xa0
+#define GSM1111_INST_SELECT 0xa4
+#define GSM1111_INST_STATUS 0xf2
+#define GSM1111_INST_READ_BINARY 0xb0
+#define GSM1111_INST_UPDATE_BINARY 0xd6
+#define GSM1111_INST_READ_RECORD 0xb2
+#define GSM1111_INST_UPDATE_RECORD 0xdc
+#define GSM1111_INST_SEEK 0xa2
+#define GSM1111_INST_INCREASE 0x32
+#define GSM1111_INST_VERIFY_CHV 0x20
+#define GSM1111_INST_CHANGE_CHV 0x24
+#define GSM1111_INST_DISABLE_CHV 0x26
+#define GSM1111_INST_ENABLE_CHV 0x28
+#define GSM1111_INST_UNBLOCK_CHV 0x2c
+#define GSM1111_INST_INVALIDATE 0x04
+#define GSM1111_INST_REHABLILITATE 0x44
+#define GSM1111_INST_RUN_GSM_ALGO 0x88
+#define GSM1111_INST_SLEEP 0xfa
+#define GSM1111_INST_GET_RESPONSE 0xc0
+#define GSM1111_INST_TERMINAL_PROFILE 0x10
+#define GSM1111_INST_ENVELOPE 0xc2
+#define GSM1111_INST_FETCH 0x12
+#define GSM1111_INST_TERMINAL_RESPONSE 0x14
+
+/* 9.3 access conditions */
+#define GSM1111_ACC_ALWAYS 0x0
+#define GSM1111_ACC_CHV1 0x1
+#define GSM1111_ACC_CHV2 0x2
+#define GSM1111_ACC_RFU 0x3
+#define GSM1111_ACC_NEW 0xf
+/* others are ADM */
+
+/* 9.3 type of file */
+#define GSM1111_TOF_RFU 0x00
+#define GSM1111_TOF_MF 0x01
+#define GSM1111_TOF_DF 0x02
+#define GSM1111_TOF_EF 0x04
+
+/* 9.3 struct of file */
+#define GSM1111_SOF_TRANSPARENT 0x00
+#define GSM1111_SOF_LINEAR 0x01
+#define GSM1111_SOF_CYCLIC 0x03
+
+/* 9.4 status */
+#define GSM1111_STAT_NORMAL 0x90
+#define GSM1111_STAT_PROACTIVE 0x91
+#define GSM1111_STAT_DL_ERROR 0x9e
+#define GSM1111_STAT_RESPONSE 0x9f
+#define GSM1111_STAT_RESPONSE_TOO 0x61
+#define GSM1111_STAT_APP_TK_BUSY 0x93
+#define GSM1111_STAT_MEM_PROBLEM 0x92
+#define GSM1111_STAT_REFERENCING 0x94
+#define GSM1111_STAT_SECURITY 0x98
+#define GSM1111_STAT_INCORR_P3 0x67
+#define GSM1111_STAT_INCORR_P1_P2 0x6b
+#define GSM1111_STAT_UKN_INST 0x6d
+#define GSM1111_STAT_WRONG_CLASS 0x6e
+#define GSM1111_STAT_TECH_PROBLEM 0x6f
+
+/* 9.4.4 Referencing management SW2 */
+#define GSM1111_REF_NO_EF 0x00
+#define GSM1111_REF_OUT_OF_RANGE 0x02
+#define GSM1111_REF_FILE_NOT_FOUND 0x04
+#define GSM1111_REF_FILE_INCONSI 0x08
+
+/* 9.4.5 Security management SW2 */
+#define GSM1111_SEC_NO_CHV 0x02
+#define GSM1111_SEC_NO_ACCESS 0x04
+#define GSM1111_SEC_CONTRA_CHV 0x08
+#define GSM1111_SEC_CONTRA_INVAL 0x10
+#define GSM1111_SEC_BLOCKED 0x40
+#define GSM1111_SEC_MAX_VALUE 0x50
+
+/* messages from application to sim client */
+enum {
+ /* requests */
+ SIM_JOB_READ_BINARY,
+ SIM_JOB_UPDATE_BINARY,
+ SIM_JOB_READ_RECORD,
+ SIM_JOB_UPDATE_RECORD,
+ SIM_JOB_SEEK_RECORD,
+ SIM_JOB_INCREASE,
+ SIM_JOB_INVALIDATE,
+ SIM_JOB_REHABILITATE,
+ SIM_JOB_RUN_GSM_ALGO,
+ SIM_JOB_PIN1_UNLOCK,
+ SIM_JOB_PIN1_CHANGE,
+ SIM_JOB_PIN1_DISABLE,
+ SIM_JOB_PIN1_ENABLE,
+ SIM_JOB_PIN1_UNBLOCK,
+ SIM_JOB_PIN2_UNLOCK,
+ SIM_JOB_PIN2_CHANGE,
+ SIM_JOB_PIN2_UNBLOCK,
+
+ /* results */
+ SIM_JOB_OK,
+ SIM_JOB_ERROR,
+};
+
+/* messages from sim client to application */
+#define SIM_JOB_OK 0
+#define SIM_JOB_ERROR 1
+
+/* error causes */
+#define SIM_CAUSE_NO_SIM 0 /* no SIM present, if detectable */
+#define SIM_CAUSE_SIM_ERROR 1 /* any error while reading SIM */
+#define SIM_CAUSE_REQUEST_ERROR 2 /* error in request */
+#define SIM_CAUSE_PIN1_REQUIRED 3 /* CHV1 is required for access */
+#define SIM_CAUSE_PIN2_REQUIRED 4 /* CHV2 is required for access */
+#define SIM_CAUSE_PIN1_BLOCKED 5 /* CHV1 was entered too many times */
+#define SIM_CAUSE_PIN2_BLOCKED 6 /* CHV2 was entered too many times */
+#define SIM_CAUSE_PUC_BLOCKED 7 /* unblock entered too many times */
+
+/* job states */
+enum {
+ SIM_JST_IDLE = 0,
+ SIM_JST_SELECT_MFDF, /* SELECT sent */
+ SIM_JST_SELECT_MFDF_RESP, /* GET RESPONSE sent */
+ SIM_JST_SELECT_EF, /* SELECT sent */
+ SIM_JST_SELECT_EF_RESP, /* GET RESPONSE sent */
+ SIM_JST_WAIT_FILE, /* file command sent */
+ SIM_JST_RUN_GSM_ALGO, /* wait for algorithm to process */
+ SIM_JST_RUN_GSM_ALGO_RESP, /* wait for response */
+ SIM_JST_PIN1_UNLOCK,
+ SIM_JST_PIN1_CHANGE,
+ SIM_JST_PIN1_DISABLE,
+ SIM_JST_PIN1_ENABLE,
+ SIM_JST_PIN1_UNBLOCK,
+ SIM_JST_PIN2_UNLOCK,
+ SIM_JST_PIN2_CHANGE,
+ SIM_JST_PIN2_UNBLOCK,
+};
+
+#define MAX_SIM_PATH_LENGTH 6 + 1 /* one for the termination */
+
+struct gsm_sim_handler {
+ struct llist_head entry;
+
+ uint32_t handle;
+ void (*cb)(struct osmocom_ms *ms, struct msgb *msg);
+};
+
+struct gsm_sim {
+ struct llist_head handlers; /* gsm_sim_handler */
+ struct llist_head jobs; /* messages */
+ uint16_t path[MAX_SIM_PATH_LENGTH];
+ uint16_t file;
+
+ struct msgb *job_msg;
+ uint32_t job_handle;
+ int job_state;
+
+ uint8_t reset;
+ uint8_t pin1[8], pin2[8];
+ uint8_t pin1_len, pin2_len;
+};
+
+struct sim_hdr {
+ int handle;
+ uint8_t job_type;
+ uint16_t path[MAX_SIM_PATH_LENGTH];
+ uint16_t file;
+ uint8_t rec_no, rec_mode; /* in case of record */
+ uint8_t seek_type_mode; /* in case of seek command */
+};
+
+#define SIM_ALLOC_SIZE 512
+#define SIM_ALLOC_HEADROOM 64
+
+struct msgb *gsm_sim_msgb_alloc(uint32_t handle, uint8_t job_type);
+uint32_t sim_open(struct osmocom_ms *ms,
+ void (*cb)(struct osmocom_ms *ms, struct msgb *msg));
+void sim_close(struct osmocom_ms *ms, uint32_t handle);
+void sim_job(struct osmocom_ms *ms, struct msgb *msg);
+
+/* Section 9.2.1 (response to selecting DF or MF) */
+struct gsm1111_response_mfdf {
+ uint16_t rfu1;
+ uint16_t free_mem;
+ uint16_t file_id;
+ uint8_t tof;
+ uint8_t rfu2[5];
+ uint8_t length;
+ uint8_t gsm_data[0];
+} __attribute__ ((packed));
+
+struct gsm1111_response_mfdf_gsm {
+ uint8_t file_char;
+ uint8_t num_df;
+ uint8_t num_ef;
+ uint8_t num_codes;
+ uint8_t rfu1;
+ uint8_t chv1_remain:4,
+ rfu2:3,
+ chv1_init;
+ uint8_t unblk1_remain:4,
+ rfu3:3,
+ unblk1_init;
+ uint8_t chv2_remain:4,
+ rfu4:3,
+ chv2_init;
+ uint8_t unblk2_remain:4,
+ rfu5:3,
+ unblk2_init;
+ uint8_t more_data[0];
+} __attribute__ ((packed));
+
+/* Section 9.2.1 (response to selecting EF) */
+struct gsm1111_response_ef {
+ uint16_t rfu1;
+ uint16_t file_size;
+ uint16_t file_id;
+ uint8_t tof;
+ uint8_t inc_allowed;
+ uint8_t acc_update:4,
+ acc_read:4;
+ uint8_t rfu2:4,
+ acc_inc:4;
+ uint8_t acc_inval:4,
+ acc_reha:4;
+ uint8_t not_inval:1,
+ rfu3:1,
+ ru_inval:1,
+ rfu4:5;
+ uint8_t length;
+ uint8_t structure;
+} __attribute__ ((packed));
+
+/* Section 10.3.17 */
+struct gsm1111_ef_loci {
+ uint32_t tmsi;
+ struct gsm48_loc_area_id lai;
+ uint8_t tmsi_time;
+ uint8_t lupd_status;
+} __attribute__ ((packed));
+
+/* Section 10.5.1 */
+struct gsm1111_ef_adn {
+ uint8_t len_bcd;
+ uint8_t ton_npi;
+ uint8_t number[10];
+ uint8_t capa_conf;
+ uint8_t ext_id;
+} __attribute__ ((packed));
+
+int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg);
+int gsm_sim_init(struct osmocom_ms *ms);
+int gsm_sim_exit(struct osmocom_ms *ms);
+int gsm_sim_job_dequeue(struct osmocom_ms *ms);
+
+
+++ /dev/null
-/*
- * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-
-/* 9.2 commands */
-#define GSM1111_CLASS_GSM 0xa0
-#define GSM1111_INST_SELECT 0xa4
-#define GSM1111_INST_STATUS 0xf2
-#define GSM1111_INST_READ_BINARY 0xb0
-#define GSM1111_INST_UPDATE_BINARY 0xd6
-#define GSM1111_INST_READ_RECORD 0xb2
-#define GSM1111_INST_UPDATE_RECORD 0xdc
-#define GSM1111_INST_SEEK 0xa2
-#define GSM1111_INST_INCREASE 0x32
-#define GSM1111_INST_VERIFY_CHV 0x20
-#define GSM1111_INST_CHANGE_CHV 0x24
-#define GSM1111_INST_DISABLE_CHV 0x26
-#define GSM1111_INST_ENABLE_CHV 0x28
-#define GSM1111_INST_UNBLOCK_CHV 0x2c
-#define GSM1111_INST_INVALIDATE 0x04
-#define GSM1111_INST_REHABLILITATE 0x44
-#define GSM1111_INST_RUN_GSM_ALGO 0x88
-#define GSM1111_INST_SLEEP 0xfa
-#define GSM1111_INST_GET_RESPONSE 0xc0
-#define GSM1111_INST_TERMINAL_PROFILE 0x10
-#define GSM1111_INST_ENVELOPE 0xc2
-#define GSM1111_INST_FETCH 0x12
-#define GSM1111_INST_TERMINAL_RESPONSE 0x14
-
-/* 9.3 access conditions */
-#define GSM1111_ACC_ALWAYS 0x0
-#define GSM1111_ACC_CHV1 0x1
-#define GSM1111_ACC_CHV2 0x2
-#define GSM1111_ACC_RFU 0x3
-#define GSM1111_ACC_NEW 0xf
-/* others are ADM */
-
-/* 9.3 type of file */
-#define GSM1111_TOF_RFU 0x00
-#define GSM1111_TOF_MF 0x01
-#define GSM1111_TOF_DF 0x02
-#define GSM1111_TOF_EF 0x04
-
-/* 9.3 struct of file */
-#define GSM1111_SOF_TRANSPARENT 0x00
-#define GSM1111_SOF_LINEAR 0x01
-#define GSM1111_SOF_CYCLIC 0x03
-
-/* 9.4 status */
-#define GSM1111_STAT_NORMAL 0x90
-#define GSM1111_STAT_PROACTIVE 0x91
-#define GSM1111_STAT_DL_ERROR 0x9e
-#define GSM1111_STAT_RESPONSE 0x9f
-#define GSM1111_STAT_RESPONSE_TOO 0x61
-#define GSM1111_STAT_APP_TK_BUSY 0x93
-#define GSM1111_STAT_MEM_PROBLEM 0x92
-#define GSM1111_STAT_REFERENCING 0x94
-#define GSM1111_STAT_SECURITY 0x98
-#define GSM1111_STAT_INCORR_P3 0x67
-#define GSM1111_STAT_INCORR_P1_P2 0x6b
-#define GSM1111_STAT_UKN_INST 0x6d
-#define GSM1111_STAT_WRONG_CLASS 0x6e
-#define GSM1111_STAT_TECH_PROBLEM 0x6f
-
-/* 9.4.4 Referencing management SW2 */
-#define GSM1111_REF_NO_EF 0x00
-#define GSM1111_REF_OUT_OF_RANGE 0x02
-#define GSM1111_REF_FILE_NOT_FOUND 0x04
-#define GSM1111_REF_FILE_INCONSI 0x08
-
-/* 9.4.5 Security management SW2 */
-#define GSM1111_SEC_NO_CHV 0x02
-#define GSM1111_SEC_NO_ACCESS 0x04
-#define GSM1111_SEC_CONTRA_CHV 0x08
-#define GSM1111_SEC_CONTRA_INVAL 0x10
-#define GSM1111_SEC_BLOCKED 0x40
-#define GSM1111_SEC_MAX_VALUE 0x50
-
-/* messages from application to sim client */
-enum {
- /* requests */
- SIM_JOB_READ_BINARY,
- SIM_JOB_UPDATE_BINARY,
- SIM_JOB_READ_RECORD,
- SIM_JOB_UPDATE_RECORD,
- SIM_JOB_SEEK_RECORD,
- SIM_JOB_INCREASE,
- SIM_JOB_INVALIDATE,
- SIM_JOB_REHABILITATE,
- SIM_JOB_RUN_GSM_ALGO,
- SIM_JOB_PIN1_UNLOCK,
- SIM_JOB_PIN1_CHANGE,
- SIM_JOB_PIN1_DISABLE,
- SIM_JOB_PIN1_ENABLE,
- SIM_JOB_PIN1_UNBLOCK,
- SIM_JOB_PIN2_UNLOCK,
- SIM_JOB_PIN2_CHANGE,
- SIM_JOB_PIN2_UNBLOCK,
-
- /* results */
- SIM_JOB_OK,
- SIM_JOB_ERROR,
-};
-
-/* messages from sim client to application */
-#define SIM_JOB_OK 0
-#define SIM_JOB_ERROR 1
-
-/* error causes */
-#define SIM_CAUSE_NO_SIM 0 /* no SIM present, if detectable */
-#define SIM_CAUSE_SIM_ERROR 1 /* any error while reading SIM */
-#define SIM_CAUSE_REQUEST_ERROR 2 /* error in request */
-#define SIM_CAUSE_PIN1_REQUIRED 3 /* CHV1 is required for access */
-#define SIM_CAUSE_PIN2_REQUIRED 4 /* CHV2 is required for access */
-#define SIM_CAUSE_PIN1_BLOCKED 5 /* CHV1 was entered too many times */
-#define SIM_CAUSE_PIN2_BLOCKED 6 /* CHV2 was entered too many times */
-#define SIM_CAUSE_PUC_BLOCKED 7 /* unblock entered too many times */
-
-/* job states */
-enum {
- SIM_JST_IDLE = 0,
- SIM_JST_SELECT_MFDF, /* SELECT sent */
- SIM_JST_SELECT_MFDF_RESP, /* GET RESPONSE sent */
- SIM_JST_SELECT_EF, /* SELECT sent */
- SIM_JST_SELECT_EF_RESP, /* GET RESPONSE sent */
- SIM_JST_WAIT_FILE, /* file command sent */
- SIM_JST_RUN_GSM_ALGO, /* wait for algorithm to process */
- SIM_JST_RUN_GSM_ALGO_RESP, /* wait for response */
- SIM_JST_PIN1_UNLOCK,
- SIM_JST_PIN1_CHANGE,
- SIM_JST_PIN1_DISABLE,
- SIM_JST_PIN1_ENABLE,
- SIM_JST_PIN1_UNBLOCK,
- SIM_JST_PIN2_UNLOCK,
- SIM_JST_PIN2_CHANGE,
- SIM_JST_PIN2_UNBLOCK,
-};
-
-#define MAX_SIM_PATH_LENGTH 6 + 1 /* one for the termination */
-
-struct gsm_sim_handler {
- struct llist_head entry;
-
- uint32_t handle;
- void (*cb)(struct osmocom_ms *ms, struct msgb *msg);
-};
-
-struct gsm_sim {
- struct llist_head handlers; /* gsm_sim_handler */
- struct llist_head jobs; /* messages */
- uint16_t path[MAX_SIM_PATH_LENGTH];
- uint16_t file;
-
- struct msgb *job_msg;
- uint32_t job_handle;
- int job_state;
-
- uint8_t reset;
- uint8_t pin1[8], pin2[8];
- uint8_t pin1_len, pin2_len;
-};
-
-struct sim_hdr {
- int handle;
- uint8_t job_type;
- uint16_t path[MAX_SIM_PATH_LENGTH];
- uint16_t file;
- uint8_t rec_no, rec_mode; /* in case of record */
- uint8_t seek_type_mode; /* in case of seek command */
-};
-
-#define SIM_ALLOC_SIZE 128
-#define SIM_ALLOC_HEADROOM 64
-
-struct msgb *gsm_sim_msgb_alloc(uint32_t handle, uint8_t job_type);
-uint32_t sim_open(struct osmocom_ms *ms,
- void (*cb)(struct osmocom_ms *ms, struct msgb *msg));
-void sim_close(struct osmocom_ms *ms, uint32_t handle);
-void sim_job(struct osmocom_ms *ms, struct msgb *msg);
-
-/* Section 9.2.1 (response to selecting DF or MF) */
-struct gsm1111_response_mfdf {
- uint16_t rfu1;
- uint16_t free_mem;
- uint16_t file_id;
- uint8_t tof;
- uint8_t rfu2[5];
- uint8_t length;
- uint8_t gsm_data[0];
-} __attribute__ ((packed));
-
-struct gsm1111_response_mfdf_gsm {
- uint8_t file_char;
- uint8_t num_df;
- uint8_t num_ef;
- uint8_t num_codes;
- uint8_t rfu1;
- uint8_t chv1_remain:4,
- rfu2:3,
- chv1_init;
- uint8_t unblk1_remain:4,
- rfu3:3,
- unblk1_init;
- uint8_t chv2_remain:4,
- rfu4:3,
- chv2_init;
- uint8_t unblk2_remain:4,
- rfu5:3,
- unblk2_init;
- uint8_t more_data[0];
-} __attribute__ ((packed));
-
-/* Section 9.2.1 (response to selecting EF) */
-struct gsm1111_response_ef {
- uint16_t rfu1;
- uint16_t file_size;
- uint16_t file_id;
- uint8_t tof;
- uint8_t inc_allowed;
- uint8_t acc_update:4,
- acc_read:4;
- uint8_t rfu2:4,
- acc_inc:4;
- uint8_t acc_inval:4,
- acc_reha:4;
- uint8_t not_inval:1,
- rfu3:1,
- ru_inval:1,
- rfu4:5;
- uint8_t length;
- uint8_t structure;
-} __attribute__ ((packed));
-
-/* Section 10.3.17 */
-struct gsm1111_ef_loci {
- uint32_t tmsi;
- struct gsm48_loc_area_id lai;
- uint8_t tmsi_time;
- uint8_t lupd_status;
-} __attribute__ ((packed));
-
-/* Section 10.5.1 */
-struct gsm1111_ef_adn {
- uint8_t len_bcd;
- uint8_t ton_npi;
- uint8_t number[10];
- uint8_t capa_conf;
- uint8_t ext_id;
-} __attribute__ ((packed));
-
-int gsm_sim_init(struct osmocom_ms *ms);
-int gsm_sim_exit(struct osmocom_ms *ms);
-int gsm_sim_job_dequeue(struct osmocom_ms *ms);
-
-
noinst_LIBRARIES = liblayer23.a
liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_interface.c lapdm.c \
- logging.c networks.c
+ logging.c networks.c sim.c
meas->rxlev += dl->rx_level;
if (dl->num_biterr) {
+printf("Dropping frame with %u bit errors\n", dl->num_biterr);
LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n",
dl->num_biterr);
return 0;
return osmo_send_l1(ms, msg);
}
+int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
+{
+ struct msgb *msg;
+ uint8_t *dat;
+
+ msg = osmo_l1_alloc(L1CTL_SIM_REQ);
+ if (!msg)
+ return -1;
+
+ dat = msgb_put(msg, length);
+ memcpy(dat, data, length);
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* just forward the SIM response to the SIM handler */
+static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ uint16_t len = msg->len - sizeof(struct l1ctl_hdr);
+ uint8_t *data = msg->data + sizeof(struct l1ctl_hdr);
+
+ printf("SIM %s\n", hexdump(data, len));
+
+ /* pull the L1 header from the msgb */
+ msgb_pull(msg, sizeof(struct l1ctl_hdr));
+ msg->l1h = NULL;
+
+ sim_apdu_resp(ms, msg);
+
+ return 0;
+}
+
/* Transmit L1CTL_PM_REQ */
int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from,
uint16_t arfcn_to)
rc = rx_l1_ccch_mode_conf(ms, msg);
msgb_free(msg);
break;
+ case L1CTL_SIM_CONF:
+ rc = rx_l1_sim_conf(ms, msg);
+ break;
default:
fprintf(stderr, "Unknown MSG: %u\n", l1h->msg_type);
msgb_free(msg);
--- /dev/null
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <osmocore/talloc.h>
+#include <osmocore/utils.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/l1ctl.h>
+
+extern void *l23_ctx;
+static int sim_process_job(struct osmocom_ms *ms);
+
+/*
+ * support
+ */
+
+uint32_t new_handle = 1;
+
+static struct gsm1111_df_name {
+ uint16_t file;
+ const char *name;
+} gsm1111_df_name[] = {
+ { 0x3f00, "MF" },
+ { 0x7f20, "DFgsm" },
+ { 0x7f10, "DFtelecom" },
+ { 0x7f22, "DFis-41" },
+ { 0x7f23, "DFfp-cts" },
+ { 0x5f50, "DFgraphics" },
+ { 0x5f30, "DFiridium" },
+ { 0x5f31, "DFglobst" },
+ { 0x5f32, "DFico" },
+ { 0x5f33, "DFaces" },
+ { 0x5f40, "DFeia/tia-553" },
+ { 0x5f60, "DFcts" },
+ { 0x5f70, "DFsolsa" },
+ { 0x5f3c, "DFmexe" },
+ { 0, NULL }
+};
+
+static const char *get_df_name(uint16_t fid)
+{
+ int i;
+ static char text[7];
+
+ for (i = 0; gsm1111_df_name[i].file; i++)
+ if (gsm1111_df_name[i].file == fid)
+ break;
+ if (gsm1111_df_name[i].file)
+ return gsm1111_df_name[i].name;
+
+ sprintf(text, "0x%04x", fid);
+ return text;
+}
+
+static struct gsm_sim_handler *sim_get_handler(struct gsm_sim *sim,
+ uint32_t handle)
+{
+ struct gsm_sim_handler *handler;
+
+ llist_for_each_entry(handler, &sim->handlers, entry)
+ if (handler->handle == handle)
+ return handler;
+
+ return NULL;
+}
+
+/*
+ * messages
+ */
+
+static const struct value_string sim_job_names[] = {
+ { SIM_JOB_READ_BINARY, "SIM_JOB_READ_BINARY" },
+ { SIM_JOB_UPDATE_BINARY, "SIM_JOB_UPDATE_BINARY" },
+ { SIM_JOB_READ_RECORD, "SIM_JOB_READ_RECORD" },
+ { SIM_JOB_UPDATE_RECORD, "SIM_JOB_UPDATE_RECORD" },
+ { SIM_JOB_SEEK_RECORD, "SIM_JOB_SEEK_RECORD" },
+ { SIM_JOB_INCREASE, "SIM_JOB_INCREASE" },
+ { SIM_JOB_INVALIDATE, "SIM_JOB_INVALIDATE" },
+ { SIM_JOB_REHABILITATE, "SIM_JOB_REHABILITATE" },
+ { SIM_JOB_RUN_GSM_ALGO, "SIM_JOB_RUN_GSM_ALGO" },
+ { SIM_JOB_PIN1_UNLOCK, "SIM_JOB_PIN1_UNLOCK" },
+ { SIM_JOB_PIN1_CHANGE, "SIM_JOB_PIN1_CHANGE" },
+ { SIM_JOB_PIN1_DISABLE, "SIM_JOB_PIN1_DISABLE" },
+ { SIM_JOB_PIN1_ENABLE, "SIM_JOB_PIN1_ENABLE" },
+ { SIM_JOB_PIN1_UNBLOCK, "SIM_JOB_PIN1_UNBLOCK" },
+ { SIM_JOB_PIN2_UNLOCK, "SIM_JOB_PIN2_UNLOCK" },
+ { SIM_JOB_PIN2_CHANGE, "SIM_JOB_PIN2_CHANGE" },
+ { SIM_JOB_PIN2_UNBLOCK, "SIM_JOB_PIN2_UNBLOCK" },
+ { SIM_JOB_OK, "SIM_JOB_OK" },
+ { SIM_JOB_ERROR, "SIM_JOB_ERROR" },
+ { 0, NULL }
+};
+
+static const char *get_job_name(int value)
+{
+ return get_value_string(sim_job_names, value);
+}
+
+/* allocate sim client message (upper layer) */
+struct msgb *gsm_sim_msgb_alloc(uint32_t handle, uint8_t job_type)
+{
+ struct msgb *msg;
+ struct sim_hdr *nsh;
+
+ msg = msgb_alloc_headroom(SIM_ALLOC_SIZE+SIM_ALLOC_HEADROOM,
+ SIM_ALLOC_HEADROOM, "SIM");
+ if (!msg)
+ return NULL;
+
+ nsh = (struct sim_hdr *) msgb_put(msg, sizeof(*nsh));
+ nsh->handle = handle;
+ nsh->job_type = job_type;
+
+ return msg;
+}
+
+/* reply to job, after it is done. reuse the msgb in the job */
+void gsm_sim_reply(struct osmocom_ms *ms, uint8_t result_type, uint8_t *result,
+ uint16_t result_len)
+{
+ struct gsm_sim *sim = &ms->sim;
+ struct msgb *msg = sim->job_msg;
+ struct sim_hdr *sh;
+ uint8_t *payload;
+ uint16_t payload_len;
+ struct gsm_sim_handler *handler;
+
+ LOGP(DSIM, LOGL_INFO, "sending result to callback function "
+ "(type=%d)\n", result_type);
+
+ /* if no handler, or no callback, just free the job */
+ sh = (struct sim_hdr *)msg->data;
+ handler = sim_get_handler(sim, sh->handle);
+ if (!handler && !handler->cb) {
+ LOGP(DSIM, LOGL_INFO, "no callback or no handler, "
+ "dropping result\n");
+ msgb_free(sim->job_msg);
+ sim->job_msg = NULL;
+ sim->job_state = SIM_JST_IDLE;
+ return;
+ }
+
+ payload = msg->data + sizeof(*sh);
+ payload_len = msg->len - sizeof(*sh);
+
+ /* remove data */
+ msg->tail -= payload_len;
+ msg->len -= payload_len;
+
+ /* add reply data */
+ sh->job_type = result_type;
+ if (result_len)
+ memcpy(msgb_put(msg, result_len), result, result_len);
+
+ /* callback */
+ sim->job_state = SIM_JST_IDLE;
+ sim->job_msg = NULL;
+ handler->cb(ms, msg);
+}
+
+/* send APDU to card reader */
+static int sim_apdu_send(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
+{
+ LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n",
+ data[0], data[1]);
+ l1ctl_tx_sim_req(ms, data, length);
+ return 0;
+}
+
+/* dequeue messages (RSL-SAP) */
+int gsm_sim_job_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm_sim *sim = &ms->sim;
+ struct sim_hdr *sh;
+ struct msgb *msg;
+ struct gsm_sim_handler *handler;
+
+ /* already have a job */
+ if (sim->job_msg)
+ return 0;
+
+ /* get next job */
+ while ((msg = msgb_dequeue(&sim->jobs))) {
+ /* resolve handler */
+ sh = (struct sim_hdr *) msg->data;
+ LOGP(DSIM, LOGL_INFO, "got new job: %s (handle=%08x)\n",
+ get_job_name(sh->job_type), sh->handle);
+ handler = sim_get_handler(sim, sh->handle);
+ if (!handler) {
+ LOGP(DSIM, LOGL_INFO, "no handler, ignoring job\n");
+ /* does not exist anymore */
+ msgb_free(msg);
+ continue;
+ }
+
+ /* init job */
+ sim->job_state = SIM_JST_IDLE;
+ sim->job_msg = msg;
+ sim->job_handle = sh->handle;
+
+ /* process current job, message is freed there */
+ sim_process_job(ms);
+ return 1; /* work done */
+ }
+
+ return 0;
+}
+
+
+/*
+ * SIM commands
+ */
+
+/* 9.2.1 */
+static int gsm1111_tx_select(struct osmocom_ms *ms, uint16_t fid)
+{
+ uint8_t buffer[5 + 2];
+
+ LOGP(DSIM, LOGL_INFO, "SELECT (file=0x%04x)\n", fid);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_SELECT;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 2;
+ buffer[5] = fid >> 8;
+ buffer[6] = fid;
+
+ return sim_apdu_send(ms, buffer, 5 + 2);
+}
+
+#if 0
+/* 9.2.2 */
+static int gsm1111_tx_status(struct osmocom_ms *ms)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "STATUS\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_STATUS;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+#endif
+
+/* 9.2.3 */
+static int gsm1111_tx_read_binary(struct osmocom_ms *ms, uint16_t offset,
+ uint8_t length)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "READ BINARY (offset=%d len=%d)\n", offset,
+ length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_READ_BINARY;
+ buffer[2] = offset >> 8;
+ buffer[3] = offset;
+ buffer[4] = length;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+
+/* 9.2.4 */
+static int gsm1111_tx_update_binary(struct osmocom_ms *ms, uint16_t offset,
+ uint8_t *data, uint8_t length)
+{
+ uint8_t buffer[5 + length];
+
+ LOGP(DSIM, LOGL_INFO, "UPDATE BINARY (offset=%d len=%d)\n", offset,
+ length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_UPDATE_BINARY;
+ buffer[2] = offset >> 8;
+ buffer[3] = offset;
+ buffer[4] = length;
+ memcpy(buffer + 5, data, length);
+
+ return sim_apdu_send(ms, buffer, 5 + length);
+}
+
+/* 9.2.5 */
+static int gsm1111_tx_read_record(struct osmocom_ms *ms, uint8_t rec_no,
+ uint8_t mode, uint8_t length)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "READ RECORD (rec_no=%d mode=%d len=%d)\n",
+ rec_no, mode, length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_READ_RECORD;
+ buffer[2] = rec_no;
+ buffer[3] = mode;
+ buffer[4] = length;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+
+/* 9.2.6 */
+static int gsm1111_tx_update_record(struct osmocom_ms *ms, uint8_t rec_no,
+ uint8_t mode, uint8_t *data, uint8_t length)
+{
+ uint8_t buffer[5 + length];
+
+ LOGP(DSIM, LOGL_INFO, "UPDATE RECORD (rec_no=%d mode=%d len=%d)\n",
+ rec_no, mode, length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_UPDATE_RECORD;
+ buffer[2] = rec_no;
+ buffer[3] = mode;
+ buffer[4] = length;
+ memcpy(buffer + 5, data, length);
+
+ return sim_apdu_send(ms, buffer, 5 + length);
+}
+
+/* 9.2.7 */
+static int gsm1111_tx_seek(struct osmocom_ms *ms, uint8_t type_mode,
+ uint8_t *pattern, uint8_t length)
+{
+ uint8_t buffer[5 + length];
+ uint8_t type = type_mode >> 4;
+ uint8_t mode = type_mode & 0x0f;
+
+ LOGP(DSIM, LOGL_INFO, "SEEK (type=%d mode=%d len=%d)\n", type, mode,
+ length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_SEEK;
+ buffer[2] = 0x00;
+ buffer[3] = type_mode;
+ buffer[4] = length;
+ memcpy(buffer + 5, pattern, length);
+
+ return sim_apdu_send(ms, buffer, 5 + length);
+}
+
+/* 9.2.8 */
+static int gsm1111_tx_increase(struct osmocom_ms *ms, uint32_t value)
+{
+ uint8_t buffer[5 + 3];
+
+ LOGP(DSIM, LOGL_INFO, "INCREASE (value=%d)\n", value);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_INCREASE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 3;
+ buffer[5] = value >> 16;
+ buffer[6] = value >> 8;
+ buffer[7] = value;
+
+ return sim_apdu_send(ms, buffer, 5 + 3);
+}
+
+/* 9.2.9 */
+static int gsm1111_tx_verify_chv(struct osmocom_ms *ms, uint8_t chv_no,
+ uint8_t *chv, uint8_t length)
+{
+ uint8_t buffer[5 + 8];
+ int i;
+
+ LOGP(DSIM, LOGL_INFO, "VERIFY CHV (CHV%d)\n", chv_no);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_VERIFY_CHV;
+ buffer[2] = 0x00;
+ buffer[3] = chv_no;
+ buffer[4] = 8;
+ for (i = 0; i < 8; i++) {
+ if (i < length)
+ buffer[5 + i] = chv[i];
+ else
+ buffer[5 + i] = 0xff;
+ }
+
+ return sim_apdu_send(ms, buffer, 5 + 8);
+}
+
+/* 9.2.10 */
+static int gsm1111_tx_change_chv(struct osmocom_ms *ms, uint8_t chv_no,
+ uint8_t *chv_old, uint8_t length_old, uint8_t *chv_new,
+ uint8_t length_new)
+{
+ uint8_t buffer[5 + 16];
+ int i;
+
+ LOGP(DSIM, LOGL_INFO, "CHANGE CHV (CHV%d)\n", chv_no);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_CHANGE_CHV;
+ buffer[2] = 0x00;
+ buffer[3] = chv_no;
+ buffer[4] = 8;
+ for (i = 0; i < 8; i++) {
+ if (i < length_old)
+ buffer[5 + i] = chv_old[i];
+ else
+ buffer[5 + i] = 0xff;
+ if (i < length_new)
+ buffer[13 + i] = chv_new[i];
+ else
+ buffer[13 + i] = 0xff;
+ }
+
+ return sim_apdu_send(ms, buffer, 5 + 16);
+}
+
+/* 9.2.11 */
+static int gsm1111_tx_disable_chv(struct osmocom_ms *ms, uint8_t *chv,
+ uint8_t length)
+{
+ uint8_t buffer[5 + 8];
+ int i;
+
+ LOGP(DSIM, LOGL_INFO, "DISABLE CHV (CHV1)\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_DISABLE_CHV;
+ buffer[2] = 0x00;
+ buffer[3] = 0x01;
+ buffer[4] = 8;
+ for (i = 0; i < 8; i++) {
+ if (i < length)
+ buffer[5 + i] = chv[i];
+ else
+ buffer[5 + i] = 0xff;
+ }
+
+ return sim_apdu_send(ms, buffer, 5 + 8);
+}
+
+/* 9.2.12 */
+static int gsm1111_tx_enable_chv(struct osmocom_ms *ms, uint8_t *chv,
+ uint8_t length)
+{
+ uint8_t buffer[5 + 8];
+ int i;
+
+ LOGP(DSIM, LOGL_INFO, "ENABLE CHV (CHV1)\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_ENABLE_CHV;
+ buffer[2] = 0x00;
+ buffer[3] = 0x01;
+ buffer[4] = 8;
+ for (i = 0; i < 8; i++) {
+ if (i < length)
+ buffer[5 + i] = chv[i];
+ else
+ buffer[5 + i] = 0xff;
+ }
+
+ return sim_apdu_send(ms, buffer, 5 + 8);
+}
+
+/* 9.2.13 */
+static int gsm1111_tx_unblock_chv(struct osmocom_ms *ms, uint8_t chv_no,
+ uint8_t *chv_unblk, uint8_t length_unblk, uint8_t *chv_new,
+ uint8_t length_new)
+{
+ uint8_t buffer[5 + 16];
+ int i;
+
+ LOGP(DSIM, LOGL_INFO, "UNBLOCK CHV (CHV%d)\n", (chv_no == 2) ? 2 : 1);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_UNBLOCK_CHV;
+ buffer[2] = 0x00;
+ buffer[3] = (chv_no == 1) ? 0 : chv_no;
+ buffer[4] = 8;
+ for (i = 0; i < 8; i++) {
+ if (i < length_unblk)
+ buffer[5 + i] = chv_unblk[i];
+ else
+ buffer[5 + i] = 0xff;
+ if (i < length_new)
+ buffer[13 + i] = chv_new[i];
+ else
+ buffer[13 + i] = 0xff;
+ }
+
+ return sim_apdu_send(ms, buffer, 5 + 16);
+}
+
+/* 9.2.14 */
+static int gsm1111_tx_invalidate(struct osmocom_ms *ms)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "INVALIDATE\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_INVALIDATE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+
+/* 9.2.15 */
+static int gsm1111_tx_rehabilitate(struct osmocom_ms *ms)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "REHABILITATE\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_REHABLILITATE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+
+/* 9.2.16 */
+static int gsm1111_tx_run_gsm_algo(struct osmocom_ms *ms, uint8_t *rand)
+{
+ uint8_t buffer[5 + 16];
+
+ LOGP(DSIM, LOGL_INFO, "RUN GSM ALGORITHM\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_RUN_GSM_ALGO;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 16;
+ memcpy(buffer + 5, rand, 16);
+
+ return sim_apdu_send(ms, buffer, 5 + 16);
+}
+
+#if 0
+/* 9.2.17 */
+static int gsm1111_tx_sleep(struct osmocom_ms *ms)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "\n");
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_SLEEP;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+#endif
+
+/* 9.2.18 */
+static int gsm1111_tx_get_response(struct osmocom_ms *ms, uint8_t length)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "GET RESPONSE (len=%d)\n", length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_GET_RESPONSE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = length;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+
+#if 0
+/* 9.2.19 */
+static int gsm1111_tx_terminal_profile(struct osmocom_ms *ms, uint8_t *data,
+ uint8_t length)
+{
+ uint8_t buffer[5 + length];
+
+ LOGP(DSIM, LOGL_INFO, "TERMINAL PROFILE (len=%d)\n", length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_TERMINAL_PROFILE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = length;
+ memcpy(buffer + 5, data, length);
+
+ return sim_apdu_send(ms, buffer, 5 + length);
+}
+
+/* 9.2.20 */
+static int gsm1111_tx_envelope(struct osmocom_ms *ms, uint8_t *data,
+ uint8_t length)
+{
+ uint8_t buffer[5 + length];
+
+ LOGP(DSIM, LOGL_INFO, "ENVELOPE (len=%d)\n", length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_ENVELOPE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = length;
+ memcpy(buffer + 5, data, length);
+
+ return sim_apdu_send(ms, buffer, 5 + length);
+}
+
+/* 9.2.21 */
+static int gsm1111_tx_fetch(struct osmocom_ms *ms, uint8_t length)
+{
+ uint8_t buffer[5];
+
+ LOGP(DSIM, LOGL_INFO, "FETCH (len=%d)\n", length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_FETCH;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = length;
+
+ return sim_apdu_send(ms, buffer, 5);
+}
+
+/* 9.2.22 */
+static int gsm1111_tx_terminal_response(struct osmocom_ms *ms, uint8_t *data,
+ uint8_t length)
+{
+ uint8_t buffer[5 + length];
+
+ LOGP(DSIM, LOGL_INFO, "TERMINAL RESPONSE (len=%d)\n", length);
+ buffer[0] = GSM1111_CLASS_GSM;
+ buffer[1] = GSM1111_INST_TERMINAL_RESPONSE;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = length;
+ memcpy(buffer + 5, data, length);
+
+ return sim_apdu_send(ms, buffer, 5 + length);
+}
+#endif
+
+/*
+ * SIM state machine
+ */
+
+/* process job */
+static int sim_process_job(struct osmocom_ms *ms)
+{
+ struct gsm_sim *sim = &ms->sim;
+ uint8_t *payload;
+ uint16_t payload_len;
+ struct sim_hdr *sh;
+ uint8_t cause;
+ int i;
+
+ /* no current */
+ if (!sim->job_msg)
+ return 0;
+
+ sh = (struct sim_hdr *)sim->job_msg->data;
+ payload = sim->job_msg->data + sizeof(*sh);
+ payload_len = sim->job_msg->len - sizeof(*sh);
+
+ /* do reset before sim reading */
+ if (!sim->reset) {
+ sim->reset = 1;
+ // FIXME: send reset command to L1
+ }
+
+ /* navigate to right DF */
+ switch (sh->job_type) {
+ case SIM_JOB_READ_BINARY:
+ case SIM_JOB_UPDATE_BINARY:
+ case SIM_JOB_READ_RECORD:
+ case SIM_JOB_UPDATE_RECORD:
+ case SIM_JOB_SEEK_RECORD:
+ case SIM_JOB_INCREASE:
+ case SIM_JOB_INVALIDATE:
+ case SIM_JOB_REHABILITATE:
+ case SIM_JOB_RUN_GSM_ALGO:
+ /* check MF / DF */
+ i = 0;
+ while (sh->path[i] && sim->path[i]) {
+ if (sh->path[i] != sh->path[i])
+ break;
+ i++;
+ }
+ /* if path in message is shorter or if paths are different */
+ if (sim->path[i]) {
+ LOGP(DSIM, LOGL_INFO, "go MF\n");
+ sim->job_state = SIM_JST_SELECT_MFDF;
+ /* go MF */
+ sim->path[0] = 0;
+ return gsm1111_tx_select(ms, 0x3f00);
+ }
+ /* if path in message is longer */
+ if (sh->path[i]) {
+ LOGP(DSIM, LOGL_INFO, "requested path is longer, go "
+ "child %s\n", get_df_name(sh->path[i]));
+ sim->job_state = SIM_JST_SELECT_MFDF;
+ /* select child */
+ sim->path[i] = sh->path[i];
+ sim->path[i + 1] = 0;
+ return gsm1111_tx_select(ms, sh->path[i]);
+ }
+ /* if paths are equal, continue */
+ }
+
+ /* set state and trigger SIM process */
+ switch (sh->job_type) {
+ case SIM_JOB_READ_BINARY:
+ case SIM_JOB_UPDATE_BINARY:
+ case SIM_JOB_READ_RECORD:
+ case SIM_JOB_UPDATE_RECORD:
+ case SIM_JOB_SEEK_RECORD:
+ case SIM_JOB_INCREASE:
+ case SIM_JOB_INVALIDATE:
+ case SIM_JOB_REHABILITATE:
+ sim->job_state = SIM_JST_SELECT_EF;
+ sim->file = sh->file;
+ return gsm1111_tx_select(ms, sh->file);
+ case SIM_JOB_RUN_GSM_ALGO:
+ if (payload_len != 16) {
+ LOGP(DSIM, LOGL_ERROR, "random not 16 bytes\n");
+ break;
+ }
+ sim->job_state = SIM_JST_RUN_GSM_ALGO;
+ return gsm1111_tx_run_gsm_algo(ms, payload);
+ case SIM_JOB_PIN1_UNLOCK:
+ 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:
+ 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");
+ break;
+ }
+ if (payload_len < 4 || payload_len > 8 || !sim->pin1_len) {
+ LOGP(DSIM, LOGL_ERROR, "key 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);
+ case SIM_JOB_PIN2_CHANGE:
+ if (!sim->pin2_len) {
+ LOGP(DSIM, LOGL_ERROR, "no pin set\n");
+ break;
+ }
+ 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);
+ case SIM_JOB_PIN1_ENABLE:
+ if (!sim->pin1_len) {
+ LOGP(DSIM, LOGL_ERROR, "no pin set\n");
+ break;
+ }
+ sim->job_state = SIM_JST_PIN1_ENABLE;
+ return gsm1111_tx_enable_chv(ms, sim->pin1, sim->pin1_len);
+ case SIM_JOB_PIN1_UNBLOCK:
+ if (payload_len < 12 || payload_len > 16) {
+ LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
+ break;
+ }
+ sim->job_state = SIM_JST_PIN1_UNLOCK;
+ memcpy(sim->pin1, payload + 8, payload_len - 8);
+ sim->pin1_len = payload_len;
+ /* NOTE: CHV1 is coded 0x00 here */
+ return gsm1111_tx_unblock_chv(ms, 0x00, payload, 8, payload + 8,
+ payload_len - 8);
+ case SIM_JOB_PIN2_UNBLOCK:
+ if (payload_len < 12 || payload_len > 16) {
+ LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\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);
+ }
+
+ LOGP(DSIM, LOGL_ERROR, "unknown job %x, please fix\n", sh->job_type);
+ cause = SIM_CAUSE_REQUEST_ERROR;
+ gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1);
+
+ return 0;
+}
+
+/* receive SIM response */
+int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_sim *sim = &ms->sim;
+ uint8_t *payload;
+ uint16_t payload_len;
+ uint8_t *data = msg->data;
+ int length = msg->len, ef_len;
+ uint8_t sw1, sw2;
+ uint8_t cause;
+ uint8_t pin_cause[2];
+ struct sim_hdr *sh;
+ struct gsm1111_response_ef *ef;
+ struct gsm1111_response_mfdf *mfdf;
+ struct gsm1111_response_mfdf_gsm *mfdf_gsm;
+ int i;
+
+ /* ignore, if current job already gone */
+ if (!sim->job_msg) {
+ LOGP(DSIM, LOGL_ERROR, "received APDU but no job, "
+ "please fix!\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ sh = (struct sim_hdr *)sim->job_msg->data;
+ payload = sim->job_msg->data + sizeof(*sh);
+ payload_len = sim->job_msg->len - sizeof(*sh);
+
+ /* process status */
+ if (length < 2) {
+ msgb_free(msg);
+ return 0;
+ }
+ sw1 = data[length - 2];
+ sw2 = data[length - 1];
+ length -= 2;
+ LOGP(DSIM, LOGL_INFO, "received APDU (len=%d sw1=0x%02x sw2=0x%02x)\n",
+ length, sw1, sw2);
+
+ 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;
+ case GSM1111_STAT_MEM_PROBLEM:
+ if (sw2 >= 0x40) {
+ LOGP(DSIM, LOGL_NOTICE, "memory of SIM failed\n");
+ sim_error:
+ cause = SIM_CAUSE_SIM_ERROR;
+ gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1);
+ msgb_free(msg);
+ return 0;
+ }
+ LOGP(DSIM, LOGL_NOTICE, "memory of SIM is bad (write took %d "
+ "times to succeed)\n", sw2);
+ /* fall through */
+ case GSM1111_STAT_NORMAL:
+ case GSM1111_STAT_PROACTIVE:
+ case GSM1111_STAT_DL_ERROR:
+ 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);
+ msgb_free(msg);
+ return 0;
+ }
+
+
+ switch (sim->job_state) {
+ /* step 1: after selecting MF / DF, request the response */
+ case SIM_JST_SELECT_MFDF:
+ /* not enough data */
+ if (sw2 < 22) {
+ LOGP(DSIM, LOGL_NOTICE, "expecting minimum 22 bytes\n");
+ goto sim_error;
+ }
+ /* request response */
+ sim->job_state = SIM_JST_SELECT_MFDF_RESP;
+ gsm1111_tx_get_response(ms, sw2);
+ msgb_free(msg);
+ return 0;
+ /* step 2: after getting response of selecting MF / DF, continue
+ * to "process_job".
+ */
+ case SIM_JST_SELECT_MFDF_RESP:
+ if (length < 22) {
+ LOGP(DSIM, LOGL_NOTICE, "expecting minimum 22 bytes\n");
+ goto sim_error;
+ }
+ mfdf = (struct gsm1111_response_mfdf *)data;
+ mfdf_gsm = (struct gsm1111_response_mfdf_gsm *)(data + 13);
+ /* if MF was selected */
+ if (sim->path[0] == 0) {
+ /* if MF was selected, but MF is not indicated */
+ if (ntohs(mfdf->file_id) != 0x3f00) {
+ LOGP(DSIM, LOGL_NOTICE, "Not MF\n");
+ goto sim_error;
+ }
+ /* if MF was selected, but type is not indicated */
+ if (mfdf->tof != GSM1111_TOF_MF) {
+ LOGP(DSIM, LOGL_NOTICE, "MF %02x != %02x "
+ "%04x\n", mfdf->tof, GSM1111_TOF_MF,
+ sim->path[0]);
+ goto sim_error;
+ }
+ /* now continue */
+ msgb_free(msg);
+ return sim_process_job(ms);
+ }
+ /* if DF was selected, but this DF is not indicated */
+ i = 0;
+ while (sim->path[i + 1])
+ i++;
+ if (ntohs(mfdf->file_id) != sim->path[i]) {
+ LOGP(DSIM, LOGL_NOTICE, "Path %04x != %04x\n",
+ ntohs(mfdf->file_id), sim->path[i]);
+ goto sim_error;
+ }
+ /* if DF was selected, but type is not indicated */
+ if (mfdf->tof != GSM1111_TOF_DF) {
+ LOGP(DSIM, LOGL_NOTICE, "TOF error\n");
+ goto sim_error;
+ }
+ /* now continue */
+ msgb_free(msg);
+ return sim_process_job(ms);
+ /* step 1: after selecting EF, request response of SELECT */
+ case SIM_JST_SELECT_EF:
+ /* not enough data */
+ if (sw2 < 14) {
+ LOGP(DSIM, LOGL_NOTICE, "expecting minimum 14 bytes\n");
+ goto sim_error;
+ }
+ /* request response */
+ sim->job_state = SIM_JST_SELECT_EF_RESP;
+ gsm1111_tx_get_response(ms, sw2);
+ msgb_free(msg);
+ return 0;
+ /* step 2: after getting response of selecting EF, do file command */
+ case SIM_JST_SELECT_EF_RESP:
+ if (length < 14) {
+ LOGP(DSIM, LOGL_NOTICE, "expecting minimum 14 bytes\n");
+ goto sim_error;
+ }
+ ef = (struct gsm1111_response_ef *)data;
+ /* if EF was selected, but type is not indicated */
+ if (ntohs(ef->file_id) != sim->file) {
+ LOGP(DSIM, LOGL_NOTICE, "EF ID %04x != %04x\n",
+ ntohs(ef->file_id), sim->file);
+ goto sim_error;
+ }
+ /* get length of file */
+ ef_len = ntohs(ef->file_size);
+ /* do file command */
+ sim->job_state = SIM_JST_WAIT_FILE;
+ switch (sh->job_type) {
+ case SIM_JOB_READ_BINARY:
+ // FIXME: do chunks when greater or equal 256 bytes */
+ gsm1111_tx_read_binary(ms, 0, ef_len);
+ break;
+ case SIM_JOB_UPDATE_BINARY:
+ // FIXME: do chunks when greater or equal 256 bytes */
+ if (ef_len < payload_len) {
+ LOGP(DSIM, LOGL_NOTICE, "selected file is "
+ "smaller (%d) than data to update "
+ "(%d)\n", ef_len, payload_len);
+ goto request_error;
+ }
+ gsm1111_tx_update_binary(ms, 0, payload, payload_len);
+ break;
+ case SIM_JOB_READ_RECORD:
+ gsm1111_tx_read_record(ms, sh->rec_no, sh->rec_mode,
+ ef_len);
+ break;
+ case SIM_JOB_UPDATE_RECORD:
+ if (ef_len != payload_len) {
+ LOGP(DSIM, LOGL_NOTICE, "selected file length "
+ "(%d) does not equal record to update "
+ "(%d)\n", ef_len, payload_len);
+ goto request_error;
+ }
+ gsm1111_tx_update_record(ms, sh->rec_no, sh->rec_mode,
+ payload, payload_len);
+ break;
+ case SIM_JOB_SEEK_RECORD:
+ gsm1111_tx_seek(ms, sh->seek_type_mode, data, length);
+ break;
+ case SIM_JOB_INCREASE:
+ if (length != 4) {
+ LOGP(DSIM, LOGL_ERROR, "expecting uint32_t as "
+ "value lenght, but got %d bytes\n",
+ length);
+ goto request_error;
+ }
+ gsm1111_tx_increase(ms, *((uint32_t *)data));
+ break;
+ case SIM_JOB_INVALIDATE:
+ gsm1111_tx_invalidate(ms);
+ break;
+ case SIM_JOB_REHABILITATE:
+ gsm1111_tx_rehabilitate(ms);
+ break;
+ }
+ msgb_free(msg);
+ return 0;
+ /* step 3: after processing file command, job is done */
+ case SIM_JST_WAIT_FILE:
+ /* reply job with data */
+ gsm_sim_reply(ms, SIM_JOB_OK, data, length);
+ msgb_free(msg);
+ return 0;
+ /* step 1: after running GSM algorithm, request response */
+ case SIM_JST_RUN_GSM_ALGO:
+ /* not enough data */
+ if (sw2 < 12) {
+ LOGP(DSIM, LOGL_NOTICE, "expecting minimum 12 bytes\n");
+ goto sim_error;
+ }
+ /* request response */
+ sim->job_state = SIM_JST_RUN_GSM_ALGO_RESP;
+ gsm1111_tx_get_response(ms, sw2);
+ msgb_free(msg);
+ return 0;
+ /* step 2: after processing GSM command, job is done */
+ case SIM_JST_RUN_GSM_ALGO_RESP:
+ /* reply job with data */
+ gsm_sim_reply(ms, SIM_JOB_OK, data, length);
+ msgb_free(msg);
+ return 0;
+ case SIM_JST_PIN1_UNLOCK:
+ case SIM_JST_PIN1_CHANGE:
+ case SIM_JST_PIN1_DISABLE:
+ case SIM_JST_PIN1_ENABLE:
+ case SIM_JST_PIN1_UNBLOCK:
+ case SIM_JST_PIN2_UNLOCK:
+ case SIM_JST_PIN2_CHANGE:
+ case SIM_JST_PIN2_UNBLOCK:
+ /* reply job with data */
+ gsm_sim_reply(ms, SIM_JOB_OK, data, length);
+ msgb_free(msg);
+ return 0;
+ }
+
+ LOGP(DSIM, LOGL_ERROR, "unknown state %u, please fix!\n",
+ sim->job_state);
+ goto request_error;
+}
+
+/*
+ * API
+ */
+
+/* open access to sim */
+uint32_t sim_open(struct osmocom_ms *ms,
+ void (*cb)(struct osmocom_ms *ms, struct msgb *msg))
+{
+ struct gsm_sim *sim = &ms->sim;
+ struct gsm_sim_handler *handler;
+
+ /* create handler and attach */
+ handler = talloc_zero(l23_ctx, struct gsm_sim_handler);
+ if (!handler)
+ return 0;
+ handler->handle = new_handle++;
+ handler->cb = cb;
+ llist_add_tail(&handler->entry, &sim->handlers);
+
+ return handler->handle;
+}
+
+/* close access to sim */
+void sim_close(struct osmocom_ms *ms, uint32_t handle)
+{
+ struct gsm_sim *sim = &ms->sim;
+ struct gsm_sim_handler *handler;
+
+ handler = sim_get_handler(sim, handle);
+ if (!handle)
+ return;
+
+ /* kill ourself */
+ llist_del(&handler->entry);
+ talloc_free(handler);
+}
+
+/* send job */
+void sim_job(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_sim *sim = &ms->sim;
+
+ msgb_enqueue(&sim->jobs, msg);
+}
+
+/*
+ * init
+ */
+
+int gsm_sim_init(struct osmocom_ms *ms)
+{
+ struct gsm_sim *sim = &ms->sim;
+
+ /* current path is undefined, forching MF */
+ sim->path[0] = 0x0bad;
+ sim->path[1] = 0;
+ sim->file = 0;
+
+ INIT_LLIST_HEAD(&sim->handlers);
+ INIT_LLIST_HEAD(&sim->jobs);
+
+ LOGP(DSIM, LOGL_INFO, "init SIM client\n");
+
+ return 0;
+}
+
+int gsm_sim_exit(struct osmocom_ms *ms)
+{
+ struct gsm_sim *sim = &ms->sim;
+ struct gsm_sim_handler *handler, *handler2;
+ struct msgb *msg;
+
+ LOGP(DSIM, LOGL_INFO, "exit SIM client\n");
+
+ /* remove pending job msg */
+ if (sim->job_msg) {
+ msgb_free(sim->job_msg);
+ sim->job_msg = NULL;
+ }
+ /* flush handlers */
+ llist_for_each_entry_safe(handler, handler2, &sim->handlers, entry)
+ sim_close(ms, handler->handle);
+ /* flush jobs */
+ while ((msg = msgb_dequeue(&sim->jobs)))
+ msgb_free(msg);
+
+ return 0;
+}
+
+
+
+
noinst_LIBRARIES = libmobile.a
libmobile_a_SOURCES = gsm322.c gsm48_cc.c gsm48_mm.c gsm48_rr.c \
- mnccms.c settings.c subscriber.c support.c gps.c sim.c \
+ mnccms.c settings.c subscriber.c support.c gps.c \
sysinfo.c transaction.c vty_interface.c
bin_PROGRAMS = mobile
set->test_rplmn_mnc);
break;
default:
- /* no SIM */
- ;
+ /* no SIM, trigger PLMN selection process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
}
- /* start PLMN + cell selection process */
- nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
- if (!nmsg)
- return -ENOMEM;
- gsm322_plmn_sendmsg(ms, nmsg);
- nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
- if (!nmsg)
- return -ENOMEM;
- gsm322_cs_sendmsg(ms, nmsg);
-
mobile_started = 1;
}
return 0;
if (!subscr->sim_valid) {
LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
return gsm322_c_any_cell_sel(ms, msg);
- LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
}
+ LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
/* stay in NULL state until PLMN is selected */
{ALL_STATES,
GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
- {SBIT(GSM322_A6_NO_SIM),
+ {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM),
GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
{ALL_STATES,
{ALL_STATES,
GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
- {SBIT(GSM322_M5_NO_SIM),
+ {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM),
GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
{ALL_STATES,
+++ /dev/null
-/*
- * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <stdint.h>
-#include <errno.h>
-#include <arpa/inet.h>
-#include <osmocore/talloc.h>
-#include <osmocore/utils.h>
-
-#include <osmocom/bb/common/logging.h>
-#include <osmocom/bb/common/osmocom_data.h>
-
-extern void *l23_ctx;
-static int sim_process_job(struct osmocom_ms *ms);
-
-/*
- * support
- */
-
-uint32_t new_handle = 1;
-
-static struct gsm1111_df_name {
- uint16_t file;
- const char *name;
-} gsm1111_df_name[] = {
- { 0x3f00, "MF" },
- { 0x7f20, "DFgsm" },
- { 0x7f10, "DFtelecom" },
- { 0x7f22, "DFis-41" },
- { 0x7f23, "DFfp-cts" },
- { 0x5f50, "DFgraphics" },
- { 0x5f30, "DFiridium" },
- { 0x5f31, "DFglobst" },
- { 0x5f32, "DFico" },
- { 0x5f33, "DFaces" },
- { 0x5f40, "DFeia/tia-553" },
- { 0x5f60, "DFcts" },
- { 0x5f70, "DFsolsa" },
- { 0x5f3c, "DFmexe" },
- { 0, NULL }
-};
-
-static const char *get_df_name(uint16_t fid)
-{
- int i;
- static char text[7];
-
- for (i = 0; gsm1111_df_name[i].file; i++)
- if (gsm1111_df_name[i].file == fid)
- break;
- if (gsm1111_df_name[i].file)
- return gsm1111_df_name[i].name;
-
- sprintf(text, "0x%04x", fid);
- return text;
-}
-
-static struct gsm_sim_handler *sim_get_handler(struct gsm_sim *sim,
- uint32_t handle)
-{
- struct gsm_sim_handler *handler;
-
- llist_for_each_entry(handler, &sim->handlers, entry)
- if (handler->handle == handle)
- return handler;
-
- return NULL;
-}
-
-/*
- * messages
- */
-
-static const struct value_string sim_job_names[] = {
- { SIM_JOB_READ_BINARY, "SIM_JOB_READ_BINARY" },
- { SIM_JOB_UPDATE_BINARY, "SIM_JOB_UPDATE_BINARY" },
- { SIM_JOB_READ_RECORD, "SIM_JOB_READ_RECORD" },
- { SIM_JOB_UPDATE_RECORD, "SIM_JOB_UPDATE_RECORD" },
- { SIM_JOB_SEEK_RECORD, "SIM_JOB_SEEK_RECORD" },
- { SIM_JOB_INCREASE, "SIM_JOB_INCREASE" },
- { SIM_JOB_INVALIDATE, "SIM_JOB_INVALIDATE" },
- { SIM_JOB_REHABILITATE, "SIM_JOB_REHABILITATE" },
- { SIM_JOB_RUN_GSM_ALGO, "SIM_JOB_RUN_GSM_ALGO" },
- { SIM_JOB_PIN1_UNLOCK, "SIM_JOB_PIN1_UNLOCK" },
- { SIM_JOB_PIN1_CHANGE, "SIM_JOB_PIN1_CHANGE" },
- { SIM_JOB_PIN1_DISABLE, "SIM_JOB_PIN1_DISABLE" },
- { SIM_JOB_PIN1_ENABLE, "SIM_JOB_PIN1_ENABLE" },
- { SIM_JOB_PIN1_UNBLOCK, "SIM_JOB_PIN1_UNBLOCK" },
- { SIM_JOB_PIN2_UNLOCK, "SIM_JOB_PIN2_UNLOCK" },
- { SIM_JOB_PIN2_CHANGE, "SIM_JOB_PIN2_CHANGE" },
- { SIM_JOB_PIN2_UNBLOCK, "SIM_JOB_PIN2_UNBLOCK" },
- { SIM_JOB_OK, "SIM_JOB_OK" },
- { SIM_JOB_ERROR, "SIM_JOB_ERROR" },
- { 0, NULL }
-};
-
-static const char *get_job_name(int value)
-{
- return get_value_string(sim_job_names, value);
-}
-
-/* allocate sim client message (upper layer) */
-struct msgb *gsm_sim_msgb_alloc(uint32_t handle, uint8_t job_type)
-{
- struct msgb *msg;
- struct sim_hdr *nsh;
-
- msg = msgb_alloc_headroom(SIM_ALLOC_SIZE+SIM_ALLOC_HEADROOM,
- SIM_ALLOC_HEADROOM, "SIM");
- if (!msg)
- return NULL;
-
- nsh = (struct sim_hdr *) msgb_put(msg, sizeof(*nsh));
- nsh->handle = handle;
- nsh->job_type = job_type;
-
- return msg;
-}
-
-/* reply to job, after it is done. reuse the msgb in the job */
-void gsm_sim_reply(struct osmocom_ms *ms, uint8_t result_type, uint8_t *result,
- uint16_t result_len)
-{
- struct gsm_sim *sim = &ms->sim;
- struct msgb *msg = sim->job_msg;
- struct sim_hdr *sh;
- uint8_t *payload;
- uint16_t payload_len;
- struct gsm_sim_handler *handler;
-
- LOGP(DSIM, LOGL_INFO, "sending result to callback function\n");
-
- /* if no handler, or no callback, just free the job */
- sh = (struct sim_hdr *)msg->data;
- handler = sim_get_handler(sim, sh->handle);
- if (!handler && !handler->cb) {
- LOGP(DSIM, LOGL_INFO, "no callback or no handler, "
- "dropping result\n");
- msgb_free(sim->job_msg);
- sim->job_msg = NULL;
- sim->job_state = SIM_JST_IDLE;
- return;
- }
-
- payload = msg->data + sizeof(*sh);
- payload_len = msg->len - sizeof(*sh);
-
- /* remove data */
- msg->tail -= payload_len;
- msg->len -= payload_len;
-
- /* add reply data */
- sh->job_type = result_type;
- if (result_len)
- memcpy(msgb_put(msg, result_len), result, result_len);
-
- /* callback */
- sim->job_state = SIM_JST_IDLE;
- sim->job_msg = NULL;
- handler->cb(ms, msg);
-}
-
-/* send APDU to card reader */
-static int sim_apdu_send(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
-{
- // FIXME: send apdu to layer 1
- LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n",
- data[0], data[1]);
- printf("process stops here, because no APDU is exchanged with layer 1\n");
- return 0;
-}
-
-/* dequeue messages (RSL-SAP) */
-int gsm_sim_job_dequeue(struct osmocom_ms *ms)
-{
- struct gsm_sim *sim = &ms->sim;
- struct sim_hdr *sh;
- struct msgb *msg;
- struct gsm_sim_handler *handler;
-
- /* already have a job */
- if (sim->job_msg)
- return 0;
-
- /* get next job */
- while ((msg = msgb_dequeue(&sim->jobs))) {
- /* resolve handler */
- sh = (struct sim_hdr *) msg->data;
- LOGP(DSIM, LOGL_INFO, "got new job: %s (handle=%08x)\n",
- get_job_name(sh->job_type), sh->handle);
- handler = sim_get_handler(sim, sh->handle);
- if (!handler) {
- LOGP(DSIM, LOGL_INFO, "no handler, ignoring job\n");
- /* does not exist anymore */
- msgb_free(msg);
- continue;
- }
-
- /* init job */
- sim->job_state = SIM_JST_IDLE;
- sim->job_msg = msg;
- sim->job_handle = sh->handle;
-
- /* process current job, message is freed there */
- sim_process_job(ms);
- return 1; /* work done */
- }
-
- return 0;
-}
-
-
-/*
- * SIM commands
- */
-
-/* 9.2.1 */
-static int gsm1111_tx_select(struct osmocom_ms *ms, uint16_t fid)
-{
- uint8_t buffer[5 + 2];
-
- LOGP(DSIM, LOGL_INFO, "SELECT (file=0x%04x)\n", fid);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_SELECT;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 2;
- buffer[5] = fid >> 8;
- buffer[6] = fid;
-
- return sim_apdu_send(ms, buffer, 5 + 2);
-}
-
-#if 0
-/* 9.2.2 */
-static int gsm1111_tx_status(struct osmocom_ms *ms)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "STATUS\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_STATUS;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 0;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-#endif
-
-/* 9.2.3 */
-static int gsm1111_tx_read_binary(struct osmocom_ms *ms, uint16_t offset,
- uint8_t length)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "READ BINARY (offset=%d len=%d)\n", offset,
- length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_READ_BINARY;
- buffer[2] = offset >> 8;
- buffer[3] = offset;
- buffer[4] = length;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-
-/* 9.2.4 */
-static int gsm1111_tx_update_binary(struct osmocom_ms *ms, uint16_t offset,
- uint8_t *data, uint8_t length)
-{
- uint8_t buffer[5 + length];
-
- LOGP(DSIM, LOGL_INFO, "UPDATE BINARY (offset=%d len=%d)\n", offset,
- length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_UPDATE_BINARY;
- buffer[2] = offset >> 8;
- buffer[3] = offset;
- buffer[4] = length;
- memcpy(buffer + 5, data, length);
-
- return sim_apdu_send(ms, buffer, 5 + length);
-}
-
-/* 9.2.5 */
-static int gsm1111_tx_read_record(struct osmocom_ms *ms, uint8_t rec_no,
- uint8_t mode, uint8_t length)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "READ RECORD (rec_no=%d mode=%d len=%d)\n",
- rec_no, mode, length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_READ_RECORD;
- buffer[2] = rec_no;
- buffer[3] = mode;
- buffer[4] = length;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-
-/* 9.2.6 */
-static int gsm1111_tx_update_record(struct osmocom_ms *ms, uint8_t rec_no,
- uint8_t mode, uint8_t *data, uint8_t length)
-{
- uint8_t buffer[5 + length];
-
- LOGP(DSIM, LOGL_INFO, "UPDATE RECORD (rec_no=%d mode=%d len=%d)\n",
- rec_no, mode, length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_UPDATE_RECORD;
- buffer[2] = rec_no;
- buffer[3] = mode;
- buffer[4] = length;
- memcpy(buffer + 5, data, length);
-
- return sim_apdu_send(ms, buffer, 5 + length);
-}
-
-/* 9.2.7 */
-static int gsm1111_tx_seek(struct osmocom_ms *ms, uint8_t type_mode,
- uint8_t *pattern, uint8_t length)
-{
- uint8_t buffer[5 + length];
- uint8_t type = type_mode >> 4;
- uint8_t mode = type_mode & 0x0f;
-
- LOGP(DSIM, LOGL_INFO, "SEEK (type=%d mode=%d len=%d)\n", type, mode,
- length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_SEEK;
- buffer[2] = 0x00;
- buffer[3] = type_mode;
- buffer[4] = length;
- memcpy(buffer + 5, pattern, length);
-
- return sim_apdu_send(ms, buffer, 5 + length);
-}
-
-/* 9.2.8 */
-static int gsm1111_tx_increase(struct osmocom_ms *ms, uint32_t value)
-{
- uint8_t buffer[5 + 3];
-
- LOGP(DSIM, LOGL_INFO, "INCREASE (value=%d)\n", value);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_INCREASE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 3;
- buffer[5] = value >> 16;
- buffer[6] = value >> 8;
- buffer[7] = value;
-
- return sim_apdu_send(ms, buffer, 5 + 3);
-}
-
-/* 9.2.9 */
-static int gsm1111_tx_verify_chv(struct osmocom_ms *ms, uint8_t chv_no,
- uint8_t *chv, uint8_t length)
-{
- uint8_t buffer[5 + 8];
- int i;
-
- LOGP(DSIM, LOGL_INFO, "VERIFY CHV (CHV%d)\n", chv_no);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_VERIFY_CHV;
- buffer[2] = 0x00;
- buffer[3] = chv_no;
- buffer[4] = 8;
- for (i = 0; i < 8; i++) {
- if (i < length)
- buffer[5 + i] = chv[i];
- else
- buffer[5 + i] = 0xff;
- }
-
- return sim_apdu_send(ms, buffer, 5 + 8);
-}
-
-/* 9.2.10 */
-static int gsm1111_tx_change_chv(struct osmocom_ms *ms, uint8_t chv_no,
- uint8_t *chv_old, uint8_t length_old, uint8_t *chv_new,
- uint8_t length_new)
-{
- uint8_t buffer[5 + 16];
- int i;
-
- LOGP(DSIM, LOGL_INFO, "CHANGE CHV (CHV%d)\n", chv_no);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_CHANGE_CHV;
- buffer[2] = 0x00;
- buffer[3] = chv_no;
- buffer[4] = 8;
- for (i = 0; i < 8; i++) {
- if (i < length_old)
- buffer[5 + i] = chv_old[i];
- else
- buffer[5 + i] = 0xff;
- if (i < length_new)
- buffer[13 + i] = chv_new[i];
- else
- buffer[13 + i] = 0xff;
- }
-
- return sim_apdu_send(ms, buffer, 5 + 16);
-}
-
-/* 9.2.11 */
-static int gsm1111_tx_disable_chv(struct osmocom_ms *ms, uint8_t *chv,
- uint8_t length)
-{
- uint8_t buffer[5 + 8];
- int i;
-
- LOGP(DSIM, LOGL_INFO, "DISABLE CHV (CHV1)\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_DISABLE_CHV;
- buffer[2] = 0x00;
- buffer[3] = 0x01;
- buffer[4] = 8;
- for (i = 0; i < 8; i++) {
- if (i < length)
- buffer[5 + i] = chv[i];
- else
- buffer[5 + i] = 0xff;
- }
-
- return sim_apdu_send(ms, buffer, 5 + 8);
-}
-
-/* 9.2.12 */
-static int gsm1111_tx_enable_chv(struct osmocom_ms *ms, uint8_t *chv,
- uint8_t length)
-{
- uint8_t buffer[5 + 8];
- int i;
-
- LOGP(DSIM, LOGL_INFO, "ENABLE CHV (CHV1)\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_ENABLE_CHV;
- buffer[2] = 0x00;
- buffer[3] = 0x01;
- buffer[4] = 8;
- for (i = 0; i < 8; i++) {
- if (i < length)
- buffer[5 + i] = chv[i];
- else
- buffer[5 + i] = 0xff;
- }
-
- return sim_apdu_send(ms, buffer, 5 + 8);
-}
-
-/* 9.2.13 */
-static int gsm1111_tx_unblock_chv(struct osmocom_ms *ms, uint8_t chv_no,
- uint8_t *chv_unblk, uint8_t length_unblk, uint8_t *chv_new,
- uint8_t length_new)
-{
- uint8_t buffer[5 + 16];
- int i;
-
- LOGP(DSIM, LOGL_INFO, "UNBLOCK CHV (CHV%d)\n", (chv_no == 2) ? 2 : 1);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_UNBLOCK_CHV;
- buffer[2] = 0x00;
- buffer[3] = (chv_no == 1) ? 0 : chv_no;
- buffer[4] = 8;
- for (i = 0; i < 8; i++) {
- if (i < length_unblk)
- buffer[5 + i] = chv_unblk[i];
- else
- buffer[5 + i] = 0xff;
- if (i < length_new)
- buffer[13 + i] = chv_new[i];
- else
- buffer[13 + i] = 0xff;
- }
-
- return sim_apdu_send(ms, buffer, 5 + 16);
-}
-
-/* 9.2.14 */
-static int gsm1111_tx_invalidate(struct osmocom_ms *ms)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "INVALIDATE\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_INVALIDATE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 0;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-
-/* 9.2.15 */
-static int gsm1111_tx_rehabilitate(struct osmocom_ms *ms)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "REHABILITATE\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_REHABLILITATE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 0;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-
-/* 9.2.16 */
-static int gsm1111_tx_run_gsm_algo(struct osmocom_ms *ms, uint8_t *rand)
-{
- uint8_t buffer[5 + 16];
-
- LOGP(DSIM, LOGL_INFO, "RUN GSM ALGORITHM\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_RUN_GSM_ALGO;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 16;
- memcpy(buffer + 5, rand, 16);
-
- return sim_apdu_send(ms, buffer, 5 + 16);
-}
-
-#if 0
-/* 9.2.17 */
-static int gsm1111_tx_sleep(struct osmocom_ms *ms)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "\n");
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_SLEEP;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = 0;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-#endif
-
-/* 9.2.18 */
-static int gsm1111_tx_get_response(struct osmocom_ms *ms, uint8_t length)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "GET RESPONSE (len=%d)\n", length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_GET_RESPONSE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = length;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-
-#if 0
-/* 9.2.19 */
-static int gsm1111_tx_terminal_profile(struct osmocom_ms *ms, uint8_t *data,
- uint8_t length)
-{
- uint8_t buffer[5 + length];
-
- LOGP(DSIM, LOGL_INFO, "TERMINAL PROFILE (len=%d)\n", length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_TERMINAL_PROFILE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = length;
- memcpy(buffer + 5, data, length);
-
- return sim_apdu_send(ms, buffer, 5 + length);
-}
-
-/* 9.2.20 */
-static int gsm1111_tx_envelope(struct osmocom_ms *ms, uint8_t *data,
- uint8_t length)
-{
- uint8_t buffer[5 + length];
-
- LOGP(DSIM, LOGL_INFO, "ENVELOPE (len=%d)\n", length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_ENVELOPE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = length;
- memcpy(buffer + 5, data, length);
-
- return sim_apdu_send(ms, buffer, 5 + length);
-}
-
-/* 9.2.21 */
-static int gsm1111_tx_fetch(struct osmocom_ms *ms, uint8_t length)
-{
- uint8_t buffer[5];
-
- LOGP(DSIM, LOGL_INFO, "FETCH (len=%d)\n", length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_FETCH;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = length;
-
- return sim_apdu_send(ms, buffer, 5);
-}
-
-/* 9.2.22 */
-static int gsm1111_tx_terminal_response(struct osmocom_ms *ms, uint8_t *data,
- uint8_t length)
-{
- uint8_t buffer[5 + length];
-
- LOGP(DSIM, LOGL_INFO, "TERMINAL RESPONSE (len=%d)\n", length);
- buffer[0] = GSM1111_CLASS_GSM;
- buffer[1] = GSM1111_INST_TERMINAL_RESPONSE;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- buffer[4] = length;
- memcpy(buffer + 5, data, length);
-
- return sim_apdu_send(ms, buffer, 5 + length);
-}
-#endif
-
-/*
- * SIM state machine
- */
-
-/* process job */
-static int sim_process_job(struct osmocom_ms *ms)
-{
- struct gsm_sim *sim = &ms->sim;
- uint8_t *payload;
- uint16_t payload_len;
- struct sim_hdr *sh;
- uint8_t cause;
- int i;
-
- /* no current */
- if (!sim->job_msg)
- return 0;
-
- sh = (struct sim_hdr *)sim->job_msg->data;
- payload = sim->job_msg->data + sizeof(*sh);
- payload_len = sim->job_msg->len - sizeof(*sh);
-
- /* do reset before sim reading */
- if (!sim->reset) {
- sim->reset = 1;
- // FIXME: send reset command to L1
- }
-
- /* check MF / DF */
- i = 0;
- while (sh->path[i] && sim->path[i]) {
- if (sh->path[i] != sh->path[i])
- break;
- i++;
- }
- /* if path in message is shorter or if paths are different */
- if (sim->path[i]) {
- LOGP(DSIM, LOGL_INFO, "wrong DF, go MF\n");
- sim->job_state = SIM_JST_SELECT_MFDF;
- /* go MF */
- sim->path[0] = 0;
- return gsm1111_tx_select(ms, 0x3f00);
- }
- /* if path in message is longer */
- if (sh->path[i]) {
- LOGP(DSIM, LOGL_INFO, "requested path is longer, go child %s\n",
- get_df_name(sh->path[i]));
- sim->job_state = SIM_JST_SELECT_MFDF;
- /* select child */
- sim->path[i] = sh->path[i];
- sim->path[i + 1] = 0;
- return gsm1111_tx_select(ms, sh->path[i]);
- }
- /* if paths are equal, continue */
-
- /* set state and trigger SIM process */
- switch (sh->job_type) {
- case SIM_JOB_READ_BINARY:
- case SIM_JOB_UPDATE_BINARY:
- case SIM_JOB_READ_RECORD:
- case SIM_JOB_UPDATE_RECORD:
- case SIM_JOB_SEEK_RECORD:
- case SIM_JOB_INCREASE:
- case SIM_JOB_INVALIDATE:
- case SIM_JOB_REHABILITATE:
- sim->job_state = SIM_JST_SELECT_EF;
- return gsm1111_tx_select(ms, sh->file);
- case SIM_JOB_RUN_GSM_ALGO:
- if (payload_len != 16) {
- LOGP(DSIM, LOGL_ERROR, "random not 16 bytes\n");
- break;
- }
- sim->job_state = SIM_JST_RUN_GSM_ALGO;
- return gsm1111_tx_run_gsm_algo(ms, payload);
- case SIM_JOB_PIN1_UNLOCK:
- 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:
- 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");
- break;
- }
- if (payload_len < 4 || payload_len > 8 || !sim->pin1_len) {
- LOGP(DSIM, LOGL_ERROR, "key 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);
- case SIM_JOB_PIN2_CHANGE:
- if (!sim->pin2_len) {
- LOGP(DSIM, LOGL_ERROR, "no pin set\n");
- break;
- }
- 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);
- case SIM_JOB_PIN1_ENABLE:
- if (!sim->pin1_len) {
- LOGP(DSIM, LOGL_ERROR, "no pin set\n");
- break;
- }
- sim->job_state = SIM_JST_PIN1_ENABLE;
- return gsm1111_tx_enable_chv(ms, sim->pin1, sim->pin1_len);
- case SIM_JOB_PIN1_UNBLOCK:
- if (payload_len < 12 || payload_len > 16) {
- LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n");
- break;
- }
- sim->job_state = SIM_JST_PIN1_UNLOCK;
- memcpy(sim->pin1, payload + 8, payload_len - 8);
- sim->pin1_len = payload_len;
- /* NOTE: CHV1 is coded 0x00 here */
- return gsm1111_tx_unblock_chv(ms, 0x00, payload, 8, payload + 8,
- payload_len - 8);
- case SIM_JOB_PIN2_UNBLOCK:
- if (payload_len < 12 || payload_len > 16) {
- LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\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);
- }
-
- LOGP(DSIM, LOGL_ERROR, "unknown job %x, please fix\n", sh->job_type);
- cause = SIM_CAUSE_REQUEST_ERROR;
- gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1);
-
- return 0;
-}
-
-/* receive SIM response */
-static int sim_apdu_receive(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm_sim *sim = &ms->sim;
- uint8_t *payload;
- uint16_t payload_len;
- uint8_t *data = msg->data;
- int length = msg->len, ef_len;
- uint8_t sw1, sw2;
- uint8_t cause;
- struct sim_hdr *sh;
- struct gsm1111_response_ef *ef;
- struct gsm1111_response_mfdf *mfdf;
- struct gsm1111_response_mfdf_gsm *mfdf_gsm;
- int i;
-
- /* ignore, if current job already gone */
- if (!sim->job_msg) {
- LOGP(DSIM, LOGL_ERROR, "received APDU but no job, "
- "please fix!\n");
- msgb_free(msg);
- return 0;
- }
-
- sh = (struct sim_hdr *)sim->job_msg->data;
- payload = sim->job_msg->data + sizeof(*sh);
- payload_len = sim->job_msg->len - sizeof(*sh);
-
- /* process status */
- if (length < 2) {
- msgb_free(msg);
- return 0;
- }
- sw1 = data[length - 2];
- sw2 = data[length - 1];
- length -= 2;
- LOGP(DSIM, LOGL_INFO, "received APDU (len=%d sw1=0x%02x sw2=0x%02x)\n",
- length, sw1, sw2);
-
- switch (sw1) {
- case GSM1111_STAT_MEM_PROBLEM:
- if (sw2 >= 0x40) {
- LOGP(DSIM, LOGL_NOTICE, "memory of SIM failed\n");
- sim_error:
- cause = SIM_CAUSE_SIM_ERROR;
- gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1);
- msgb_free(msg);
- return 0;
- }
- LOGP(DSIM, LOGL_NOTICE, "memory of SIM is bad (write took %d "
- "times to succeed)\n", sw2);
- /* fall through */
- case GSM1111_STAT_NORMAL:
- case GSM1111_STAT_PROACTIVE:
- case GSM1111_STAT_DL_ERROR:
- 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);
- msgb_free(msg);
- return 0;
- }
-
-
- switch (sim->job_state) {
- /* step 1: after selecting MF / DF, request the response */
- case SIM_JST_SELECT_MFDF:
- /* not enough data */
- if (sw2 < 22) {
- LOGP(DSIM, LOGL_NOTICE, "expecting minimum 22 bytes\n");
- goto sim_error;
- }
- /* request response */
- sim->job_state = SIM_JST_SELECT_MFDF_RESP;
- gsm1111_tx_get_response(ms, sw2);
- msgb_free(msg);
- return 0;
- /* step 2: after getting response of selecting MF / DF, continue
- * to "process_job".
- */
- case SIM_JST_SELECT_MFDF_RESP:
- if (length < 22) {
- LOGP(DSIM, LOGL_NOTICE, "expecting minimum 22 bytes\n");
- goto sim_error;
- }
- mfdf = (struct gsm1111_response_mfdf *)data;
- mfdf_gsm = (struct gsm1111_response_mfdf_gsm *)(data + 13);
- /* if MF was selected, but MF is not indicated */
- if (ntohs(mfdf->file_id) != 0x3f00 && sim->path[0] == 0) {
- goto sim_error;
- }
- /* if MF was selected, but type is not indicated */
- if (mfdf->tof != GSM1111_TOF_MF && sim->path[0]) {
- goto sim_error;
- }
- /* if DF was selected, but this DF is not indicated */
- i = 0;
- while (sim->path[i + 1])
- i++;
- if (ntohs(mfdf->file_id) != sim->path[i]) {
- goto sim_error;
- }
- /* if DF was selected, but type is not indicated */
- if (mfdf->tof != GSM1111_TOF_DF) {
- goto sim_error;
- }
- /* now continue */
- msgb_free(msg);
- return sim_process_job(ms);
- /* step 1: after selecting EF, request response of SELECT */
- case SIM_JST_SELECT_EF:
- /* not enough data */
- if (sw2 < 14) {
- LOGP(DSIM, LOGL_NOTICE, "expecting minimum 14 bytes\n");
- goto sim_error;
- }
- /* request response */
- sim->job_state = SIM_JST_SELECT_EF_RESP;
- gsm1111_tx_get_response(ms, sw2);
- msgb_free(msg);
- return 0;
- /* step 2: after getting response of selecting EF, do file command */
- case SIM_JST_SELECT_EF_RESP:
- if (length < 14) {
- LOGP(DSIM, LOGL_NOTICE, "expecting minimum 14 bytes\n");
- goto sim_error;
- }
- ef = (struct gsm1111_response_ef *)data;
- /* if EF was selected, but type is not indicated */
- if (ntohs(ef->file_id) != sim->file) {
- goto sim_error;
- }
- /* get length of file */
- ef_len = ntohs(ef->file_size);
- /* do file command */
- sim->job_state = SIM_JST_WAIT_FILE;
- switch (sh->job_type) {
- case SIM_JOB_READ_BINARY:
- // FIXME: do chunks when greater or equal 256 bytes */
- gsm1111_tx_read_binary(ms, 0, ef_len);
- break;
- case SIM_JOB_UPDATE_BINARY:
- // FIXME: do chunks when greater or equal 256 bytes */
- if (ef_len < length) {
- LOGP(DSIM, LOGL_NOTICE, "selected file is "
- "smaller (%d) than data to update "
- "(%d)\n", ef_len, length);
- goto request_error;
- }
- gsm1111_tx_update_binary(ms, 0, data, length);
- break;
- case SIM_JOB_READ_RECORD:
- gsm1111_tx_read_record(ms, sh->rec_no, sh->rec_mode,
- ef_len);
- break;
- case SIM_JOB_UPDATE_RECORD:
- if (ef_len != length) {
- LOGP(DSIM, LOGL_NOTICE, "selected file length "
- "(%d) does not equal record to update "
- "(%d)\n", ef_len, length);
- goto request_error;
- }
- gsm1111_tx_update_record(ms, sh->rec_no, sh->rec_mode,
- data, length);
- break;
- case SIM_JOB_SEEK_RECORD:
- gsm1111_tx_seek(ms, sh->seek_type_mode, data, length);
- break;
- case SIM_JOB_INCREASE:
- if (length != 4) {
- LOGP(DSIM, LOGL_ERROR, "expecting uint32_t as "
- "value lenght, but got %d bytes\n",
- length);
- goto request_error;
- }
- gsm1111_tx_increase(ms, *((uint32_t *)data));
- break;
- case SIM_JOB_INVALIDATE:
- gsm1111_tx_invalidate(ms);
- break;
- case SIM_JOB_REHABILITATE:
- gsm1111_tx_rehabilitate(ms);
- break;
- }
- msgb_free(msg);
- return 0;
- /* step 3: after processing file command, job is done */
- case SIM_JST_WAIT_FILE:
- /* reply job with data */
- gsm_sim_reply(ms, SIM_JOB_OK, data, length);
- msgb_free(msg);
- return 0;
- /* step 1: after running GSM algorithm, request response */
- case SIM_JST_RUN_GSM_ALGO:
- /* not enough data */
- if (sw2 < 12) {
- LOGP(DSIM, LOGL_NOTICE, "expecting minimum 12 bytes\n");
- goto sim_error;
- }
- /* request response */
- sim->job_state = SIM_JST_RUN_GSM_ALGO_RESP;
- gsm1111_tx_get_response(ms, sw2);
- msgb_free(msg);
- return 0;
- /* step 2: after processing GSM command, job is done */
- case SIM_JST_RUN_GSM_ALGO_RESP:
- /* reply job with data */
- gsm_sim_reply(ms, SIM_JOB_OK, data, length);
- msgb_free(msg);
- return 0;
- case SIM_JST_PIN1_UNLOCK:
- case SIM_JST_PIN1_CHANGE:
- case SIM_JST_PIN1_DISABLE:
- case SIM_JST_PIN1_ENABLE:
- case SIM_JST_PIN1_UNBLOCK:
- case SIM_JST_PIN2_UNLOCK:
- case SIM_JST_PIN2_CHANGE:
- case SIM_JST_PIN2_UNBLOCK:
- /* reply job with data */
- gsm_sim_reply(ms, SIM_JOB_OK, data, length);
- msgb_free(msg);
- return 0;
- }
-
- LOGP(DSIM, LOGL_ERROR, "unknown state %u, please fix!\n",
- sim->job_state);
- goto request_error;
-}
-
-/*
- * API
- */
-
-/* open access to sim */
-uint32_t sim_open(struct osmocom_ms *ms,
- void (*cb)(struct osmocom_ms *ms, struct msgb *msg))
-{
- struct gsm_sim *sim = &ms->sim;
- struct gsm_sim_handler *handler;
-
- /* create handler and attach */
- handler = talloc_zero(l23_ctx, struct gsm_sim_handler);
- if (!handler)
- return 0;
- handler->handle = new_handle++;
- handler->cb = cb;
- llist_add_tail(&handler->entry, &sim->handlers);
-
- return handler->handle;
-}
-
-/* close access to sim */
-void sim_close(struct osmocom_ms *ms, uint32_t handle)
-{
- struct gsm_sim *sim = &ms->sim;
- struct gsm_sim_handler *handler;
-
- handler = sim_get_handler(sim, handle);
- if (!handle)
- return;
-
- /* kill ourself */
- llist_del(&handler->entry);
- talloc_free(handler);
-}
-
-/* send job */
-void sim_job(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm_sim *sim = &ms->sim;
-
- msgb_enqueue(&sim->jobs, msg);
-}
-
-/*
- * init
- */
-
-int gsm_sim_init(struct osmocom_ms *ms)
-{
- struct gsm_sim *sim = &ms->sim;
-
- /* current path is root (MF), no file selected */
- sim->path[0] = 0;
- sim->file = 0;
-
- INIT_LLIST_HEAD(&sim->handlers);
- INIT_LLIST_HEAD(&sim->jobs);
-
- LOGP(DSIM, LOGL_INFO, "init SIM client\n");
-
- return 0;
-}
-
-int gsm_sim_exit(struct osmocom_ms *ms)
-{
- struct gsm_sim *sim = &ms->sim;
- struct gsm_sim_handler *handler, *handler2;
- struct msgb *msg;
-
- LOGP(DSIM, LOGL_INFO, "exit SIM client\n");
-
- /* remove pending job msg */
- if (sim->job_msg) {
- msgb_free(sim->job_msg);
- sim->job_msg = NULL;
- }
- /* flush handlers */
- llist_for_each_entry_safe(handler, handler2, &sim->handlers, entry)
- sim_close(ms, handler->handle);
- /* flush jobs */
- while ((msg = msgb_dequeue(&sim->jobs)))
- msgb_free(msg);
-
- return 0;
-}
-
-
-
-
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;
}
uint8_t length)
{
struct gsm_subscriber *subscr = &ms->subscr;
+ char *imsi;
/* get actual length */
if (length < 1)
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);
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;
}
/* 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;
}
vty_notify(ms, "PIN is blocked\n");
break;
default:
+ if (!subscr_sim_files[subscr->sim_file_index].
+ mandatory) {
+ LOGP(DMM, LOGL_NOTICE, "SIM reading failed, "
+ "ignoring!\n");
+ goto ignore;
+ }
LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n");
vty_notify(ms, NULL);
}
}
+ignore:
msgb_free(msg);
/* trigger next file */