All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
validate.c
Go to the documentation of this file.
1 /**
2  * $Id: 85244caf7174c01ed9a26be9a9dc8cf05ea4865a $
3  * @file validate.c
4  * @brief Authentication for yubikey OTP tokens using the ykclient library.
5  *
6  * @author Arran Cudbard-Bell <a.cudbardb@networkradius.com>
7  * @copyright 2013 The FreeRADIUS server project
8  * @copyright 2013 Network RADIUS <info@networkradius.com>
9  */
10 #include "rlm_yubikey.h"
11 
12 #ifdef HAVE_YKCLIENT
13 #include <freeradius-devel/connection.h>
14 
15 /** Frees a ykclient handle
16  *
17  * @param[in] yandle rlm_yubikey_handle_t to close and free.
18  * @return returns 0.
19  */
20 static int _mod_conn_free(ykclient_handle_t **yandle)
21 {
22  ykclient_handle_done(yandle);
23 
24  return 0;
25 }
26 
27 /** Creates a new connection handle for use by the FR connection API.
28  *
29  * Matches the fr_connection_create_t function prototype, is passed to
30  * fr_connection_pool_init, and called when a new connection is required by the
31  * connection pool API.
32  *
33  * @see fr_connection_pool_init
34  * @see fr_connection_create_t
35  * @see connection.c
36  */
37 static void *mod_conn_create(TALLOC_CTX *ctx, void *instance, UNUSED struct timeval const *timeout)
38 {
39  rlm_yubikey_t *inst = instance;
40  ykclient_rc status;
41  ykclient_handle_t *yandle, **marker;
42 
43  status = ykclient_handle_init(inst->ykc, &yandle);
44  if (status != YKCLIENT_OK) {
45  ERROR("rlm_yubikey (%s): %s", inst->name, ykclient_strerror(status));
46 
47  return NULL;
48  }
49  marker = talloc(ctx, ykclient_handle_t *);
50  talloc_set_destructor(marker, _mod_conn_free);
51  *marker = yandle;
52 
53  return yandle;
54 }
55 
57 {
58  ykclient_rc status;
59  CONF_SECTION *servers;
60 
61  char prefix[100];
62 
63  int count = 0;
64 
65  if (!inst->client_id) {
66  ERROR("rlm_yubikey (%s): validation.client_id must be set (to a valid id) when validation is enabled",
67  inst->name);
68 
69  return -1;
70  }
71 
72  if (!inst->api_key || !*inst->api_key || is_zero(inst->api_key)) {
73  ERROR("rlm_yubikey (%s): validation.api_key must be set (to a valid key) when validation is enabled",
74  inst->name);
75 
76  return -1;
77  }
78 
79  DEBUG("rlm_yubikey (%s): Initialising ykclient", inst->name);
80 
81  status = ykclient_global_init();
82  if (status != YKCLIENT_OK) {
83 yk_error:
84  ERROR("rlm_yubikey (%s): %s", ykclient_strerror(status), inst->name);
85 
86  return -1;
87  }
88 
89  status = ykclient_init(&inst->ykc);
90  if (status != YKCLIENT_OK) {
91  goto yk_error;
92  }
93 
94  servers = cf_section_sub_find(conf, "servers");
95  if (servers) {
96  CONF_PAIR *uri, *first;
97  /*
98  * If there were no uris configured we just use the default
99  * ykclient uris which point to the yubico servers.
100  */
101  first = uri = cf_pair_find(servers, "uri");
102  if (!uri) {
103  goto init;
104  }
105 
106  while (uri) {
107  count++;
108  uri = cf_pair_find_next(servers, uri, "uri");
109  }
110  inst->uris = talloc_zero_array(inst, char const *, count);
111 
112  uri = first;
113  count = 0;
114  while (uri) {
115  inst->uris[count++] = cf_pair_value(uri);
116  uri = cf_pair_find_next(servers, uri, "uri");
117  }
118  if (count) {
119  status = ykclient_set_url_templates(inst->ykc, count, inst->uris);
120  if (status != YKCLIENT_OK) {
121  goto yk_error;
122  }
123  }
124  }
125 
126 init:
127  status = ykclient_set_client_b64(inst->ykc, inst->client_id, inst->api_key);
128  if (status != YKCLIENT_OK) {
129  ERROR("rlm_yubikey (%s): Failed setting API credentials: %s", ykclient_strerror(status), inst->name);
130 
131  return -1;
132  }
133 
134  snprintf(prefix, sizeof(prefix), "rlm_yubikey (%s)", inst->name);
135  inst->pool = module_connection_pool_init(conf, inst, mod_conn_create, NULL, prefix);
136  if (!inst->pool) {
137  ykclient_done(&inst->ykc);
138 
139  return -1;
140  }
141 
142  return 0;
143 }
144 
146 {
147  fr_connection_pool_free(inst->pool);
148  ykclient_done(&inst->ykc);
149  ykclient_global_done();
150 
151  return 0;
152 }
153 
154 rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request, char const *passcode)
155 {
156  rlm_rcode_t rcode = RLM_MODULE_OK;
157  ykclient_rc status;
158  ykclient_handle_t *yandle;
159 
160  yandle = fr_connection_get(inst->pool);
161  if (!yandle) return RLM_MODULE_FAIL;
162 
163  /*
164  * The libcurl multi-handle interface will tear down the TCP sockets for any partially completed
165  * requests when their easy handle is removed from the multistack.
166  *
167  * For performance reasons ykclient will stop processing the request immediately after receiving
168  * a response from one of the servers. If we then immediately call ykclient_handle_cleanup
169  * the connections are destroyed and will need to be re-established the next time the handle
170  * is used.
171  *
172  * To try and prevent this from happening, we leave cleanup until the *next* time
173  * the handle is used, by which time the requests will of hopefully completed and the connections
174  * can be re-used.
175  *
176  */
177  ykclient_handle_cleanup(yandle);
178 
179  status = ykclient_request_process(inst->ykc, yandle, passcode);
180  if (status != YKCLIENT_OK) {
181  REDEBUG("%s", ykclient_strerror(status));
182  switch (status) {
183  case YKCLIENT_BAD_OTP:
184  case YKCLIENT_REPLAYED_OTP:
185  rcode = RLM_MODULE_REJECT;
186  break;
187 
188  case YKCLIENT_NO_SUCH_CLIENT:
189  rcode = RLM_MODULE_NOTFOUND;
190  break;
191 
192  default:
193  rcode = RLM_MODULE_FAIL;
194  }
195  }
196 
197  fr_connection_release(inst->pool, yandle);
198 
199  return rcode;
200 }
201 #endif
bool is_zero(char const *value)
Check whether the string is all zeros.
Definition: misc.c:365
The module is OK, continue.
Definition: radiusd.h:91
char const * name
Instance name.
Definition: rlm_yubikey.h:25
rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request, char const *passcode)
int rlm_yubikey_ykclient_detach(rlm_yubikey_t *inst)
#define UNUSED
Definition: libradius.h:134
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
static float timeout
Definition: radclient.c:43
#define inst
CONF_PAIR * cf_pair_find(CONF_SECTION const *, char const *name)
Definition: conffile.c:3478
char const * cf_pair_value(CONF_PAIR const *pair)
Definition: conffile.c:3506
fr_connection_pool_t * module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_connection_create_t c, fr_connection_alive_t a, char const *prefix)
Initialise a module specific connection pool.
Definition: modules.c:1759
#define DEBUG(fmt,...)
Definition: log.h:175
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Create a new memcached handle.
static int _mod_conn_free(rlm_cache_memcached_handle_t *mandle)
Free a connection handle.
int rlm_yubikey_ykclient_init(CONF_SECTION *conf, rlm_yubikey_t *inst)
Immediately reject the request.
Definition: radiusd.h:89
char const ** uris
Yubicloud URLs to validate the token against.
Definition: rlm_yubikey.h:31
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *, CONF_PAIR const *, char const *name)
Find a pair with a name matching attr, after specified pair.
Definition: conffile.c:3673
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
static char const * prefix
Definition: mainconfig.c:81
Module failed, don't reply.
Definition: radiusd.h:90
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
Definition: connection.c:1291
#define REDEBUG(fmt,...)
Definition: log.h:254
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
Definition: connection.c:1305
User not found.
Definition: radiusd.h:95
#define ERROR(fmt,...)
Definition: log.h:145
void fr_connection_pool_free(fr_connection_pool_t *pool)
Delete a connection pool.
Definition: connection.c:1226