The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 35f236aab2b414ba45eca78ecf0cf48db8c27768 $
19  *
20  * @brief Utility 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 RCSID("$Id: 35f236aab2b414ba45eca78ecf0cf48db8c27768 $")
27 
28 #define LOG_PREFIX "couchbase"
29 
30 #include <freeradius-devel/json/base.h>
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/server/map.h>
33 
34 #include "mod.h"
35 #include "couchbase.h"
36 
37 /** Delete a connection 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 /** Delete a object built by mod_build_api_opts()
57  *
58  * Release the underlying mod_build_api_opts() objects
59  *
60  * @param inst The module instance.
61  * @return 0.
62  */
64 {
65  couchbase_opts_t *opts = inst->api_opts;
66 
67  if (!opts) return 0;
68 
69  DEBUG("Releasing the couchbase api options");
70 
71  for (; opts != NULL; opts = opts->next) {
72  TALLOC_FREE(opts->key);
73  TALLOC_FREE(opts->val);
74  }
75 
76  TALLOC_FREE(opts);
77 
78  /* return */
79  return 0;
80 }
81 
82 /** Build a couchbase_opts_t structure from the configuration "couchbase_api" list
83  *
84  * Parse the "couchbase_api" list from the module configuration file and store this
85  * as a couchbase_opts_t object (key/value list).
86  *
87  * @param conf Configuration list.
88  * @param inst The module instance.
89  * @return
90  * - 0 on success.
91  * - -1 on failure.
92  */
94 {
95  CONF_SECTION *cs; /* module config list */
96  CONF_ITEM *ci; /* config item */
97  CONF_PAIR *cp; /* config pair */
98  couchbase_opts_t *entry = NULL; /* couchbase api options */
99 
100  /* find opts list */
101  cs = cf_section_find(conf, "opts", NULL);
102 
103  /* check list */
104  if (!cs) return 0;
105 
106  /* parse libcouchbase_opts list */
107  cf_log_debug(cs, "opts {");
108 
109  for (ci = cf_item_next(cs, NULL); ci != NULL; ci = cf_item_next(cs, ci)) {
110  /*
111  * Ignore things we don't care about.
112  */
113  if (!cf_item_is_pair(ci)) {
114  continue;
115  }
116 
117  /* get value pair from item */
118  cp = cf_item_to_pair(ci);
119 
120  /* create opts object */
121  if (!entry) {
122  entry = talloc_zero(inst, couchbase_opts_t);
123  inst->api_opts = entry;
124  } else {
125  entry->next = talloc_zero(inst->api_opts, couchbase_opts_t);
126  entry = entry->next;
127  }
128  entry->next = NULL;
129  entry->key = talloc_typed_strdup(entry, cf_pair_attr(cp));
130  entry->val = talloc_typed_strdup(entry, cf_pair_value(cp));
131 
132  /* debugging */
133  cf_log_debug(cs, "\t%s = \"%s\"", entry->key, entry->val);
134  }
135 
136  cf_log_debug(cs, "}");
137 
138  /* return */
139  return 0;
140 }
141 
142 /** Create a new connection pool handle
143  *
144  * Create a new connection to Couchbase within the pool and initialize
145  * information associated with the connection instance.
146  *
147  * @param ctx The connection parent context.
148  * @param instance The module instance.
149  * @param timeout Maximum time to establish the connection.
150  * @return
151  * - New connection handle.
152  * - NULL on error.
153  */
154 void *mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
155 {
156  rlm_couchbase_t *inst = talloc_get_type_abort(instance, rlm_couchbase_t); /* module instance pointer */
157  rlm_couchbase_handle_t *chandle = NULL; /* connection handle pointer */
158  cookie_t *cookie = NULL; /* couchbase cookie */
159  lcb_t cb_inst; /* couchbase connection instance */
160  lcb_error_t cb_error; /* couchbase error status */
161  couchbase_opts_t const *opts = inst->api_opts; /* couchbase extra API settings */
162 
163  /* create instance */
164  cb_error = couchbase_init_connection(&cb_inst, inst->server, inst->bucket, inst->username,
165  inst->password, fr_time_delta_to_sec(timeout), opts);
166 
167  /* check couchbase instance */
168  if (cb_error != LCB_SUCCESS) {
169  ERROR("failed to initiate couchbase connection: %s (0x%x)",
170  lcb_strerror(NULL, cb_error), cb_error);
171  /* destroy/free couchbase instance */
172  lcb_destroy(cb_inst);
173  /* fail */
174  return NULL;
175  }
176 
177  /* allocate memory for couchbase connection instance abstraction */
178  chandle = talloc_zero(ctx, rlm_couchbase_handle_t);
179  talloc_set_destructor(chandle, _mod_conn_free);
180 
181  /* allocate cookie off handle */
182  cookie = talloc_zero(chandle, cookie_t);
183 
184  /* init tokener error and json object */
185  cookie->jerr = json_tokener_success;
186  cookie->jobj = NULL;
187 
188  /* populate handle */
189  chandle->cookie = cookie;
190  chandle->handle = cb_inst;
191 
192  /* return handle struct */
193  return chandle;
194 }
195 
196 /** Check the health of a connection handle
197  *
198  * Attempt to determine the state of the Couchbase connection by requesting
199  * a cluster statistics report. Mark the connection as failed if the request
200  * returns anything other than success.
201  *
202  * @param opaque The module instance (currently unused).
203  * @param connection The connection handle.
204  * @return
205  * - 0 on success (alive).
206  * - -1 on failure (unavailable).
207  */
208 int mod_conn_alive(UNUSED void *opaque, void *connection)
209 {
210  rlm_couchbase_handle_t *chandle = connection; /* connection handle pointer */
211  lcb_t cb_inst = chandle->handle; /* couchbase instance */
212  lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error status */
213 
214  /* attempt to get server stats */
215  if ((cb_error = couchbase_server_stats(cb_inst, NULL)) != LCB_SUCCESS) {
216  /* log error */
217  ERROR("failed to get couchbase server stats: %s (0x%x)",
218  lcb_strerror(NULL, cb_error), cb_error);
219  /* error out */
220  return -1;
221  }
222  return 0;
223 }
224 
225 /** Build a JSON object map from the configuration "map" list
226  *
227  * Parse the "map" list from the module configuration file and store this
228  * as a JSON object (key/value list) in the module instance. This map will be
229  * used to lookup and map attributes for all incoming accounting requests.
230  *
231  * @param conf Configuration list.
232  * @param inst The module instance.
233  * @return
234  * - 0 on success.
235  * - -1 on failure.
236  */
238 {
239  CONF_SECTION *cs; /* module config list */
240  CONF_ITEM *ci; /* config item */
241  CONF_PAIR *cp; /* config pair */
242  const char *attribute, *element; /* attribute and element names */
243 
244  /* find update list */
245  cs = cf_section_find(conf, "update", NULL);
246 
247  /* backwards compatibility */
248  if (!cs) {
249  cs = cf_section_find(conf, "map", NULL);
250  WARN("found deprecated 'map' list - please change to 'update'");
251  }
252 
253  /* check list */
254  if (!cs) {
255  ERROR("failed to find 'update' list in config");
256  /* fail */
257  return -1;
258  }
259 
260  /* create attribute map object */
261  inst->map = json_object_new_object();
262 
263  /* parse update list */
264  for (ci = cf_item_next(cs, NULL); ci != NULL; ci = cf_item_next(cs, ci)) {
265  /* validate item */
266  if (!cf_item_is_pair(ci)) {
267  ERROR("failed to parse invalid item in 'update' list");
268  /* free map */
269  if (inst->map) {
270  json_object_put(inst->map);
271  }
272  /* fail */
273  return -1;
274  }
275 
276  /* get value pair from item */
277  cp = cf_item_to_pair(ci);
278 
279  /* get pair name (attribute name) */
280  attribute = cf_pair_attr(cp);
281 
282  /* get pair value (element name) */
283  element = cf_pair_value(cp);
284 
285  /* add pair name and value */
286  json_object_object_add(inst->map, attribute, json_object_new_string(element));
287 
288  /* debugging */
289  DEBUG3("added attribute '%s' to element '%s' mapping", attribute, element);
290  }
291 
292  /* debugging */
293  DEBUG3("built attribute to element mapping %s", json_object_to_json_string(inst->map));
294 
295  /* return */
296  return 0;
297 }
298 
299 /** Map attributes to JSON element names
300  *
301  * Attempt to map the passed attribute name to the configured JSON element
302  * name using the JSON object map mod_build_attribute_element_map().
303  *
304  * @param name The character name of the requested attribute.
305  * @param map The JSON object map to use for the lookup.
306  * @param buf The buffer where the given element will be stored if found.
307  * @return
308  * - 0 on success.
309  * - -1 on failure.
310  */
311 int mod_attribute_to_element(const char *name, json_object *map, void *buf)
312 {
313  json_object *j_value; /* json object values */
314 
315  /* clear buffer */
316  memset((char *) buf, 0, MAX_KEY_SIZE);
317 
318  /* attempt to map attribute */
319  if (json_object_object_get_ex(map, name, &j_value)) {
320  /* copy and check size */
321  if (strlcpy(buf, json_object_get_string(j_value), MAX_KEY_SIZE) >= MAX_KEY_SIZE) {
322  /* oops ... this value is bigger than our buffer ... error out */
323  ERROR("json map value larger than MAX_KEY_SIZE - %d", MAX_KEY_SIZE);
324  /* return fail */
325  return -1;
326  }
327  /* looks good */
328  return 0;
329  }
330 
331  /* debugging */
332  DEBUG("skipping attribute with no map entry - %s", name);
333 
334  /* default return */
335  return -1;
336 }
337 
338 /** Build value pairs from the passed JSON object and add to the request
339  *
340  * Parse the passed JSON object and create value pairs that will be injected into
341  * the given request for authorization.
342  *
343  * Example JSON document structure:
344  * @code{.json}
345  * {
346  * "docType": "raduser",
347  * "userName": "test",
348  * "control": {
349  * "SHA-Password": {
350  * "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
351  * "op": ":="
352  * }
353  * },
354  * "reply": {
355  * "Reply-Message": {
356  * "value": "Hidey Ho!",
357  * "op": "="
358  * }
359  * }
360  * }
361  * @endcode
362  *
363  * @param[in] ctx to allocate maps in.
364  * @param[in] out Cursor to append maps to.
365  * @param[in] request The request to which the generated pairs should be added.
366  * @param[in] json The JSON object representation of the user document.
367  * @param[in] list The pair list fr_request_attr_control or fr_request_attr_reply
368  * @return
369  * - 1 if no section found.
370  * - 0 on success.
371  * - <0 on error.
372  */
373 int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, fr_dict_attr_t const *list)
374 {
375  json_object *list_obj;
376  char const *list_name = list->name;
377 
378  /*
379  * Check for a section matching the specified list
380  */
381  if (!json_object_object_get_ex(json, list_name, &list_obj)) {
382  RDEBUG2("Couldn't find \"%s\" key in json object - Not adding value pairs for this attribute list",
383  list_name);
384 
385  return 1;
386  }
387 
388  /*
389  * Check the key representing the list is a JSON object
390  */
391  if (!json_object_is_type(list_obj, json_type_object)) {
392  RERROR("Invalid json type for \"%s\" key - Attribute lists must be json objects", list_name);
393 
394  return -1;
395  }
396 
397  fr_dcursor_tail(out); /* Wind to the end */
398 
399  /*
400  * Loop through the keys in this object.
401  *
402  * Where attr_name is the key, and attr_value_obj is
403  * the object containing the attributes value and
404  * operator.
405  */
406  json_object_object_foreach(list_obj, attr_name, attr_value_obj) {
407  json_object *value_obj, *op_obj;
408  fr_dict_attr_t const *da;
409  fr_token_t op;
410 
411  if (!json_object_is_type(attr_value_obj, json_type_object)) {
412  REDEBUG("Invalid json type for \"%s\" key - Attributes must be json objects", attr_name);
413 
414  error:
415  fr_dcursor_free_list(out); /* Free any maps we added */
416  return -1;
417  }
418 
419  RDEBUG3("Parsing %s - \"%s\" : { %s }", list_name,
420  attr_name, json_object_to_json_string(attr_value_obj));
421 
422  /*
423  * Check we have a value key
424  */
425  if (!json_object_object_get_ex(attr_value_obj, "value", &value_obj)) {
426  REDEBUG("Missing \"value\" key in: %s - \"%s\" : { %s }", list_name,
427  attr_name, json_object_to_json_string(attr_value_obj));
428 
429  goto error;
430  }
431 
432  /*
433  * Parse the operator and check its valid
434  */
435  if (json_object_object_get_ex(attr_value_obj, "op", &op_obj)) {
436  char const *op_str;
437 
438  op_str = json_object_get_string(op_obj);
439  if (!op_str) {
440  bad_op:
441  REDEBUG("Invalid \"op\" key in: %s - \"%s\" : { %s }", list_name,
442  attr_name, json_object_to_json_string(attr_value_obj));
443 
444  goto error;
445  }
446 
448  if (!fr_assignment_op[op] && !fr_comparison_op[op]) goto bad_op;
449  } else {
450  op = T_OP_SET; /* The default */
451  }
452 
453  /*
454  * Lookup the string attr_name in the
455  * request dictionary.
456  */
457  da = fr_dict_attr_by_name(NULL, fr_dict_root(request->dict), attr_name);
458  if (!da) {
459  RPERROR("Invalid attribute \"%s\"", attr_name);
460  goto error;
461  }
462 
463  /*
464  * Create a map representing the operation
465  */
466  {
467  fr_value_box_t tmp = { .type = FR_TYPE_NULL };
468  map_t *map;
469 
470  if (fr_json_object_to_value_box(ctx, &tmp, value_obj, da, true) < 0) {
471  bad_value:
472  RPERROR("Failed parsing value for \"%s\"", attr_name);
473  goto error;
474  }
475 
476  if (fr_value_box_cast_in_place(ctx, &tmp, da->type, da) < 0) {
477  fr_value_box_clear(&tmp);
478  goto bad_value;
479  }
480 
481  if (map_afrom_value_box(ctx, &map,
482  attr_name, T_BARE_WORD,
483  &(tmpl_rules_t){
484  .attr = {
485  .dict_def = request->dict,
486  .list_def = list
487  }
488  },
489  op,
490  &tmp, true) < 0) {
491  fr_value_box_clear(&tmp);
492  goto bad_value;
493  }
494 
495  fr_dcursor_insert(out, map);
496  }
497  }
498 
499  return 0;
500 }
501 
502 /** Convert value pairs to json objects
503  *
504  * Take the passed value pair and convert it to a json-c JSON object..
505  *
506  * @param request The request object.
507  * @param vp The value pair to convert.
508  * @return A JSON object.
509  */
511 {
512  char value[255]; /* radius attribute value */
513 
514  /* add this attribute/value pair to our json output */
515  {
516  unsigned int i;
517 
518  switch (vp->vp_type) {
519  case FR_TYPE_UINT32:
520  i = vp->vp_uint32;
521  goto print_int;
522 
523  case FR_TYPE_UINT16:
524  i = vp->vp_uint16;
525  goto print_int;
526 
527  case FR_TYPE_UINT8:
528  i = vp->vp_uint8;
529 
530  print_int:
531  /* skip if we have flags */
532  if (vp->da->flags.has_value) break;
533 #ifdef HAVE_JSON_OBJECT_NEW_INT64
534  /* debug */
535  RDEBUG3("creating new int64 for unsigned 32 bit int/byte/short '%s'", vp->da->name);
536  /* return as 64 bit int - JSON spec does not support unsigned ints */
537  return json_object_new_int64(i);
538 #else
539  /* debug */
540  RDEBUG3("creating new int for unsigned 32 bit int/byte/short '%s'", vp->da->name);
541  /* return as 64 bit int - JSON spec does not support unsigned ints */
542  return json_object_new_int(i);
543 #endif
544 
545  case FR_TYPE_INT32:
546 #ifdef HAVE_JSON_OBJECT_NEW_INT64
547  /* debug */
548  RDEBUG3("creating new int64 for signed 32 bit integer '%s'", vp->da->name);
549  /* return as 64 bit int - json-c represents all ints as 64 bits internally */
550  return json_object_new_int64(vp->vp_int32);
551 #else
552  RDEBUG3("creating new int for signed 32 bit integer '%s'", vp->da->name);
553  /* return as signed int */
554  return json_object_new_int(vp->vp_int32);
555 #endif
556 
557  case FR_TYPE_UINT64:
558 #ifdef HAVE_JSON_OBJECT_NEW_INT64
559  /* debug */
560  RDEBUG3("creating new int64 for 64 bit integer '%s'", vp->da->name);
561  /* return as 64 bit int - because it is a 64 bit int */
562  return json_object_new_int64(vp->vp_uint64);
563 #else
564  /* warning */
565  RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
566  break;
567 #endif
568 
569  default:
570  /* silence warnings - do nothing */
571  break;
572  }
573  }
574 
575  /* keep going if not set above */
576  switch (vp->vp_type) {
577  case FR_TYPE_STRING:
578  /* debug */
579  RDEBUG3("assigning string '%s' as string", vp->da->name);
580  /* return string value */
581  return json_object_new_string(vp->vp_strvalue);
582 
583  default:
584  /* debug */
585  RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
586  /* get standard value */
588  /* return string value from above */
589  return json_object_new_string(value);
590  }
591 }
592 
593 /** Ensure accounting documents always contain a valid timestamp
594  *
595  * Inspect the given JSON object representation of an accounting document
596  * fetched from Couchbase and ensuse it contains a valid (non NULL) timestamp value.
597  *
598  * @param json JSON object representation of an accounting document.
599  * @param vps The value pairs associated with the current accounting request.
600  * @return
601  * - 0 on success.
602  * - -1 on failure.
603  */
604 int mod_ensure_start_timestamp(json_object *json, fr_pair_list_t *vps)
605 {
606  json_object *j_value; /* json object value */
607  struct tm tm; /* struct to hold event time */
608  time_t ts = 0; /* values to hold time in seconds */
609  fr_pair_t *vp; /* values to hold value pairs */
610  char value[255]; /* store radius attribute values and our timestamp */
611 
612  /* get our current start timestamp from our json body */
613  if (json_object_object_get_ex(json, "startTimestamp", &j_value) == 0) {
614  /* debugging ... this shouldn't ever happen */
615  DEBUG("failed to find 'startTimestamp' in current json body");
616  /* return */
617  return -1;
618  }
619 
620  /* check for null value */
621  if (json_object_get_string(j_value) != NULL) {
622  /* already set - nothing left to do */
623  return 0;
624  }
625 
626  /* get current event timestamp */
627  if ((vp = fr_pair_find_by_da(vps, NULL, attr_event_timestamp)) != NULL) {
628  /* get seconds value from attribute */
629  ts = fr_unix_time_to_sec(vp->vp_date);
630  } else {
631  /* debugging */
632  DEBUG("failed to find event timestamp in current request");
633  /* return */
634  return -1;
635  }
636 
637  /* clear value */
638  memset(value, 0, sizeof(value));
639 
640  /* get elapsed session time */
641  if ((vp = fr_pair_find_by_da(vps, NULL, attr_acct_session_time)) != NULL) {
642  /* calculate diff */
643  ts = (ts - vp->vp_uint32);
644  /* calculate start time */
645  size_t length = strftime(value, sizeof(value), "%b %e %Y %H:%M:%S %Z", localtime_r(&ts, &tm));
646  /* check length */
647  if (length > 0) {
648  /* debugging */
649  DEBUG("calculated start timestamp: %s", value);
650  /* store new value in json body */
651  json_object_object_add_ex(json, "startTimestamp", json_object_new_string(value),
652  JSON_C_OBJECT_KEY_IS_CONSTANT);
653  } else {
654  /* debugging */
655  DEBUG("failed to format calculated timestamp");
656  /* return */
657  return -1;
658  }
659  }
660 
661  /* default return */
662  return 0;
663 }
664 
665 /** Handle client value processing for client_map_section()
666  *
667  * @param out Character output
668  * @param cp Configuration pair
669  * @param data The client data
670  * @return
671  * - 0 on success.
672  * - -1 on failure.
673  */
674 static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
675 {
676  json_object *j_value;
677 
678  if (!json_object_object_get_ex((json_object *)data, cf_pair_value(cp), &j_value)) {
679  *out = NULL;
680  return 0;
681  }
682 
683  if (!j_value) return -1;
684 
685  *out = talloc_strdup(NULL, json_object_get_string(j_value));
686  if (!*out) return -1;
687 
688  return 0;
689 }
690 
691 /** Load client entries from Couchbase client documents on startup
692  *
693  * This function executes the view defined in the module configuration and loops
694  * through all returned rows. The view is called with "stale=false" to ensure the
695  * most accurate data available when the view is called. This will force an index
696  * rebuild on this design document in Couchbase. However, since this function is only
697  * run once at server startup this should not be a concern.
698  *
699  * @param inst The module instance.
700  * @param tmpl Default values for new clients.
701  * @param map The client attribute configuration list.
702  * @return
703  * - 0 on success.
704  * - -1 on failure.
705  */
707 {
708  rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
709  char vpath[256], vid[MAX_KEY_SIZE], vkey[MAX_KEY_SIZE]; /* view path and fields */
710  char error[512]; /* view error return */
711  int idx = 0; /* row array index counter */
712  int retval = 0; /* return value */
713  lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */
714  json_object *json, *j_value; /* json object holders */
715  json_object *jrows = NULL; /* json object to hold view rows */
716  CONF_SECTION *client; /* freeradius config list */
717  fr_client_t *c; /* freeradius client */
718 
719  /* get handle */
720  handle = fr_pool_connection_get(inst->pool, NULL);
721 
722  /* check handle */
723  if (!handle) return -1;
724 
725  /* set couchbase instance */
726  lcb_t cb_inst = handle->handle;
727 
728  /* set cookie */
729  cookie_t *cookie = handle->cookie;
730 
731  /* build view path */
732  snprintf(vpath, sizeof(vpath), "%s?stale=false", inst->client_view);
733 
734  /* query view for document */
735  cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL);
736 
737  /* check error and object */
738  if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
739  /* log error */
740  ERROR("failed to execute view request or parse return");
741  /* set return */
742  retval = -1;
743  /* return */
744  goto free_and_return;
745  }
746 
747  /* debugging */
748  DEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
749 
750  /* check for error in json object */
751  if (json_object_object_get_ex(cookie->jobj, "error", &json)) {
752  /* build initial error buffer */
753  strlcpy(error, json_object_get_string(json), sizeof(error));
754  /* get error reason */
755  if (json_object_object_get_ex(cookie->jobj, "reason", &json)) {
756  /* append divider */
757  strlcat(error, " - ", sizeof(error));
758  /* append reason */
759  strlcat(error, json_object_get_string(json), sizeof(error));
760  }
761  /* log error */
762  ERROR("view request failed with error: %s", error);
763  /* set return */
764  retval = -1;
765  /* return */
766  goto free_and_return;
767  }
768 
769  /* check for document id in return */
770  if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) {
771  /* log error */
772  ERROR("failed to fetch rows from view payload");
773  /* set return */
774  retval = -1;
775  /* return */
776  goto free_and_return;
777  }
778 
779  /* get and hold rows */
780  jrows = json_object_get(json);
781 
782  /* free cookie object */
783  if (cookie->jobj) {
784  json_object_put(cookie->jobj);
785  cookie->jobj = NULL;
786  }
787 
788  /* debugging */
789  DEBUG3("jrows == %s", json_object_to_json_string(jrows));
790 
791  /* check for valid row value */
792  if (!json_object_is_type(jrows, json_type_array) || json_object_array_length(jrows) < 1) {
793  /* log error */
794  ERROR("no valid rows returned from view: %s", vpath);
795  /* set return */
796  retval = -1;
797  /* return */
798  goto free_and_return;
799  }
800 
801  /* loop across all row elements */
802  for (idx = 0; (size_t)idx < (size_t)json_object_array_length(jrows); idx++) {
803  /* fetch current index */
804  json = json_object_array_get_idx(jrows, idx);
805 
806  /* get view id */
807  if (json_object_object_get_ex(json, "id", &j_value)) {
808  /* clear view id */
809  memset(vid, 0, sizeof(vid));
810  /* copy and check length */
811  if (strlcpy(vid, json_object_get_string(j_value), sizeof(vid)) >= sizeof(vid)) {
812  ERROR("id from row longer than MAX_KEY_SIZE (%d)",
813  MAX_KEY_SIZE);
814  continue;
815  }
816  } else {
817  WARN("failed to fetch id from row - skipping");
818  continue;
819  }
820 
821  /* get view key */
822  if (json_object_object_get_ex(json, "key", &j_value)) {
823  /* clear view key */
824  memset(vkey, 0, sizeof(vkey));
825  /* copy and check length */
826  if (strlcpy(vkey, json_object_get_string(j_value), sizeof(vkey)) >= sizeof(vkey)) {
827  ERROR("key from row longer than MAX_KEY_SIZE (%d)",
828  MAX_KEY_SIZE);
829  continue;
830  }
831  } else {
832  WARN("failed to fetch key from row - skipping");
833  continue;
834  }
835 
836  /* fetch document */
837  cb_error = couchbase_get_key(cb_inst, cookie, vid);
838 
839  /* check error and object */
840  if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
841  /* log error */
842  ERROR("failed to execute get request or parse return");
843  /* set return */
844  retval = -1;
845  /* return */
846  goto free_and_return;
847  }
848 
849  /* debugging */
850  DEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
851 
852  /* allocate conf list */
853  client = tmpl ? cf_section_dup(NULL, NULL, tmpl, "client", vkey, true) :
854  cf_section_alloc(NULL, NULL, "client", vkey);
855 
856  if (client_map_section(client, map, _get_client_value, cookie->jobj) < 0) {
857  /* free config section */
858  talloc_free(client);
859  /* set return */
860  retval = -1;
861  /* return */
862  goto free_and_return;
863  }
864 
865  /*
866  * @todo These should be parented from something.
867  */
868  c = client_afrom_cs(NULL, client, false, 0);
869  if (!c) {
870  ERROR("failed to allocate client");
871  /* free config section */
872  talloc_free(client);
873  /* set return */
874  retval = -1;
875  /* return */
876  goto free_and_return;
877  }
878 
879  /*
880  * Client parents the CONF_SECTION which defined it.
881  */
882  talloc_steal(c, client);
883 
884  /* attempt to add client */
885  if (!client_add(NULL, c)) {
886  ERROR("failed to add client '%s' from '%s', possible duplicate?", vkey, vid);
887  /* free client */
888  client_free(c);
889  /* set return */
890  retval = -1;
891  /* return */
892  goto free_and_return;
893  }
894 
895  /* debugging */
896  DEBUG("client '%s' added", c->longname);
897 
898  /* free json object */
899  if (cookie->jobj) {
900  json_object_put(cookie->jobj);
901  cookie->jobj = NULL;
902  }
903  }
904 
905  free_and_return:
906 
907  /* free rows */
908  if (jrows) {
909  json_object_put(jrows);
910  }
911 
912  /* free json object */
913  if (cookie->jobj) {
914  json_object_put(cookie->jobj);
915  cookie->jobj = NULL;
916  }
917 
918  /* release handle */
919  if (handle) fr_pool_connection_release(inst->pool, NULL, handle);
920 
921  /* return */
922  return retval;
923 }
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
Common header for all CONF_* types.
Definition: cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition: cf_util.c:597
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:629
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition: cf_util.c:1495
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition: cf_util.c:970
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
CONF_SECTION * cf_section_dup(TALLOC_CTX *ctx, CONF_SECTION *parent, CONF_SECTION const *cs, char const *name1, char const *name2, bool copy_meta)
Duplicate a configuration section.
Definition: cf_util.c:894
#define cf_item_next(_ci, _prev)
Definition: cf_util.h:92
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition: cf_util.h:137
#define cf_log_debug(_cf, _fmt,...)
Definition: cf_util.h:268
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:377
lcb_error_t couchbase_server_stats(lcb_t instance, const void *cookie)
Request Couchbase server statistics.
Definition: couchbase.c:254
lcb_error_t couchbase_init_connection(lcb_t *instance, const char *host, const char *bucket, const char *user, const char *pass, lcb_uint32_t timeout, const couchbase_opts_t *opts)
Initialize a Couchbase connection instance.
Definition: couchbase.c:196
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:325
Couchbase wrapper function prototypes and datatypes.
json_object * jobj
JSON objects handled by the json-c library.
Definition: couchbase.h:45
HIDDEN fr_dict_attr_t const * attr_event_timestamp
Definition: rlm_couchbase.c:78
HIDDEN fr_dict_attr_t const * attr_acct_session_time
Definition: rlm_couchbase.c:77
char * val
Value for the key used in lcb_cntl_string().
Definition: couchbase.h:69
char * key
Key value for lcb_cntl_string().
Definition: couchbase.h:68
enum json_tokener_error jerr
Error values produced by the json-c library.
Definition: couchbase.h:47
couchbase_opts_t * next
Linked list.
Definition: couchbase.h:70
Information relating to the parsing of Couchbase document payloads.
Definition: couchbase.h:44
static void * fr_dcursor_tail(fr_dcursor_t *cursor)
Wind cursor to the tail item in the list.
Definition: dcursor.h:258
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition: dcursor.h:434
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition: dcursor.h:659
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition: dict_util.c:2860
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
Test enumeration values.
Definition: dict_test.h:92
int fr_json_object_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, json_object *object, fr_dict_attr_t const *enumv, bool tainted)
Convert json object to fr_value_box_t.
Definition: json.c:96
char const * longname
Client identifier.
Definition: client.h:84
Describes a host allowed to send packets to the server.
Definition: client.h:77
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RWARN(fmt,...)
Definition: log.h:297
#define RERROR(fmt,...)
Definition: log.h:298
#define RPERROR(fmt,...)
Definition: log.h:302
int map_afrom_value_box(TALLOC_CTX *ctx, map_t **out, char const *lhs, fr_token_t lhs_quote, tmpl_rules_t const *lhs_rules, fr_token_t op, fr_value_box_t *rhs, bool steal_rhs_buffs)
Convert a value box to a map.
Definition: map.c:1270
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
@ FR_TYPE_UINT16
16 Bit unsigned integer.
Definition: merged_model.c:98
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_INT32
32 Bit signed integer.
Definition: merged_model.c:105
@ FR_TYPE_UINT64
64 Bit unsigned integer.
Definition: merged_model.c:100
unsigned long int size_t
Definition: merged_model.c:25
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:163
int mod_attribute_to_element(const char *name, json_object *map, void *buf)
Map attributes to JSON element names.
Definition: mod.c:311
static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
Handle client value processing for client_map_section()
Definition: mod.c:674
int mod_free_api_opts(rlm_couchbase_t *inst)
Delete a object built by mod_build_api_opts()
Definition: mod.c:63
int mod_build_api_opts(CONF_SECTION *conf, rlm_couchbase_t *inst)
Build a couchbase_opts_t structure from the configuration "couchbase_api" list.
Definition: mod.c:93
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:706
int mod_build_attribute_element_map(CONF_SECTION *conf, rlm_couchbase_t *inst)
Build a JSON object map from the configuration "map" list.
Definition: mod.c:237
int mod_ensure_start_timestamp(json_object *json, fr_pair_list_t *vps)
Ensure accounting documents always contain a valid timestamp.
Definition: mod.c:604
void * mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
Create a new connection pool handle.
Definition: mod.c:154
int mod_conn_alive(UNUSED void *opaque, void *connection)
Check the health of a connection handle.
Definition: mod.c:208
int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, fr_dict_attr_t const *list)
Build value pairs from the passed JSON object and add to the request.
Definition: mod.c:373
json_object * mod_value_pair_to_json_object(request_t *request, fr_pair_t *vp)
Convert value pairs to json objects.
Definition: mod.c:510
static int _mod_conn_free(rlm_couchbase_handle_t *chandle)
Delete a connection pool handle and free related resources.
Definition: mod.c:45
Function prototypes and datatypes used in the module.
void * cookie
Couchbase cookie (cookie_u cookie_t).
Definition: mod.h:73
#define MAX_KEY_SIZE
Definition: mod.h:38
void * handle
Real couchbase instance.
Definition: mod.h:72
Couchbase instance specific information.
Definition: mod.h:71
The main module instance.
Definition: mod.h:44
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition: pool.c:1405
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition: pool.c:1390
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define WARN(fmt,...)
Definition: radclient.h:47
static rs_t * conf
Definition: radsniff.c:53
static char const * name
#define FR_SBUFF_OUT(_start, _len_or_end)
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
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:615
void client_free(fr_client_t *client)
Free a client.
Definition: client.c:95
fr_client_t * client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, CONF_SECTION *server_cs, size_t extra)
Allocate a new client from a config section.
Definition: client.c:697
bool client_add(fr_client_list_t *clients, fr_client_t *client)
Add a client to a fr_client_list_t.
Definition: client.c:184
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
size_t strlcat(char *dst, char const *src, size_t siz)
Definition: strlcat.c:35
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
Value pair map.
Definition: map.h:77
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition: table.h:134
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:333
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition: time.h:504
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:645
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
const bool fr_assignment_op[T_TOKEN_LAST]
Definition: token.c:168
fr_table_num_ordered_t const fr_tokens_table[]
Definition: token.c:33
const bool fr_comparison_op[T_TOKEN_LAST]
Definition: token.c:198
enum fr_token fr_token_t
@ T_INVALID
Definition: token.h:39
@ T_BARE_WORD
Definition: token.h:120
@ T_OP_SET
Definition: token.h:84
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition: pair_print.c:40
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition: value.c:3521
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition: value.c:3672
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984