static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, size_t len) { int _errno = 0; if (send(data->sock, msg, len, 0) < 0) { _errno = errno; perror("send[EAP-SIM DB UNIX]"); } if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || _errno == ECONNREFUSED) { /* Try to reconnect */ eap_sim_db_close_socket(data); if (eap_sim_db_open_socket(data) < 0) return -1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " "external server"); if (send(data->sock, msg, len, 0) < 0) { perror("send[EAP-SIM DB UNIX]"); return -1; } } return 0; }
/** * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface * @config: Configuration data (e.g., file name) * @get_complete_cb: Callback function for reporting availability of triplets * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ void * eap_sim_db_init(const char *config, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx) { struct eap_sim_db_data *data; data = wpa_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->sock = -1; data->get_complete_cb = get_complete_cb; data->ctx = ctx; data->fname = VM_STRDUP(config); if (data->fname == NULL) goto fail; if (strncmp(data->fname, "unix:", 5) == 0) { if (eap_sim_db_open_socket(data)) goto fail; } return data; fail: eap_sim_db_close_socket(data); VM_FREE(data->fname); VM_FREE(data); return NULL; }
/** * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface * @config: Configuration data (e.g., file name) * @db_timeout: Database lookup timeout * @get_complete_cb: Callback function for reporting availability of triplets * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ struct eap_sim_db_data * eap_sim_db_init(const char *config, unsigned int db_timeout, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx) { struct eap_sim_db_data *data; char *pos; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->sock = -1; data->get_complete_cb = get_complete_cb; data->ctx = ctx; data->eap_sim_db_timeout = db_timeout; data->fname = os_strdup(config); if (data->fname == NULL) goto fail; pos = os_strstr(data->fname, " db="); if (pos) { *pos = '\0'; #ifdef CONFIG_SQLITE pos += 4; data->sqlite_db = db_open(pos); if (data->sqlite_db == NULL) goto fail; #endif /* CONFIG_SQLITE */ } if (os_strncmp(data->fname, "unix:", 5) == 0) { if (eap_sim_db_open_socket(data)) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " "connection not available - will retry " "later"); } } return data; fail: eap_sim_db_close_socket(data); os_free(data->fname); os_free(data); return NULL; }
/** * eap_sim_db_get_gsm_triplets - Get GSM triplets * @priv: Private data pointer from eap_sim_db_init() * @identity: User name identity * @identity_len: Length of identity in bytes * @max_chal: Maximum number of triplets * @_rand: Buffer for RAND values * @kc: Buffer for Kc values * @sres: Buffer for SRES values * @cb_session_ctx: Session callback context for get_complete_cb() * Returns: Number of triplets received (has to be less than or equal to * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the * callback function registered with eap_sim_db_init() will be called once the * results become available. * * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in * ASCII format. * * When using an external server for GSM triplets, this function can always * start a request and return EAP_SIM_DB_PENDING immediately if authentication * triplets are not available. Once the triplets are received, callback * function registered with eap_sim_db_init() is called to notify EAP state * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() * function will then be called again and the newly received triplets will then * be given to the caller. */ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, size_t identity_len, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx) { struct eap_sim_db_data *data = priv; struct eap_sim_db_pending *entry; int len, ret; size_t i; char msg[40]; if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); return EAP_SIM_DB_FAILURE; } identity++; identity_len--; for (i = 0; i < identity_len; i++) { if (identity[i] == '@') { identity_len = i; break; } } if (identity_len + 1 > sizeof(entry->imsi)) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); return EAP_SIM_DB_FAILURE; } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI", identity, identity_len); entry = eap_sim_db_get_pending(data, identity, identity_len, 0); if (entry) { int num_chal; if (entry->state == FAILURE) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "failure"); VM_FREE(entry); return EAP_SIM_DB_FAILURE; } if (entry->state == PENDING) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "still pending"); eap_sim_db_add_pending(data, entry); return EAP_SIM_DB_PENDING; } wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "%d challenges", entry->u.sim.num_chal); num_chal = entry->u.sim.num_chal; if (num_chal > max_chal) num_chal = max_chal; memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN); memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); VM_FREE(entry); return num_chal; } if (data->sock < 0) { if (eap_sim_db_open_socket(data) < 0) return EAP_SIM_DB_FAILURE; } len = snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); if (len < 0 || len + identity_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; memcpy(msg + len, identity, identity_len); len += identity_len; ret = snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return EAP_SIM_DB_FAILURE; len += ret; wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " "data for IMSI", identity, identity_len); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; entry = wpa_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; os_get_time(&entry->timestamp); memcpy(entry->imsi, identity, identity_len); entry->imsi_len = identity_len; entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); eap_sim_db_expire_pending(data); return EAP_SIM_DB_PENDING; }
/** * eap_sim_db_get_aka_auth - Get AKA authentication values * @priv: Private data pointer from eap_sim_db_init() * @identity: User name identity * @identity_len: Length of identity in bytes * @_rand: Buffer for RAND value * @autn: Buffer for AUTN value * @ik: Buffer for IK value * @ck: Buffer for CK value * @res: Buffer for RES value * @res_len: Buffer for RES length * @cb_session_ctx: Session callback context for get_complete_cb() * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this * case, the callback function registered with eap_sim_db_init() will be * called once the results become available. * * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in * ASCII format. * * When using an external server for AKA authentication, this function can * always start a request and return EAP_SIM_DB_PENDING immediately if * authentication triplets are not available. Once the authentication data are * received, callback function registered with eap_sim_db_init() is called to * notify EAP state machine to reprocess the message. This * eap_sim_db_get_aka_auth() function will then be called again and the newly * received triplets will then be given to the caller. */ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, void *cb_session_ctx) { struct eap_sim_db_data *data = priv; struct eap_sim_db_pending *entry; int len; size_t i; char msg[40]; if (identity_len < 2 || identity == NULL || identity[0] != EAP_AKA_PERMANENT_PREFIX) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); return EAP_SIM_DB_FAILURE; } identity++; identity_len--; for (i = 0; i < identity_len; i++) { if (identity[i] == '@') { identity_len = i; break; } } if (identity_len + 1 > sizeof(entry->imsi)) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); return EAP_SIM_DB_FAILURE; } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", identity, identity_len); entry = eap_sim_db_get_pending(data, identity, identity_len, 1); if (entry) { if (entry->state == FAILURE) { VM_FREE(entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); return EAP_SIM_DB_FAILURE; } if (entry->state == PENDING) { eap_sim_db_add_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); return EAP_SIM_DB_PENDING; } wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " "received authentication data"); memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); *res_len = entry->u.aka.res_len; VM_FREE(entry); return 0; } if (data->sock < 0) { if (eap_sim_db_open_socket(data) < 0) return EAP_SIM_DB_FAILURE; } len = snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); if (len < 0 || len + identity_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; memcpy(msg + len, identity, identity_len); len += identity_len; wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " "data for IMSI", identity, identity_len); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; entry = wpa_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; os_get_time(&entry->timestamp); entry->aka = 1; memcpy(entry->imsi, identity, identity_len); entry->imsi_len = identity_len; entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); eap_sim_db_expire_pending(data); return EAP_SIM_DB_PENDING; }
/** * eap_sim_db_get_gsm_triplets - Get GSM triplets * @data: Private data pointer from eap_sim_db_init() * @username: Permanent username (prefix | IMSI) * @max_chal: Maximum number of triplets * @_rand: Buffer for RAND values * @kc: Buffer for Kc values * @sres: Buffer for SRES values * @cb_session_ctx: Session callback context for get_complete_cb() * Returns: Number of triplets received (has to be less than or equal to * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the * callback function registered with eap_sim_db_init() will be called once the * results become available. * * When using an external server for GSM triplets, this function can always * start a request and return EAP_SIM_DB_PENDING immediately if authentication * triplets are not available. Once the triplets are received, callback * function registered with eap_sim_db_init() is called to notify EAP state * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() * function will then be called again and the newly received triplets will then * be given to the caller. */ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx) { struct eap_sim_db_pending *entry; int len, ret; char msg[40]; const char *imsi; size_t imsi_len; if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", username); return EAP_SIM_DB_FAILURE; } imsi = username + 1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", imsi); entry = eap_sim_db_get_pending(data, imsi, 0); if (entry) { int num_chal; if (entry->state == FAILURE) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "failure"); eap_sim_db_free_pending(data, entry); return EAP_SIM_DB_FAILURE; } if (entry->state == PENDING) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "still pending"); eap_sim_db_add_pending(data, entry); return EAP_SIM_DB_PENDING; } wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "%d challenges", entry->u.sim.num_chal); num_chal = entry->u.sim.num_chal; if (num_chal > max_chal) num_chal = max_chal; os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); os_memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN); os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); eap_sim_db_free_pending(data, entry); return num_chal; } if (data->sock < 0) { if (eap_sim_db_open_socket(data) < 0) return EAP_SIM_DB_FAILURE; } imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); if (os_snprintf_error(sizeof(msg), len) || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); if (os_snprintf_error(sizeof(msg) - len, ret)) return EAP_SIM_DB_FAILURE; len += ret; wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); eap_sim_db_expire_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry); return EAP_SIM_DB_PENDING; }
/** * eap_sim_db_get_aka_auth - Get AKA authentication values * @data: Private data pointer from eap_sim_db_init() * @username: Permanent username (prefix | IMSI) * @_rand: Buffer for RAND value * @autn: Buffer for AUTN value * @ik: Buffer for IK value * @ck: Buffer for CK value * @res: Buffer for RES value * @res_len: Buffer for RES length * @cb_session_ctx: Session callback context for get_complete_cb() * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this * case, the callback function registered with eap_sim_db_init() will be * called once the results become available. * * When using an external server for AKA authentication, this function can * always start a request and return EAP_SIM_DB_PENDING immediately if * authentication triplets are not available. Once the authentication data are * received, callback function registered with eap_sim_db_init() is called to * notify EAP state machine to reprocess the message. This * eap_sim_db_get_aka_auth() function will then be called again and the newly * received triplets will then be given to the caller. */ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, void *cb_session_ctx) { struct eap_sim_db_pending *entry; int len; char msg[40]; const char *imsi; size_t imsi_len; if (username == NULL || (username[0] != EAP_AKA_PERMANENT_PREFIX && username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", username); return EAP_SIM_DB_FAILURE; } imsi = username + 1; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", imsi); entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { eap_sim_db_free_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); return EAP_SIM_DB_FAILURE; } if (entry->state == PENDING) { eap_sim_db_add_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); return EAP_SIM_DB_PENDING; } wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " "received authentication data"); os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); *res_len = entry->u.aka.res_len; eap_sim_db_free_pending(data, entry); return 0; } if (data->sock < 0) { if (eap_sim_db_open_socket(data) < 0) return EAP_SIM_DB_FAILURE; } imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); if (os_snprintf_error(sizeof(msg), len) || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return EAP_SIM_DB_FAILURE; entry->aka = 1; os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); eap_sim_db_expire_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry); return EAP_SIM_DB_PENDING; }