diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
commit | 50b37d4a27d3295a29afca2286f1a5a086142cec (patch) | |
tree | 9212f763934ee090ef72d823f559f52ce387f268 /src/modules/rlm_yubikey/validate.c | |
parent | Initial commit. (diff) | |
download | freeradius-upstream.tar.xz freeradius-upstream.zip |
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/modules/rlm_yubikey/validate.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/modules/rlm_yubikey/validate.c b/src/modules/rlm_yubikey/validate.c new file mode 100644 index 0000000..7dc13d8 --- /dev/null +++ b/src/modules/rlm_yubikey/validate.c @@ -0,0 +1,201 @@ +/** + * $Id$ + * @file validate.c + * @brief Authentication for yubikey OTP tokens using the ykclient library. + * + * @author Arran Cudbard-Bell <a.cudbardb@networkradius.com> + * @copyright 2013 The FreeRADIUS server project + * @copyright 2013 Network RADIUS <info@networkradius.com> + */ +#include "rlm_yubikey.h" + +#ifdef HAVE_YKCLIENT +#include <freeradius-devel/connection.h> + +/** Frees a ykclient handle + * + * @param[in] yandle rlm_yubikey_handle_t to close and free. + * @return returns 0. + */ +static int _mod_conn_free(ykclient_handle_t **yandle) +{ + ykclient_handle_done(yandle); + + return 0; +} + +/** Creates a new connection handle for use by the FR connection API. + * + * Matches the fr_connection_create_t function prototype, is passed to + * fr_connection_pool_init, and called when a new connection is required by the + * connection pool API. + * + * @see fr_connection_pool_init + * @see fr_connection_create_t + * @see connection.c + */ +static void *mod_conn_create(TALLOC_CTX *ctx, void *instance) +{ + rlm_yubikey_t *inst = instance; + ykclient_rc status; + ykclient_handle_t *yandle, **marker; + + status = ykclient_handle_init(inst->ykc, &yandle); + if (status != YKCLIENT_OK) { + ERROR("rlm_yubikey (%s): %s", inst->name, ykclient_strerror(status)); + + return NULL; + } + marker = talloc(ctx, ykclient_handle_t *); + talloc_set_destructor(marker, _mod_conn_free); + *marker = yandle; + + return yandle; +} + +int rlm_yubikey_ykclient_init(CONF_SECTION *conf, rlm_yubikey_t *inst) +{ + ykclient_rc status; + CONF_SECTION *servers; + + char prefix[100]; + + int count = 0; + + if (!inst->client_id) { + ERROR("rlm_yubikey (%s): validation.client_id must be set (to a valid id) when validation is enabled", + inst->name); + + return -1; + } + + if (!inst->api_key || !*inst->api_key || is_zero(inst->api_key)) { + ERROR("rlm_yubikey (%s): validation.api_key must be set (to a valid key) when validation is enabled", + inst->name); + + return -1; + } + + DEBUG("rlm_yubikey (%s): Initialising ykclient", inst->name); + + status = ykclient_global_init(); + if (status != YKCLIENT_OK) { +yk_error: + ERROR("rlm_yubikey (%s): %s", ykclient_strerror(status), inst->name); + + return -1; + } + + status = ykclient_init(&inst->ykc); + if (status != YKCLIENT_OK) { + goto yk_error; + } + + servers = cf_section_sub_find(conf, "servers"); + if (servers) { + CONF_PAIR *uri, *first; + /* + * If there were no uris configured we just use the default + * ykclient uris which point to the yubico servers. + */ + first = uri = cf_pair_find(servers, "uri"); + if (!uri) { + goto init; + } + + while (uri) { + count++; + uri = cf_pair_find_next(servers, uri, "uri"); + } + inst->uris = talloc_zero_array(inst, char const *, count); + + uri = first; + count = 0; + while (uri) { + inst->uris[count++] = cf_pair_value(uri); + uri = cf_pair_find_next(servers, uri, "uri"); + } + if (count) { + status = ykclient_set_url_templates(inst->ykc, count, inst->uris); + if (status != YKCLIENT_OK) { + goto yk_error; + } + } + } + +init: + status = ykclient_set_client_b64(inst->ykc, inst->client_id, inst->api_key); + if (status != YKCLIENT_OK) { + ERROR("rlm_yubikey (%s): Failed setting API credentials: %s", ykclient_strerror(status), inst->name); + + return -1; + } + + snprintf(prefix, sizeof(prefix), "rlm_yubikey (%s)", inst->name); + inst->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, prefix); + if (!inst->pool) { + ykclient_done(&inst->ykc); + + return -1; + } + + return 0; +} + +int rlm_yubikey_ykclient_detach(rlm_yubikey_t *inst) +{ + fr_connection_pool_free(inst->pool); + ykclient_done(&inst->ykc); + ykclient_global_done(); + + return 0; +} + +rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request, char const *passcode) +{ + rlm_rcode_t rcode = RLM_MODULE_OK; + ykclient_rc status; + ykclient_handle_t *yandle; + + yandle = fr_connection_get(inst->pool); + if (!yandle) return RLM_MODULE_FAIL; + + /* + * The libcurl multi-handle interface will tear down the TCP sockets for any partially completed + * requests when their easy handle is removed from the multistack. + * + * For performance reasons ykclient will stop processing the request immediately after receiving + * a response from one of the servers. If we then immediately call ykclient_handle_cleanup + * the connections are destroyed and will need to be re-established the next time the handle + * is used. + * + * To try and prevent this from happening, we leave cleanup until the *next* time + * the handle is used, by which time the requests will of hopefully completed and the connections + * can be re-used. + * + */ + ykclient_handle_cleanup(yandle); + + status = ykclient_request_process(inst->ykc, yandle, passcode); + if (status != YKCLIENT_OK) { + REDEBUG("%s", ykclient_strerror(status)); + switch (status) { + case YKCLIENT_BAD_OTP: + case YKCLIENT_REPLAYED_OTP: + rcode = RLM_MODULE_REJECT; + break; + + case YKCLIENT_NO_SUCH_CLIENT: + rcode = RLM_MODULE_NOTFOUND; + break; + + default: + rcode = RLM_MODULE_FAIL; + } + } + + fr_connection_release(inst->pool, yandle); + + return rcode; +} +#endif |