summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_yubikey/validate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_yubikey/validate.c')
-rw-r--r--src/modules/rlm_yubikey/validate.c201
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