/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2010 Intel Corporation. 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 version 2 as * published by the Free Software Foundation. * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "smsutil.h" #include "stkutil.h" #include "simutil.h" #include "util.h" enum stk_data_object_flag { DATAOBJ_FLAG_MANDATORY = 1, DATAOBJ_FLAG_MINIMUM = 2 }; typedef gboolean (*dataobj_handler)(struct comprehension_tlv_iter *, void *); /* * Defined in TS 102.223 Section 8.13 * GSM SMS PDUs are limited to 164 bytes according to 23.040 */ struct gsm_sms_tpdu { unsigned int len; unsigned char tpdu[164]; }; static char *decode_text(unsigned char dcs, int len, const unsigned char *data) { char *utf8; switch (dcs) { case 0x00: { long written; unsigned long max_to_unpack = len * 8 / 7; unsigned char *unpacked = unpack_7bit(data, len, 0, FALSE, max_to_unpack, &written, 0); if (unpacked == NULL) return FALSE; utf8 = convert_gsm_to_utf8(unpacked, written, NULL, NULL, 0); g_free(unpacked); break; } case 0x04: utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0); break; case 0x08: utf8 = g_convert((const gchar *) data, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); break; default: utf8 = NULL; } return utf8; } /* For data object only to indicate its existence */ static gboolean parse_dataobj_common_bool(struct comprehension_tlv_iter *iter, gboolean *out) { if (comprehension_tlv_iter_get_length(iter) != 0) return FALSE; *out = TRUE; return TRUE; } /* For data object that only has one byte */ static gboolean parse_dataobj_common_byte(struct comprehension_tlv_iter *iter, unsigned char *out) { const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *out = data[0]; return TRUE; } /* For data object that only has a byte array with undetermined length */ static gboolean parse_dataobj_common_byte_array( struct comprehension_tlv_iter *iter, struct stk_common_byte_array *array) { const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); array->len = len; array->array = g_try_malloc(len); if (array->array == NULL) return FALSE; memcpy(array->array, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.1 */ static gboolean parse_dataobj_address(struct comprehension_tlv_iter *iter, void *user) { struct stk_address *addr = user; const unsigned char *data; unsigned int len; char *number; len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); number = g_try_malloc(len * 2 - 1); if (number == NULL) return FALSE; addr->ton_npi = data[0]; addr->number = number; extract_bcd_number(data + 1, len - 1, addr->number); return TRUE; } /* Defined in TS 102.223 Section 8.2 */ static gboolean parse_dataobj_alpha_id(struct comprehension_tlv_iter *iter, void *user) { char **alpha_id = user; const unsigned char *data; unsigned int len; char *utf8; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); utf8 = sim_string_to_utf8(data, len); if (utf8 == NULL) return FALSE; *alpha_id = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.3 */ static gboolean parse_dataobj_subaddress(struct comprehension_tlv_iter *iter, void *user) { struct stk_subaddress *subaddr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; if (len > sizeof(subaddr->subaddr)) return FALSE; data = comprehension_tlv_iter_get_data(iter); subaddr->len = len; memcpy(subaddr->subaddr, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.4 */ static gboolean parse_dataobj_ccp(struct comprehension_tlv_iter *iter, void *user) { struct stk_ccp *ccp = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; if (len > sizeof(ccp->ccp)) return FALSE; data = comprehension_tlv_iter_get_data(iter); ccp->len = len; memcpy(ccp->ccp, data, len); return TRUE; } /* Described in TS 102.223 Section 8.8 */ static gboolean parse_dataobj_duration(struct comprehension_tlv_iter *iter, void *user) { struct stk_duration *duration = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] > 0x02) return FALSE; if (data[1] == 0) return FALSE; duration->unit = data[0]; duration->interval = data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.9 */ static gboolean parse_dataobj_item(struct comprehension_tlv_iter *iter, void *user) { struct stk_item *item = user; const unsigned char *data; unsigned int len; char *utf8; len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* The identifier is between 0x01 and 0xFF */ if (data[0] == 0) return FALSE; utf8 = sim_string_to_utf8(data + 1, len - 1); if (utf8 == NULL) return FALSE; item->id = data[0]; item->text = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.10 */ static gboolean parse_dataobj_item_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *id = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *id = data[0]; return TRUE; } /* Defined in TS 102.223 Section 8.11 */ static gboolean parse_dataobj_response_len(struct comprehension_tlv_iter *iter, void *user) { struct stk_response_length *response_len = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); response_len->min = data[0]; response_len->max = data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.12 */ static gboolean parse_dataobj_result(struct comprehension_tlv_iter *iter, void *user) { struct stk_result *result = user; const unsigned char *data; unsigned int len; unsigned char *additional; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); if ((len < 2) && ((data[0] == 0x20) || (data[0] == 0x21) || (data[0] == 0x26) || (data[0] == 0x38) || (data[0] == 0x39) || (data[0] == 0x3a) || (data[0] == 0x3c) || (data[0] == 0x3d))) return FALSE; additional = g_try_malloc(len - 1); if (additional == NULL) return FALSE; result->type = data[0]; result->additional_len = len - 1; result->additional = additional; memcpy(result->additional, data + 1, len - 1); return TRUE; } /* Defined in TS 102.223 Section 8.13 */ static gboolean parse_dataobj_gsm_sms_tpdu(struct comprehension_tlv_iter *iter, void *user) { struct gsm_sms_tpdu *tpdu = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1 || len > sizeof(tpdu->tpdu)) return FALSE; data = comprehension_tlv_iter_get_data(iter); tpdu->len = len; memcpy(tpdu->tpdu, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.15 */ static gboolean parse_dataobj_text(struct comprehension_tlv_iter *iter, void *user) { char **text = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *data = comprehension_tlv_iter_get_data(iter); char *utf8; /* DCS followed by some text, cannot be 1 */ if (len == 1) return FALSE; if (len == 0) { *text = NULL; return TRUE; } utf8 = decode_text(data[0], len - 1, data + 1); if (utf8 == NULL) return FALSE; *text = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.16 */ static gboolean parse_dataobj_tone(struct comprehension_tlv_iter *iter, void *user) { unsigned char *tone = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *tone = data[0]; return TRUE; } /* Defined in TS 102.223 Section 8.18 */ static gboolean parse_dataobj_file_list(struct comprehension_tlv_iter *iter, void *user) { GSList **fl = user; const unsigned char *data; unsigned int len; unsigned int i; unsigned int start; struct stk_file *sf; unsigned char last_type; len = comprehension_tlv_iter_get_length(iter); if (len < 5) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* SIM EFs always start with ROOT MF, 0x3f */ if (data[1] != 0x3f) return FALSE; start = 1; last_type = 0x3f; for (i = 3; i < len; i += 2) { /* Check the validity of file type. * According to TS 11.11, each file id contains of two bytes, * in which the first byte is the type of file. For GSM is: * 0x3f: master file * 0x7f: 1st level dedicated file * 0x5f: 2nd level dedicated file * 0x2f: elementary file under the master file * 0x6f: elementary file under 1st level dedicated file * 0x4f: elementary file under 2nd level dedicated file */ switch (data[i]) { case 0x3f: if ((last_type != 0x2f) && (last_type != 0x6f) && (last_type != 0x4f)) goto error; start = i; break; case 0x2f: if (last_type != 0x3f) goto error; break; case 0x6f: if (last_type != 0x7f) goto error; break; case 0x4f: if (last_type != 0x5f) goto error; break; case 0x7f: if (last_type != 0x3f) goto error; break; case 0x5f: if (last_type != 0x7f) goto error; break; default: goto error; } if ((data[i] == 0x2f) || (data[i] == 0x6f) || (data[i] == 0x4f)) { if (i + 1 >= len) goto error; sf = g_try_new0(struct stk_file, 1); if (sf == NULL) goto error; sf->len = i - start + 2; memcpy(sf->file, data + start, i - start + 2); *fl = g_slist_prepend(*fl, sf); } last_type = data[i]; } if ((data[len - 2] != 0x2f) && (data[len - 2] != 0x6f) && (data[len - 2] != 0x4f)) goto error; *fl = g_slist_reverse(*fl); return TRUE; error: g_slist_foreach(*fl, (GFunc)g_free, NULL); g_slist_free(*fl); return FALSE; } /* Defined in TS 102.223 Section 8.19 */ static gboolean parse_dataobj_location_info(struct comprehension_tlv_iter *iter, void *user) { struct stk_location_info *li = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if ((len != 5) && (len != 7) && (len != 9)) return FALSE; data = comprehension_tlv_iter_get_data(iter); sim_parse_mcc_mnc(data, li->mcc, li->mnc); li->lac_tac = (data[3] << 8) + data[4]; if (len >= 7) { li->has_ci = TRUE; li->ci = (data[5] << 8) + data[6]; } if (len == 9) { li->has_ext_ci = TRUE; li->ext_ci = (data[7] << 8) + data[8]; } return TRUE; } /* Defined in TS 102.223 Section 8.20. * * According to 3GPP TS 24.008, Section 10.5.1.4, IMEI is composed of * 15 digits and totally 8 bytes are used to represent it. * * Bits 1-3 of first byte represent the type of identity, and they * are 0 1 0 separately for IMEI. Bit 4 of first byte is the odd/even * indication, and it's 1 to indicate IMEI has odd number of digits (15). * The rest bytes are coded using BCD coding. * * For example, if the IMEI is "123456789012345", then it's coded as * "1A 32 54 76 98 10 32 54". */ static gboolean parse_dataobj_imei(struct comprehension_tlv_iter *iter, void *user) { char **imei = user; const unsigned char *data; unsigned int len; static const char digit_lut[] = "0123456789*#abc\0"; len = comprehension_tlv_iter_get_length(iter); if (len != 8) return FALSE; data = comprehension_tlv_iter_get_data(iter); if ((data[0] & 0x0f) != 0x0a) return FALSE; /* Assume imei is at least 16 bytes long (15 for imei + null) */ (*imei)[0] = digit_lut[(data[0] & 0xf0) >> 4]; extract_bcd_number(data + 1, 7, *imei + 1); return TRUE; } /* Defined in TS 102.223 Section 8.21 */ static gboolean parse_dataobj_help_request(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in TS 102.223 Section 8.22 */ static gboolean parse_dataobj_network_measurement_results( struct comprehension_tlv_iter *iter, void *user) { unsigned char **nmr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len != 0x10) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* Assume network measurement result is 16 bytes long */ memcpy(*nmr, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.23 */ static gboolean parse_dataobj_default_text(struct comprehension_tlv_iter *iter, void *user) { char **text = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *data = comprehension_tlv_iter_get_data(iter); char *utf8; /* DCS followed by some text, cannot be 1 */ if (len <= 1) return FALSE; utf8 = decode_text(data[0], len - 1, data + 1); if (utf8 == NULL) return FALSE; *text = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.24 */ static gboolean parse_dataobj_items_next_action_indicator( struct comprehension_tlv_iter *iter, void *user) { struct stk_items_next_action_indicator *inai = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(inai->list))) return FALSE; data = comprehension_tlv_iter_get_data(iter); inai->len = len; memcpy(inai->list, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.25 */ static gboolean parse_dataobj_event_list(struct comprehension_tlv_iter *iter, void *user) { struct stk_event_list *el = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(el->list))) return FALSE; data = comprehension_tlv_iter_get_data(iter); el->len = len; memcpy(el->list, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.26 */ static gboolean parse_dataobj_cause(struct comprehension_tlv_iter *iter, void *user) { struct stk_cause *cause = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len == 1) || (len > sizeof(cause->cause))) return FALSE; cause->has_cause = TRUE; if (len == 0) return TRUE; data = comprehension_tlv_iter_get_data(iter); cause->len = len; memcpy(cause->cause, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.27 */ static gboolean parse_dataobj_location_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.28 */ static gboolean parse_dataobj_transaction_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_transaction_id *ti = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(ti->list))) return FALSE; data = comprehension_tlv_iter_get_data(iter); ti->len = len; memcpy(ti->list, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.30 */ static gboolean parse_dataobj_call_control_requested_action( struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.31 */ static gboolean parse_dataobj_icon_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_icon_id *id = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); id->qualifier = data[0]; id->id = data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.32 */ static gboolean parse_dataobj_item_icon_id_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_item_icon_id_list *iiil = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 2) || (len > 127)) return FALSE; data = comprehension_tlv_iter_get_data(iter); iiil->qualifier = data[0]; iiil->len = len - 1; memcpy(iiil->list, data + 1, iiil->len); return TRUE; } /* Defined in TS 102.223 Section 8.33 */ static gboolean parse_dataobj_card_reader_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.34 */ static gboolean parse_dataobj_card_atr( struct comprehension_tlv_iter *iter, void *user) { struct stk_card_atr *ca = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(ca->atr))) return FALSE; data = comprehension_tlv_iter_get_data(iter); ca->len = len; memcpy(ca->atr, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.35 */ static gboolean parse_dataobj_c_apdu( struct comprehension_tlv_iter *iter, void *user) { struct stk_c_apdu *ca = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 4) || (len > 241)) return FALSE; data = comprehension_tlv_iter_get_data(iter); ca->cla = data[0]; ca->ins = data[1]; ca->p1 = data[2]; ca->p2 = data[3]; /* lc is 0 has the same meaning as lc is absent. But le is 0 means * the maximum number of bytes expected in the response data field * is 256. So we need to rely on has_le to know if it presents. */ if (len > 5) { ca->lc = data[4]; if (ca->lc > sizeof(ca->data)) return FALSE; memcpy(ca->data, data+5, ca->lc); if ((len - ca->lc) == 6) { ca->le = data[len-1]; ca->has_le = TRUE; } else if (len - ca->lc != 5) return FALSE; } else if (len == 5) { ca->lc = 0; ca->le = data[4]; ca->has_le = TRUE; } return TRUE; } /* Defined in TS 102.223 Section 8.36 */ static gboolean parse_dataobj_r_apdu( struct comprehension_tlv_iter *iter, void *user) { struct stk_r_apdu *ra = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 2) || (len > 239)) return FALSE; data = comprehension_tlv_iter_get_data(iter); ra->sw1 = data[len-2]; ra->sw2 = data[len-1]; if (len > 2) { ra->len = len - 2; memcpy(ra->data, data, ra->len); } else ra->len = 0; return TRUE; } /* Defined in 102.223 Section 8.43 */ static gboolean parse_dataobj_imm_resp(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in TS 102.223 Section 8.72 */ static gboolean parse_dataobj_text_attr(struct comprehension_tlv_iter *iter, void *user) { struct stk_text_attribute *attr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len > sizeof(attr->attributes)) return FALSE; data = comprehension_tlv_iter_get_data(iter); memcpy(attr->attributes, data, len); attr->len = len; return TRUE; } /* Defined in TS 102.223 Section 8.80 */ static gboolean parse_dataobj_frame_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *frame_id = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] >= 0x10) return FALSE; *frame_id = data[0]; return TRUE; } static dataobj_handler handler_for_type(enum stk_data_object_type type) { switch (type) { case STK_DATA_OBJECT_TYPE_ADDRESS: return parse_dataobj_address; case STK_DATA_OBJECT_TYPE_ALPHA_ID: return parse_dataobj_alpha_id; case STK_DATA_OBJECT_TYPE_SUBADDRESS: return parse_dataobj_subaddress; case STK_DATA_OBJECT_TYPE_CCP: return parse_dataobj_ccp; case STK_DATA_OBJECT_TYPE_DURATION: return parse_dataobj_duration; case STK_DATA_OBJECT_TYPE_ITEM: return parse_dataobj_item; case STK_DATA_OBJECT_TYPE_ITEM_ID: return parse_dataobj_item_id; case STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH: return parse_dataobj_response_len; case STK_DATA_OBJECT_TYPE_RESULT: return parse_dataobj_result; case STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU: return parse_dataobj_gsm_sms_tpdu; case STK_DATA_OBJECT_TYPE_TEXT: return parse_dataobj_text; case STK_DATA_OBJECT_TYPE_TONE: return parse_dataobj_tone; case STK_DATA_OBJECT_TYPE_FILE_LIST: return parse_dataobj_file_list; case STK_DATA_OBJECT_TYPE_LOCATION_INFO: return parse_dataobj_location_info; case STK_DATA_OBJECT_TYPE_IMEI: return parse_dataobj_imei; case STK_DATA_OBJECT_TYPE_HELP_REQUEST: return parse_dataobj_help_request; case STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS: return parse_dataobj_network_measurement_results; case STK_DATA_OBJECT_TYPE_DEFAULT_TEXT: return parse_dataobj_default_text; case STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR: return parse_dataobj_items_next_action_indicator; case STK_DATA_OBJECT_TYPE_EVENT_LIST: return parse_dataobj_event_list; case STK_DATA_OBJECT_TYPE_CAUSE: return parse_dataobj_cause; case STK_DATA_OBJECT_TYPE_LOCATION_STATUS: return parse_dataobj_location_status; case STK_DATA_OBJECT_TYPE_TRANSACTION_ID: return parse_dataobj_transaction_id; case STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION: return parse_dataobj_call_control_requested_action; case STK_DATA_OBJECT_TYPE_ICON_ID: return parse_dataobj_icon_id; case STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST: return parse_dataobj_item_icon_id_list; case STK_DATA_OBJECT_TYPE_CARD_READER_STATUS: return parse_dataobj_card_reader_status; case STK_DATA_OBJECT_TYPE_CARD_ATR: return parse_dataobj_card_atr; case STK_DATA_OBJECT_TYPE_C_APDU: return parse_dataobj_c_apdu; case STK_DATA_OBJECT_TYPE_R_APDU: return parse_dataobj_r_apdu; case STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE: return parse_dataobj_imm_resp; case STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE: return parse_dataobj_text_attr; case STK_DATA_OBJECT_TYPE_FRAME_ID: return parse_dataobj_frame_id; default: return NULL; }; } struct dataobj_handler_entry { enum stk_data_object_type type; int flags; void *data; gboolean parsed; }; static gboolean parse_dataobj(struct comprehension_tlv_iter *iter, enum stk_data_object_type type, ...) { GSList *entries = NULL; GSList *l; va_list args; gboolean minimum_set = TRUE; va_start(args, type); while (type != STK_DATA_OBJECT_TYPE_INVALID) { struct dataobj_handler_entry *entry; entry = g_new0(struct dataobj_handler_entry, 1); entry->type = type; entry->flags = va_arg(args, int); entry->data = va_arg(args, void *); type = va_arg(args, enum stk_data_object_type); entries = g_slist_prepend(entries, entry); } entries = g_slist_reverse(entries); for (l = entries; l; l = l->next) { dataobj_handler handler; struct dataobj_handler_entry *entry = l->data; handler = handler_for_type(entry->type); if (handler == NULL) continue; if (comprehension_tlv_iter_get_tag(iter) == entry->type) { if (handler(iter, entry->data)) entry->parsed = TRUE; if (comprehension_tlv_iter_next(iter) == FALSE) break; } } for (l = entries; l; l = l->next) { struct dataobj_handler_entry *entry = l->data; if ((entry->flags & DATAOBJ_FLAG_MINIMUM) && entry->parsed == FALSE) minimum_set = TRUE; } g_slist_foreach(entries, (GFunc)g_free, NULL); g_slist_free(entries); return minimum_set; } static void destroy_display_text(struct stk_command *command) { g_free(command->display_text.text); } static gboolean parse_display_text(struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_display_text *obj = &command->display_text; gboolean ret; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return FALSE; if (command->dst != STK_DEVICE_IDENTITY_TYPE_DISPLAY) return FALSE; obj->frame_id = 0xFF; ret = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0, &obj->immediate_response, STK_DATA_OBJECT_TYPE_DURATION, 0, &obj->duration, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attribute, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); if (ret == FALSE) return FALSE; command->destructor = destroy_display_text; return TRUE; } static void destroy_get_inkey(struct stk_command *command) { g_free(command->get_inkey.text); } static gboolean parse_get_inkey(struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_display_text *obj = &command->get_inkey; gboolean ret; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return FALSE; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return FALSE; obj->frame_id = 0xFF; ret = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0, &obj->immediate_response, STK_DATA_OBJECT_TYPE_DURATION, 0, &obj->duration, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attribute, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); if (ret == FALSE) return FALSE; command->destructor = destroy_get_inkey; return TRUE; } static void destroy_get_input(struct stk_command *command) { g_free(command->get_input.text); } static gboolean parse_get_input(struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_get_input *obj = &command->get_input; gboolean ret; obj->frame_id = 0xFF; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return FALSE; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return FALSE; ret = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->response_length, STK_DATA_OBJECT_TYPE_DEFAULT_TEXT, 0, &obj->default_text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attribute, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); if (ret == FALSE) return FALSE; command->destructor = destroy_get_input; return TRUE; } static void destroy_send_sms(struct stk_command *command) { g_free(command->send_sms.alpha_id); g_free(command->send_sms.address.number); } static gboolean parse_send_sms(struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_send_sms *obj = &command->send_sms; struct gsm_sms_tpdu tpdu; gboolean ret; obj->frame_id = 0xFF; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return FALSE; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return FALSE; ret = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ADDRESS, 0, &obj->address, STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU, 0, &tpdu, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attribute, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); if (ret == FALSE) return FALSE; command->destructor = destroy_send_sms; if (sms_decode(tpdu.tpdu, tpdu.len, TRUE, tpdu.len, &obj->gsm_sms) == FALSE) { command->destructor(command); return FALSE; } return TRUE; } struct stk_command *stk_command_new_from_pdu(const unsigned char *pdu, unsigned int len) { struct ber_tlv_iter ber; struct comprehension_tlv_iter iter; const unsigned char *data; struct stk_command *command; gboolean ok; ber_tlv_iter_init(&ber, pdu, len); if (ber_tlv_iter_next(&ber) != TRUE) return NULL; /* We should be wrapped in a Proactive UICC Command Tag 0xD0 */ if (ber_tlv_iter_get_short_tag(&ber) != 0xD0) return NULL; ber_tlv_iter_recurse_comprehension(&ber, &iter); /* * Now parse actual command details, they come in order with * Command Details TLV first, followed by Device Identities TLV */ if (comprehension_tlv_iter_next(&iter) != TRUE) return NULL; if (comprehension_tlv_iter_get_tag(&iter) != STK_DATA_OBJECT_TYPE_COMMAND_DETAILS) return NULL; if (comprehension_tlv_iter_get_length(&iter) != 0x03) return NULL; data = comprehension_tlv_iter_get_data(&iter); command = g_new0(struct stk_command, 1); command->number = data[0]; command->type = data[1]; command->qualifier = data[2]; if (comprehension_tlv_iter_next(&iter) != TRUE) goto fail; if (comprehension_tlv_iter_get_tag(&iter) != STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES) goto fail; if (comprehension_tlv_iter_get_length(&iter) != 0x02) goto fail; data = comprehension_tlv_iter_get_data(&iter); command->src = data[0]; command->dst = data[1]; if (comprehension_tlv_iter_next(&iter) != TRUE) return FALSE; switch (command->type) { case STK_COMMAND_TYPE_DISPLAY_TEXT: ok = parse_display_text(command, &iter); break; case STK_COMMAND_TYPE_GET_INKEY: ok = parse_get_inkey(command, &iter); break; case STK_COMMAND_TYPE_GET_INPUT: ok = parse_get_input(command, &iter); break; case STK_COMMAND_TYPE_SEND_SMS: ok = parse_send_sms(command, &iter); break; default: ok = FALSE; break; }; if (ok) return command; fail: if (command->destructor) command->destructor(command); g_free(command); return NULL; } void stk_command_free(struct stk_command *command) { if (command->destructor) command->destructor(command); g_free(command); }