All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mod.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: e1da2bba2eeec286e091e0a9f24be2c7fa151df3 $
19  *
20  * @brief Utillity functions used in the module.
21  * @file mod.c
22  *
23  * @author Aaron Hurt <ahurt@anbcs.com>
24  * @copyright 2013-2014 The FreeRADIUS Server Project.
25  */
26 
27 RCSID("$Id: e1da2bba2eeec286e091e0a9f24be2c7fa151df3 $")
28 
29 #include <freeradius-devel/radiusd.h>
30 
31 #include <libcouchbase/couchbase.h>
32 #include "../rlm_json/json.h"
33 
34 #include "mod.h"
35 #include "couchbase.h"
36 
37 /** Delete a conneciton pool handle and free related resources
38  *
39  * Destroys the underlying Couchbase connection handle freeing any related
40  * resources and closes the socket connection.
41  *
42  * @param chandle The connection handle to destroy.
43  * @return 0.
44  */
46 {
47  lcb_t cb_inst = chandle->handle; /* couchbase instance */
48 
49  /* destroy/free couchbase instance */
50  lcb_destroy(cb_inst);
51 
52  /* return */
53  return 0;
54 }
55 
56 /** Create a new connection pool handle
57  *
58  * Create a new connection to Couchbase within the pool and initialize
59  * information associated with the connection instance.
60  *
61  * @param ctx The connection parent context.
62  * @param instance The module instance.
63  * @param timeout Maximum time to establish the connection.
64  * @return
65  * - New connection handle.
66  * - NULL on error.
67  */
68 void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
69 {
70  rlm_couchbase_t *inst = instance; /* module instance pointer */
71  rlm_couchbase_handle_t *chandle = NULL; /* connection handle pointer */
72  cookie_t *cookie = NULL; /* couchbase cookie */
73  lcb_t cb_inst; /* couchbase connection instance */
74  lcb_error_t cb_error; /* couchbase error status */
75 
76  /* create instance */
77  cb_error = couchbase_init_connection(&cb_inst, inst->server, inst->bucket, inst->password,
78  FR_TIMEVAL_TO_MS(timeout));
79 
80  /* check couchbase instance */
81  if (cb_error != LCB_SUCCESS) {
82  ERROR("rlm_couchbase: failed to initiate couchbase connection: %s (0x%x)",
83  lcb_strerror(NULL, cb_error), cb_error);
84  /* destroy/free couchbase instance */
85  lcb_destroy(cb_inst);
86  /* fail */
87  return NULL;
88  }
89 
90  /* allocate memory for couchbase connection instance abstraction */
91  chandle = talloc_zero(ctx, rlm_couchbase_handle_t);
92  talloc_set_destructor(chandle, _mod_conn_free);
93 
94  /* allocate cookie off handle */
95  cookie = talloc_zero(chandle, cookie_t);
96 
97  /* init tokener error and json object */
98  cookie->jerr = json_tokener_success;
99  cookie->jobj = NULL;
100 
101  /* populate handle */
102  chandle->cookie = cookie;
103  chandle->handle = cb_inst;
104 
105  /* return handle struct */
106  return chandle;
107 }
108 
109 /** Check the health of a connection handle
110  *
111  * Attempt to determing the state of the Couchbase connection by requesting
112  * a cluster statistics report. Mark the connection as failed if the request
113  * returns anything other than success.
114  *
115  * @param instance The module instance (currently unused).
116  * @param handle The connection handle.
117  * @return
118  * - 0 on success (alive).
119  * - -1 on failure (unavailable).
120  */
121 int mod_conn_alive(UNUSED void *instance, void *handle)
122 {
123  rlm_couchbase_handle_t *chandle = handle; /* connection handle pointer */
124  lcb_t cb_inst = chandle->handle; /* couchbase instance */
125  lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error status */
126 
127  /* attempt to get server stats */
128  if ((cb_error = couchbase_server_stats(cb_inst, NULL)) != LCB_SUCCESS) {
129  /* log error */
130  ERROR("rlm_couchbase: failed to get couchbase server stats: %s (0x%x)",
131  lcb_strerror(NULL, cb_error), cb_error);
132  /* error out */
133  return -1;
134  }
135  return 0;
136 }
137 
138 /** Build a JSON object map from the configuration "map" section
139  *
140  * Parse the "map" section from the module configuration file and store this
141  * as a JSON object (key/value list) in the module instance. This map will be
142  * used to lookup and map attributes for all incoming accounting requests.
143  *
144  * @param conf Configuration section.
145  * @param instance The module instance.
146  * @return
147  * - 0 on success.
148  * - -1 on failure.
149  */
151 {
152  rlm_couchbase_t *inst = instance; /* our module instance */
153  CONF_SECTION *cs; /* module config section */
154  CONF_ITEM *ci; /* config item */
155  CONF_PAIR *cp; /* conig pair */
156  const char *attribute, *element; /* attribute and element names */
157 
158  /* find update section */
159  cs = cf_section_sub_find(conf, "update");
160 
161  /* backwards compatibility */
162  if (!cs) {
163  cs = cf_section_sub_find(conf, "map");
164  WARN("rlm_couchbase: found deprecated 'map' section - please change to 'update'");
165  }
166 
167  /* check section */
168  if (!cs) {
169  ERROR("rlm_couchbase: failed to find 'update' section in config");
170  /* fail */
171  return -1;
172  }
173 
174  /* create attribute map object */
175  inst->map = json_object_new_object();
176 
177  /* parse update section */
178  for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
179  /* validate item */
180  if (!cf_item_is_pair(ci)) {
181  ERROR("rlm_couchbase: failed to parse invalid item in 'update' section");
182  /* free map */
183  if (inst->map) {
184  json_object_put(inst->map);
185  }
186  /* fail */
187  return -1;
188  }
189 
190  /* get value pair from item */
191  cp = cf_item_to_pair(ci);
192 
193  /* get pair name (attribute name) */
194  attribute = cf_pair_attr(cp);
195 
196  /* get pair value (element name) */
197  element = cf_pair_value(cp);
198 
199  /* add pair name and value */
200  json_object_object_add(inst->map, attribute, json_object_new_string(element));
201 
202  /* debugging */
203  DEBUG3("rlm_couchbase: added attribute '%s' to element '%s' mapping", attribute, element);
204  }
205 
206  /* debugging */
207  DEBUG3("rlm_couchbase: built attribute to element mapping %s", json_object_to_json_string(inst->map));
208 
209  /* return */
210  return 0;
211 }
212 
213 /** Map attributes to JSON element names
214  *
215  * Attempt to map the passed attribute name to the configured JSON element
216  * name using the JSON object map mod_build_attribute_element_map().
217  *
218  * @param name The character name of the requested attribute.
219  * @param map The JSON object map to use for the lookup.
220  * @param buf The buffer where the given element will be stored if found.
221  * @return
222  * - 0 on success.
223  * - -1 on failure.
224  */
225 int mod_attribute_to_element(const char *name, json_object *map, void *buf)
226 {
227  json_object *jval; /* json object values */
228 
229  /* clear buffer */
230  memset((char *) buf, 0, MAX_KEY_SIZE);
231 
232  /* attempt to map attribute */
233  if (json_object_object_get_ex(map, name, &jval)) {
234  /* copy and check size */
235  if (strlcpy(buf, json_object_get_string(jval), MAX_KEY_SIZE) >= MAX_KEY_SIZE) {
236  /* oops ... this value is bigger than our buffer ... error out */
237  ERROR("rlm_couchbase: json map value larger than MAX_KEY_SIZE - %d", MAX_KEY_SIZE);
238  /* return fail */
239  return -1;
240  }
241  /* looks good */
242  return 0;
243  }
244 
245  /* debugging */
246  DEBUG("rlm_couchbase: skipping attribute with no map entry - %s", name);
247 
248  /* default return */
249  return -1;
250 }
251 
252 /** Build value pairs from the passed JSON object and add to the request
253  *
254  * Parse the passed JSON object and create value pairs that will be injected into
255  * the given request for authorization.
256  *
257  * Example JSON document structure:
258  * @code{.json}
259  * {
260  * "docType": "raduser",
261  * "userName": "test",
262  * "config": {
263  * "SHA-Password": {
264  * "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
265  * "op": ":="
266  * }
267  * },
268  * "reply": {
269  * "Reply-Message": {
270  * "value": "Hidey Ho!",
271  * "op": "="
272  * }
273  * }
274  * }
275  * @endcode
276  *
277  * @param json The JSON object representation of the user documnent.
278  * @param section The pair section ("config" or "reply").
279  * @param request The request to which the generated pairs should be added.
280  */
281 void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request)
282 {
283  json_object *jobj, *jval, *jop; /* json object pointers */
284  TALLOC_CTX *ctx; /* talloc context for fr_pair_make */
285  VALUE_PAIR *vp, **ptr; /* value pair and value pair pointer for fr_pair_make */
286 
287  /* assign ctx and vps for fr_pair_make based on section */
288  if (strcmp(section, "config") == 0) {
289  ctx = request;
290  ptr = &(request->config);
291  } else if (strcmp(section, "reply") == 0) {
292  ctx = request->reply;
293  ptr = &(request->reply->vps);
294  } else {
295  /* log error - this shouldn't happen */
296  RERROR("invalid section passed for fr_pair_make");
297  /* return */
298  return NULL;
299  }
300 
301  /* get config payload */
302  if (json_object_object_get_ex(json, section, &jobj)) {
303  /* make sure we have the correct type */
304  if (!fr_json_object_is_type(jobj, json_type_object)) {
305  /* log error */
306  RERROR("invalid json type for '%s' section - sections must be json objects", section);
307  /* reuturn */
308  return NULL;
309  }
310  /* loop through object */
311  json_object_object_foreach(jobj, attribute, json_vp) {
312  /* check for appropriate type in value and op */
313  if (!fr_json_object_is_type(json_vp, json_type_object)) {
314  /* log error */
315  RERROR("invalid json type for '%s' attribute - attributes must be json objects",
316  attribute);
317  /* return */
318  return NULL;
319  }
320  /* debugging */
321  RDEBUG("parsing '%s' attribute: %s => %s", section, attribute,
322  json_object_to_json_string(json_vp));
323  /* create pair from json object */
324  if (json_object_object_get_ex(json_vp, "value", &jval) &&
325  json_object_object_get_ex(json_vp, "op", &jop)) {
326  /* make correct pairs based on json object type */
327  switch (fr_json_object_get_type(jval)) {
328  case json_type_double:
329  case json_type_int:
330  case json_type_string:
331  /* debugging */
332  RDEBUG("adding '%s' attribute to '%s' section", attribute, section);
333  /* add pair */
334  vp = fr_pair_make(ctx, ptr, attribute, json_object_get_string(jval),
335  fr_str2int(fr_tokens_table, json_object_get_string(jop), 0));
336  /* check pair */
337  if (!vp) {
338  RERROR("could not build value pair for '%s' attribute (%s)",
339  attribute, fr_strerror());
340  /* return */
341  return NULL;
342  }
343  break;
344 
345  case json_type_object:
346  case json_type_array:
347  /* log error - we want to handle these eventually */
348  RERROR("skipping unhandled nested json object or array value pair object");
349  break;
350 
351  default:
352  /* log error - this shouldn't ever happen */
353  RERROR("skipping unhandled json type in value pair object");
354  break;
355  }
356  } else {
357  /* log error */
358  RERROR("failed to get 'value' or 'op' element for '%s' attribute", attribute);
359  }
360  }
361  /* return NULL */
362  return NULL;
363  }
364 
365  /* debugging */
366  RDEBUG("couldn't find '%s' section in json object - not adding value pairs for this section", section);
367 
368  /* return NULL */
369  return NULL;
370 }
371 
372 /** Convert value pairs to json objects
373  *
374  * Take the passed value pair and convert it to a json-c JSON object.
375  * This code is heavily based on the fr_json_from_pair() function
376  * from src/lib/print.c.
377  *
378  * @param request The request object.
379  * @param vp The value pair to convert.
380  * @return A JSON object.
381  */
383 {
384  char value[255]; /* radius attribute value */
385 
386  /* add this attribute/value pair to our json output */
387  if (!vp->da->flags.has_tag) {
388  unsigned int i;
389 
390  switch (vp->da->type) {
391  case PW_TYPE_INTEGER:
392  i = vp->vp_integer;
393  goto print_int;
394 
395  case PW_TYPE_SHORT:
396  i = vp->vp_short;
397  goto print_int;
398 
399  case PW_TYPE_BYTE:
400  i = vp->vp_byte;
401 
402  print_int:
403  /* skip if we have flags */
404  if (vp->da->flags.has_value) break;
405 #ifdef HAVE_JSON_OBJECT_NEW_INT64
406  /* debug */
407  RDEBUG3("creating new int64 for unsigned 32 bit int/byte/short '%s'", vp->da->name);
408  /* return as 64 bit int - JSON spec does not support unsigned ints */
409  return json_object_new_int64(i);
410 #else
411  /* debug */
412  RDEBUG3("creating new int for unsigned 32 bit int/byte/short '%s'", vp->da->name);
413  /* return as 64 bit int - JSON spec does not support unsigned ints */
414  return json_object_new_int(i);
415 #endif
416 
417  case PW_TYPE_SIGNED:
418 #ifdef HAVE_JSON_OBJECT_NEW_INT64
419  /* debug */
420  RDEBUG3("creating new int64 for signed 32 bit integer '%s'", vp->da->name);
421  /* return as 64 bit int - json-c represents all ints as 64 bits internally */
422  return json_object_new_int64(vp->vp_signed);
423 #else
424  RDEBUG3("creating new int for signed 32 bit integer '%s'", vp->da->name);
425  /* return as signed int */
426  return json_object_new_int(vp->vp_signed);
427 #endif
428 
429  case PW_TYPE_INTEGER64:
430 #ifdef HAVE_JSON_OBJECT_NEW_INT64
431  /* debug */
432  RDEBUG3("creating new int64 for 64 bit integer '%s'", vp->da->name);
433  /* return as 64 bit int - because it is a 64 bit int */
434  return json_object_new_int64(vp->vp_integer64);
435 #else
436  /* warning */
437  RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
438  break;
439 #endif
440 
441  default:
442  /* silence warnings - do nothing */
443  break;
444  }
445  }
446 
447  /* keep going if not set above */
448  switch (vp->da->type) {
449  case PW_TYPE_STRING:
450  /* debug */
451  RDEBUG3("assigning string '%s' as string", vp->da->name);
452  /* return string value */
453  return json_object_new_string(vp->vp_strvalue);
454 
455  default:
456  /* debug */
457  RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
458  /* get standard value */
459  fr_pair_value_snprint(value, sizeof(value), vp, 0);
460  /* return string value from above */
461  return json_object_new_string(value);
462  }
463 }
464 
465 /** Ensure accounting documents always contain a valid timestamp
466  *
467  * Inspect the given JSON object representation of an accounting document
468  * fetched from Couchbase and ensuse it contains a valid (non NULL) timestamp value.
469  *
470  * @param json JSON object representation of an accounting document.
471  * @param vps The value pairs associated with the current accounting request.
472  * @return
473  * - 0 on success.
474  * - -1 on failure.
475  */
476 int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
477 {
478  json_object *jval; /* json object value */
479  struct tm tm; /* struct to hold event time */
480  time_t ts = 0; /* values to hold time in seconds */
481  VALUE_PAIR *vp; /* values to hold value pairs */
482  char value[255]; /* store radius attribute values and our timestamp */
483 
484  /* get our current start timestamp from our json body */
485  if (json_object_object_get_ex(json, "startTimestamp", &jval) == 0) {
486  /* debugging ... this shouldn't ever happen */
487  DEBUG("rlm_couchbase: failed to find 'startTimestamp' in current json body");
488  /* return */
489  return -1;
490  }
491 
492  /* check for null value */
493  if (json_object_get_string(jval) != NULL) {
494  /* already set - nothing left to do */
495  return 0;
496  }
497 
498  /* get current event timestamp */
499  if ((vp = fr_pair_find_by_num(vps, 0, PW_EVENT_TIMESTAMP, TAG_ANY)) != NULL) {
500  /* get seconds value from attribute */
501  ts = vp->vp_date;
502  } else {
503  /* debugging */
504  DEBUG("rlm_couchbase: failed to find event timestamp in current request");
505  /* return */
506  return -1;
507  }
508 
509  /* clear value */
510  memset(value, 0, sizeof(value));
511 
512  /* get elapsed session time */
513  if ((vp = fr_pair_find_by_num(vps, 0, PW_ACCT_SESSION_TIME, TAG_ANY)) != NULL) {
514  /* calculate diff */
515  ts = (ts - vp->vp_integer);
516  /* calculate start time */
517  size_t length = strftime(value, sizeof(value), "%b %e %Y %H:%M:%S %Z", localtime_r(&ts, &tm));
518  /* check length */
519  if (length > 0) {
520  /* debugging */
521  DEBUG("rlm_couchbase: calculated start timestamp: %s", value);
522  /* store new value in json body */
523  json_object_object_add(json, "startTimestamp", json_object_new_string(value));
524  } else {
525  /* debugging */
526  DEBUG("rlm_couchbase: failed to format calculated timestamp");
527  /* return */
528  return -1;
529  }
530  }
531 
532  /* default return */
533  return 0;
534 }
535 
536 /** Handle client value processing for client_map_section()
537  *
538  * @param out Character output
539  * @param cp Configuration pair
540  * @param data The client data
541  * @return
542  * - 0 on success.
543  * - -1 on failure.
544  */
545 static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
546 {
547  json_object *jval;
548 
549  if (!json_object_object_get_ex((json_object *)data, cf_pair_value(cp), &jval)) {
550  *out = NULL;
551  return 0;
552  }
553 
554  if (!jval) return -1;
555 
556  *out = talloc_strdup(NULL, json_object_get_string(jval));
557  if (!*out) return -1;
558 
559  return 0;
560 }
561 
562 /** Load client entries from Couchbase client documents on startup
563  *
564  * This function executes the view defined in the module configuration and loops
565  * through all returned rows. The view is called with "stale=false" to ensure the
566  * most accurate data available when the view is called. This will force an index
567  * rebuild on this design document in Couchbase. However, since this function is only
568  * run once at sever startup this should not be a concern.
569  *
570  * @param inst The module instance.
571  * @param tmpl Default values for new clients.
572  * @param map The client attribute configuration section.
573  * @return
574  * - 0 on success.
575  * - -1 on failure.
576  */
578 {
579  rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
580  char vpath[256], vid[MAX_KEY_SIZE], vkey[MAX_KEY_SIZE]; /* view path and fields */
581  char error[512]; /* view error return */
582  int idx = 0; /* row array index counter */
583  int retval = 0; /* return value */
584  lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */
585  json_object *json, *jval; /* json object holders */
586  json_object *jrows = NULL; /* json object to hold view rows */
587  CONF_SECTION *client; /* freeradius config section */
588  RADCLIENT *c; /* freeradius client */
589 
590  /* get handle */
591  handle = fr_connection_get(inst->pool);
592 
593  /* check handle */
594  if (!handle) return -1;
595 
596  /* set couchbase instance */
597  lcb_t cb_inst = handle->handle;
598 
599  /* set cookie */
600  cookie_t *cookie = handle->cookie;
601 
602  /* build view path */
603  snprintf(vpath, sizeof(vpath), "%s?stale=false", inst->client_view);
604 
605  /* query view for document */
606  cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL);
607 
608  /* check error and object */
609  if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
610  /* log error */
611  ERROR("rlm_couchbase: failed to execute view request or parse return");
612  /* set return */
613  retval = -1;
614  /* return */
615  goto free_and_return;
616  }
617 
618  /* debugging */
619  DEBUG3("rlm_couchbase: cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
620 
621  /* check for error in json object */
622  if (json_object_object_get_ex(cookie->jobj, "error", &json)) {
623  /* build initial error buffer */
624  strlcpy(error, json_object_get_string(json), sizeof(error));
625  /* get error reason */
626  if (json_object_object_get_ex(cookie->jobj, "reason", &json)) {
627  /* append divider */
628  strlcat(error, " - ", sizeof(error));
629  /* append reason */
630  strlcat(error, json_object_get_string(json), sizeof(error));
631  }
632  /* log error */
633  ERROR("rlm_couchbase: view request failed with error: %s", error);
634  /* set return */
635  retval = -1;
636  /* return */
637  goto free_and_return;
638  }
639 
640  /* check for document id in return */
641  if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) {
642  /* log error */
643  ERROR("rlm_couchbase: failed to fetch rows from view payload");
644  /* set return */
645  retval = -1;
646  /* return */
647  goto free_and_return;
648  }
649 
650  /* get and hold rows */
651  jrows = json_object_get(json);
652 
653  /* free cookie object */
654  if (cookie->jobj) {
655  json_object_put(cookie->jobj);
656  cookie->jobj = NULL;
657  }
658 
659  /* debugging */
660  DEBUG3("rlm_couchbase: jrows == %s", json_object_to_json_string(jrows));
661 
662  /* check for valid row value */
663  if (!fr_json_object_is_type(jrows, json_type_array) || json_object_array_length(jrows) < 1) {
664  /* log error */
665  ERROR("rlm_couchbase: no valid rows returned from view: %s", vpath);
666  /* set return */
667  retval = -1;
668  /* return */
669  goto free_and_return;
670  }
671 
672  /* loop across all row elements */
673  for (idx = 0; idx < json_object_array_length(jrows); idx++) {
674  /* fetch current index */
675  json = json_object_array_get_idx(jrows, idx);
676 
677  /* get view id */
678  if (json_object_object_get_ex(json, "id", &jval)) {
679  /* clear view id */
680  memset(vid, 0, sizeof(vid));
681  /* copy and check length */
682  if (strlcpy(vid, json_object_get_string(jval), sizeof(vid)) >= sizeof(vid)) {
683  ERROR("rlm_couchbase: id from row longer than MAX_KEY_SIZE (%d)",
684  MAX_KEY_SIZE);
685  continue;
686  }
687  } else {
688  WARN("rlm_couchbase: failed to fetch id from row - skipping");
689  continue;
690  }
691 
692  /* get view key */
693  if (json_object_object_get_ex(json, "key", &jval)) {
694  /* clear view key */
695  memset(vkey, 0, sizeof(vkey));
696  /* copy and check length */
697  if (strlcpy(vkey, json_object_get_string(jval), sizeof(vkey)) >= sizeof(vkey)) {
698  ERROR("rlm_couchbase: key from row longer than MAX_KEY_SIZE (%d)",
699  MAX_KEY_SIZE);
700  continue;
701  }
702  } else {
703  WARN("rlm_couchbase: failed to fetch key from row - skipping");
704  continue;
705  }
706 
707  /* fetch document */
708  cb_error = couchbase_get_key(cb_inst, cookie, vid);
709 
710  /* check error and object */
711  if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
712  /* log error */
713  ERROR("rlm_couchbase: failed to execute get request or parse return");
714  /* set return */
715  retval = -1;
716  /* return */
717  goto free_and_return;
718  }
719 
720  /* debugging */
721  DEBUG3("rlm_couchbase: cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
722 
723  /* allocate conf section */
724  client = tmpl ? cf_section_dup(NULL, tmpl, "client", vkey, true) :
725  cf_section_alloc(NULL, "client", vkey);
726 
727  if (client_map_section(client, map, _get_client_value, cookie->jobj) < 0) {
728  /* free config setion */
729  talloc_free(client);
730  /* set return */
731  retval = -1;
732  /* return */
733  goto free_and_return;
734  }
735 
736  /*
737  * @todo These should be parented from something.
738  */
739  c = client_afrom_cs(NULL, client, false, false);
740  if (!c) {
741  ERROR("rlm_couchbase: failed to allocate client");
742  /* free config setion */
743  talloc_free(client);
744  /* set return */
745  retval = -1;
746  /* return */
747  goto free_and_return;
748  }
749 
750  /*
751  * Client parents the CONF_SECTION which defined it.
752  */
753  talloc_steal(c, client);
754 
755  /* attempt to add client */
756  if (!client_add(NULL, c)) {
757  ERROR("rlm_couchbase: failed to add client '%s' from '%s', possible duplicate?", vkey, vid);
758  /* free client */
759  client_free(c);
760  /* set return */
761  retval = -1;
762  /* return */
763  goto free_and_return;
764  }
765 
766  /* debugging */
767  DEBUG("rlm_couchbase: client '%s' added", c->longname);
768 
769  /* free json object */
770  if (cookie->jobj) {
771  json_object_put(cookie->jobj);
772  cookie->jobj = NULL;
773  }
774  }
775 
776  free_and_return:
777 
778  /* free rows */
779  if (jrows) {
780  json_object_put(jrows);
781  }
782 
783  /* free json object */
784  if (cookie->jobj) {
785  json_object_put(cookie->jobj);
786  cookie->jobj = NULL;
787  }
788 
789  /* release handle */
790  if (handle) {
791  fr_connection_release(inst->pool, handle);
792  }
793 
794  /* return */
795  return retval;
796 }
void * mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Create a new connection pool handle.
Definition: mod.c:68
json_object * map
Json object to hold user defined attribute map.
Definition: mod.h:69
VALUE_PAIR * config
VALUE_PAIR (s) used to set per request parameters for modules and the server core at runtime...
Definition: radiusd.h:227
#define DEBUG3(fmt,...)
Definition: log.h:177
#define RERROR(fmt,...)
Definition: log.h:207
int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
Load client entries from Couchbase client documents on startup.
Definition: mod.c:577
RADCLIENT * client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa)
Allocate a new client from a config section.
Definition: client.c:858
#define fr_json_object_get_type(_obj)
Definition: json_missing.h:56
#define json_object_object_foreach(obj, key, val)
Definition: json_missing.h:92
fr_connection_pool_t * pool
Connection pool.
Definition: mod.h:70
#define RWARN(fmt,...)
Definition: log.h:206
32 Bit signed integer.
Definition: radius.h:45
static char const * name
char const * password
Couchbase bucket password.
Definition: mod.h:55
bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
Add a client to a RADCLIENT_LIST.
Definition: client.c:192
void * handle
Real couchbase instance.
Definition: mod.h:79
lcb_error_t couchbase_server_stats(lcb_t instance, const void *cookie)
Request Couchbase server statistics.
Definition: couchbase.c:247
#define UNUSED
Definition: libradius.h:134
lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key)
Retrieve a document by key from Couchbase.
Definition: couchbase.c:318
void size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
Print the value of an attribute to a string.
Definition: pair.c:2107
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
const FR_NAME_NUMBER fr_tokens_table[]
Definition: token.c:30
Couchbase wrapper function prototypes and datatypes.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
uint8_t length
Definition: proto_bfd.c:203
static float timeout
Definition: radclient.c:43
#define inst
static expr_map_t map[]
Definition: rlm_expr.c:169
char const * server
Couchbase server list.
Definition: mod.h:53
fr_dict_attr_flags_t flags
Flags.
Definition: dict.h:88
char const * cf_pair_value(CONF_PAIR const *pair)
Definition: conffile.c:3506
int mod_attribute_to_element(const char *name, json_object *map, void *buf)
Map attributes to JSON element names.
Definition: mod.c:225
The main module instance.
Definition: mod.h:47
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
8 Bit unsigned integer.
Definition: radius.h:42
char const * cf_pair_attr(CONF_PAIR const *pair)
Definition: conffile.c:3497
#define DEBUG(fmt,...)
Definition: log.h:175
CONF_SECTION * cf_section_dup(CONF_SECTION *parent, CONF_SECTION const *cs, char const *name1, char const *name2, bool copy_meta)
Duplicate a configuration section.
Definition: conffile.c:697
int json_object_object_get_ex(struct json_object *jso, const char *key, struct json_object **value)
Definition: json_missing.c:40
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *item)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: conffile.c:181
void * mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request)
Build value pairs from the passed JSON object and add to the request.
Definition: mod.c:281
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
json_object * jobj
JSON objects handled by the json-c library.
Definition: couchbase.h:41
RADIUS_PACKET * reply
Outgoing response.
Definition: radiusd.h:225
bool cf_item_is_pair(CONF_ITEM const *item)
Definition: conffile.c:3928
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
32 Bit unsigned integer.
Definition: radius.h:34
static rs_t * conf
Definition: radsniff.c:46
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
lcb_error_t couchbase_init_connection(lcb_t *instance, const char *host, const char *bucket, const char *pass, lcb_uint32_t timeout)
Initialize a Couchbase connection instance.
Definition: couchbase.c:198
enum json_tokener_error jerr
Error values produced by the json-c library.
Definition: couchbase.h:43
64 Bit unsigned integer.
Definition: radius.h:51
Describes a host allowed to send packets to the server.
Definition: clients.h:35
char const * bucket
Couchbase bucket.
Definition: mod.h:54
char name[1]
Attribute name.
Definition: dict.h:89
const char * client_view
Couchbase view that returns client documents.
Definition: mod.h:60
uint8_t data[]
Definition: eap_pwd.h:625
#define TAG_ANY
Definition: pair.h:191
static int _mod_conn_free(rlm_couchbase_handle_t *chandle)
Delete a conneciton pool handle and free related resources.
Definition: mod.c:45
lcb_error_t couchbase_query_view(lcb_t instance, const void *cookie, const char *path, const char *post)
Query a Couchbase design document view.
Definition: couchbase.c:370
json_object * mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp)
Convert value pairs to json objects.
Definition: mod.c:382
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
Definition: connection.c:1291
CONF_SECTION * cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2)
Allocate a CONF_SECTION.
Definition: conffile.c:626
#define WARN(fmt,...)
Definition: log.h:144
Information relating to the parsing of Couchbase document payloads.
Definition: couchbase.h:40
void * cookie
Couchbase cookie (cookie_u cookie_t).
Definition: mod.h:80
unsigned int has_tag
Tagged attribute.
Definition: dict.h:46
CONF_ITEM * cf_item_find_next(CONF_SECTION const *section, CONF_ITEM const *item)
Return the next item after a CONF_ITEM.
Definition: conffile.c:3850
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
int mod_conn_alive(UNUSED void *instance, void *handle)
Check the health of a connection handle.
Definition: mod.c:121
Couchbase instance specific information.
Definition: mod.h:78
char const * longname
Client identifier.
Definition: clients.h:40
#define MAX_KEY_SIZE
Definition: mod.h:41
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
Definition: connection.c:1305
#define FR_TIMEVAL_TO_MS(_x)
Definition: conffile.h:235
String of printable characters.
Definition: radius.h:33
PW_TYPE type
Value type.
Definition: dict.h:80
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:152
#define RCSID(id)
Definition: build.h:135
#define fr_json_object_is_type(_obj, _type)
Definition: json_missing.h:57
int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data)
Create a client CONF_SECTION using a mapping section to map values from a result set to client attrib...
Definition: client.c:775
VALUE_PAIR * fr_pair_make(TALLOC_CTX *ctx, VALUE_PAIR **vps, char const *attribute, char const *value, FR_TOKEN op)
Create a VALUE_PAIR from ASCII strings.
Definition: pair.c:338
#define RDEBUG(fmt,...)
Definition: log.h:243
#define ERROR(fmt,...)
Definition: log.h:145
16 Bit unsigned integer.
Definition: radius.h:43
static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
Handle client value processing for client_map_section()
Definition: mod.c:545
Function prototypes and datatypes used in the module.
int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance)
Build a JSON object map from the configuration "map" section.
Definition: mod.c:150
int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
Ensure accounting documents always contain a valid timestamp.
Definition: mod.c:476
#define RDEBUG3(fmt,...)
Definition: log.h:245
unsigned int has_value
Has a value.
Definition: dict.h:48
size_t strlcat(char *dst, char const *src, size_t siz)
Definition: strlcat.c:40
void client_free(RADCLIENT *client)
Definition: client.c:62