From 46c1e8fa60c3b9152ff1762a3754a49cf1c4aae0 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 20 Nov 2017 09:15:31 -0800 Subject: [PATCH] simauth: new module to handle EAP-SIM/AKA auth This is the core module that takes care of registering authentication drivers. EAP-SIM/AKA will be able to acquire a driver that supports the required algorithms. The driver implementation (hardcoded/ofono etc.) is isolated into separate plugin modules. --- src/simauth.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/simauth.h | 141 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 src/simauth.c create mode 100644 src/simauth.h diff --git a/src/simauth.c b/src/simauth.c new file mode 100644 index 00000000..d0287a93 --- /dev/null +++ b/src/simauth.c @@ -0,0 +1,185 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include "watchlist.h" +#include "simauth.h" + +static struct l_queue *auth_providers; + +struct iwd_sim_auth { + const struct iwd_sim_auth_driver *driver; + void *driver_data; + bool aka_supported : 1; + bool sim_supported : 1; + char *nai; + int pending; + struct watchlist destory_watches; +}; + +struct iwd_sim_auth *iwd_sim_auth_create(bool sim_supported, bool aka_supported, + char *nai, const struct iwd_sim_auth_driver *driver, + void *driver_data) +{ + struct iwd_sim_auth *auth = l_new(struct iwd_sim_auth, 1); + + if (!auth) + return NULL; + + auth->aka_supported = aka_supported; + auth->sim_supported = sim_supported; + auth->nai = l_strdup(nai); + auth->driver = driver; + auth->driver_data = driver_data; + watchlist_init(&auth->destory_watches, NULL); + + l_queue_push_head(auth_providers, auth); + + return auth; +} + +void *iwd_sim_auth_get_data(struct iwd_sim_auth *auth) +{ + return auth->driver_data; +} + +static void destroy_provider(void *data) +{ + struct iwd_sim_auth *auth = data; + + if (auth->driver->cancel_request) + auth->driver->cancel_request(auth, auth->pending); + + WATCHLIST_NOTIFY_NO_ARGS(&auth->destory_watches, + sim_auth_destroyed_cb_t); + + if (auth->driver->remove) + auth->driver->remove(auth); + + l_free(auth->nai); + l_free(auth); +} + +int iwd_sim_auth_remove(struct iwd_sim_auth *auth) +{ + bool r; + + r = l_queue_remove(auth_providers, auth); + if (!r) + return -ENOENT; + + destroy_provider(auth); + + return 0; +} + +const char *iwd_sim_auth_get_nai(struct iwd_sim_auth *auth) +{ + return auth->nai; +} + +struct iwd_sim_auth *iwd_sim_auth_find(bool sim, bool aka) +{ + struct iwd_sim_auth *auth; + const struct l_queue_entry *entry; + + for (entry = l_queue_get_entries(auth_providers); entry; + entry = entry->next) { + auth = entry->data; + + if (sim && !auth->sim_supported) + continue; + + if (aka && !auth->aka_supported) + continue; + + return auth; + } + + return NULL; +} + +unsigned int sim_auth_destroyed_watch_add(struct iwd_sim_auth *auth, + sim_auth_destroyed_cb_t cb, void *data) +{ + return watchlist_add(&auth->destory_watches, cb, data, NULL); +} + +void sim_auth_destroyed_watch_remove(struct iwd_sim_auth *auth, + unsigned int id) +{ + watchlist_remove(&auth->destory_watches, id); +} + +bool sim_auth_check_milenage(struct iwd_sim_auth *auth, + const uint8_t *rand, const uint8_t *autn, + sim_auth_check_milenage_cb_t cb, void *data) +{ + if (!auth->aka_supported) + return false; + + /* save ID in case simauth is destroyed */ + auth->pending = auth->driver->check_milenage(auth, rand, autn, + cb, data); + + return auth->pending; +} + +bool sim_auth_run_gsm(struct iwd_sim_auth *auth, const uint8_t *rands, + int num_rands, sim_auth_run_gsm_cb_t cb, void *data) +{ + if (!auth->sim_supported) + return false; + + /* save ID in case simauth is destroyed */ + auth->pending = auth->driver->run_gsm(auth, rands, num_rands, cb, data); + + return auth->pending; +} + +void sim_auth_cancel_request(struct iwd_sim_auth *auth, int id) +{ + if (auth->driver->cancel_request) + auth->driver->cancel_request(auth, id); +} + +bool sim_auth_init(void) +{ + auth_providers = l_queue_new(); + + if (!auth_providers) + return false; + + return true; +} + +void sim_auth_exit(void) +{ + if (l_queue_length(auth_providers) > 0) + l_warn("Auth provider queue was not empty on exit!"); + + l_queue_destroy(auth_providers, destroy_provider); +} diff --git a/src/simauth.h b/src/simauth.h new file mode 100644 index 00000000..be9abcf9 --- /dev/null +++ b/src/simauth.h @@ -0,0 +1,141 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#define EAP_AKA_KI_LEN 16 +#define EAP_AKA_OPC_LEN 16 +#define EAP_AKA_AMF_LEN 2 +#define EAP_AKA_SQN_LEN 6 +#define EAP_SIM_KC_LEN 8 +#define EAP_SIM_SRES_LEN 4 + +#define NUM_RANDS_MAX 3 + +struct iwd_sim_auth; + +typedef void (*sim_auth_destroyed_cb_t)(void *data); + +/* + * Callback containing Milenage keys. + * + * @param res RES value + * @param ck CK key + * @param ik IK key + * @param auts AUTS key, if sync failure this will be non NULL. With a + * normal success, this should be NULL. + * @param data User data + * + * Note: If there was an error (invalid AUTN or some other error), all the keys + * (res/ck/ik/auts) will be NULL. A sync error will result in res, ck, and ik + * being NULL but auts should point to a valid AUTS key. + */ +typedef void (*sim_auth_check_milenage_cb_t)(const uint8_t *res, + const uint8_t *ck, const uint8_t *ik, const uint8_t *auts, + void *data); + +/* + * Callback with GSM data + * + * @param sres SRES value + * @param kc Kc value + * @param user_data User data + * + * Note: If GSM authentication was unsuccessful sres and kc will both be NULL + */ +typedef void (*sim_auth_run_gsm_cb_t)(const uint8_t *sres, + const uint8_t *kc, void *user_data); + +struct iwd_sim_auth_driver { + const char *name; + int (*check_milenage)(struct iwd_sim_auth *auth, const uint8_t *rand, + const uint8_t *autn, + sim_auth_check_milenage_cb_t cb, void *data); + int (*run_gsm)(struct iwd_sim_auth *auth, const uint8_t *rands, + int num_rands, sim_auth_run_gsm_cb_t cb, + void *data); + void (*cancel_request)(struct iwd_sim_auth *auth, int id); + void (*remove)(struct iwd_sim_auth *auth); +}; + +/* + * Create a new authentication provider. This new provider will be added to + * the list of available providers. It is expected that the auth provider + * should be immediately available for auth requests. + */ +struct iwd_sim_auth *iwd_sim_auth_create(bool sim_supported, + bool aka_supported, char *nai, + const struct iwd_sim_auth_driver *driver, void *driver_data); + +void *iwd_sim_auth_get_data(struct iwd_sim_auth *auth); + +int iwd_sim_auth_remove(struct iwd_sim_auth *auth); + +const char *iwd_sim_auth_get_nai(struct iwd_sim_auth *auth); + +unsigned int sim_auth_destroyed_watch_add(struct iwd_sim_auth *auth, + sim_auth_destroyed_cb_t cb, void *data); + +void sim_auth_destroyed_watch_remove(struct iwd_sim_auth *auth, + unsigned int id); +/* + * Find an appropriate driver for running SIM/AKA algorithms + * + * @param sim True if the driver should support SIM authentication + * @param aka True if the driver should support AKA authentication + * + * @returns Driver handle found, NULL if none available + */ +struct iwd_sim_auth *iwd_sim_auth_find(bool sim, bool aka); +/* + * Check that 'rand' and 'autn' are valid from the server. + * + * @param auth Auth handle found with iwd_sim_auth_find() + * @param rand List of RAND's from the server + * @param autn AUTN from server + * @param cb Callback with milenage values + * @param data User data + * + * @param False if there was an error + */ +bool sim_auth_check_milenage(struct iwd_sim_auth *auth, + const uint8_t *rand, const uint8_t *autn, + sim_auth_check_milenage_cb_t cb, void *data); + +/* + * Retrieve EAP-SIM Kc and SRES values + * + * @param Auth Auth handle found with iwd_sim_auth_find() + * @param rands Buffer containing N 16 byte RANDs (n == 2 || n == 3) + * @param num_rands Number of 16 byte RANDs in 'rand' + * @param cb Callback with GSM key values + * @param data User data + * + * @return False if there was an error + */ +bool sim_auth_run_gsm(struct iwd_sim_auth *auth, const uint8_t *rands, + int num_rands, sim_auth_run_gsm_cb_t cb, void *data); + +bool sim_auth_init(void); + +void sim_auth_exit(void);