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: 1d4e024b8d037410be86696b4f3f972fab9068ad $
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: 1d4e024b8d037410be86696b4f3f972fab9068ad $")
28
29 #include <freeradius-devel/radiusd.h>
30
31 #include "mod.h"
32 #include "couchbase.h"
33 #include "jsonc_missing.h"
34
35 /** Delete a conneciton pool handle and free related resources
36 *
37 * Destroys the underlying Couchbase connection handle freeing any related
38 * resources and closes the socket connection.
39 *
40 * @param chandle The connection handle to destroy.
41 * @return Always returns 0 (success) in all conditions.
42 */
_mod_conn_free(rlm_couchbase_handle_t * chandle)43 static int _mod_conn_free(rlm_couchbase_handle_t *chandle)
44 {
45 lcb_t cb_inst = chandle->handle; /* couchbase instance */
46
47 /* destroy/free couchbase instance */
48 lcb_destroy(cb_inst);
49
50 /* return */
51 return 0;
52 }
53
54 /** Create a new connection pool handle
55 *
56 * Create a new connection to Couchbase within the pool and initialize
57 * information associated with the connection instance.
58 *
59 * @param ctx The connection parent context.
60 * @param instance The module instance.
61 * @return The new connection handle or NULL on error.
62 */
mod_conn_create(TALLOC_CTX * ctx,void * instance)63 void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
64 {
65 rlm_couchbase_t *inst = instance; /* module instance pointer */
66 rlm_couchbase_handle_t *chandle = NULL; /* connection handle pointer */
67 cookie_t *cookie = NULL; /* couchbase cookie */
68 lcb_t cb_inst; /* couchbase connection instance */
69 lcb_error_t cb_error; /* couchbase error status */
70
71 /* create instance */
72 cb_error = couchbase_init_connection(&cb_inst, inst->server, inst->bucket, inst->password);
73
74 /* check couchbase instance */
75 if (cb_error != LCB_SUCCESS) {
76 ERROR("rlm_couchbase: failed to initiate couchbase connection: %s (0x%x)",
77 lcb_strerror(NULL, cb_error), cb_error);
78 /* destroy/free couchbase instance */
79 lcb_destroy(cb_inst);
80 /* fail */
81 return NULL;
82 }
83
84 /* allocate memory for couchbase connection instance abstraction */
85 chandle = talloc_zero(ctx, rlm_couchbase_handle_t);
86 talloc_set_destructor(chandle, _mod_conn_free);
87
88 /* allocate cookie off handle */
89 cookie = talloc_zero(chandle, cookie_t);
90
91 /* init tokener error and json object */
92 cookie->jerr = json_tokener_success;
93 cookie->jobj = NULL;
94
95 /* populate handle */
96 chandle->cookie = cookie;
97 chandle->handle = cb_inst;
98
99 /* return handle struct */
100 return chandle;
101 }
102
103 /** Build a JSON object map from the configuration "update" section
104 *
105 * Parse the "map" section from the module configuration file and store this
106 * as a JSON object (key/value list) in the module instance. This map will be
107 * used to lookup and map attributes for all incoming accounting requests.
108 *
109 * @param conf Configuration section.
110 * @param instance The module instance.
111 * @return Returns 0 on success, -1 on error.
112 */
mod_build_attribute_element_map(CONF_SECTION * conf,void * instance)113 int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance)
114 {
115 rlm_couchbase_t *inst = instance; /* our module instance */
116 CONF_SECTION *cs; /* module config section */
117 CONF_ITEM *ci; /* config item */
118 CONF_PAIR *cp; /* conig pair */
119 const char *attribute, *element; /* attribute and element names */
120
121 /* find update section */
122 cs = cf_section_sub_find(conf, "update");
123
124 /* backwards compatibility */
125 if (!cs) {
126 cs = cf_section_sub_find(conf, "map");
127 WARN("rlm_couchbase: found deprecated 'map' section - please change to 'update'");
128 }
129
130 /* check section */
131 if (!cs) {
132 ERROR("rlm_couchbase: failed to find 'update' section in config");
133 /* fail */
134 return -1;
135 }
136
137 /* create attribute map object */
138 inst->map = json_object_new_object();
139
140 /* parse update section */
141 for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
142 /* validate item */
143 if (!cf_item_is_pair(ci)) {
144 ERROR("rlm_couchbase: failed to parse invalid item in 'update' section");
145 /* free map */
146 if (inst->map) {
147 json_object_put(inst->map);
148 }
149 /* fail */
150 return -1;
151 }
152
153 /* get value pair from item */
154 cp = cf_item_to_pair(ci);
155
156 /* get pair name (attribute name) */
157 attribute = cf_pair_attr(cp);
158
159 if (!dict_attrbyname(attribute)) {
160 ERROR("Unknown RADIUS attribute '%s'", attribute);
161 return -1;
162 }
163
164 /* get pair value (element name) */
165 element = cf_pair_value(cp);
166
167 /* add pair name and value */
168 json_object_object_add(inst->map, attribute, json_object_new_string(element));
169
170 /* debugging */
171 DEBUG3("rlm_couchbase: added attribute '%s' to element '%s' mapping", attribute, element);
172 }
173
174 /* debugging */
175 DEBUG3("rlm_couchbase: built attribute to element mapping %s", json_object_to_json_string(inst->map));
176
177 /* return */
178 return 0;
179 }
180
181 /** Map attributes to JSON element names
182 *
183 * Attempt to map the passed attribute name to the configured JSON element
184 * name using the JSON object map mod_build_attribute_element_map().
185 *
186 * @param name The character name of the requested attribute.
187 * @param map The JSON object map to use for the lookup.
188 * @param buf The buffer where the given element will be stored if found.
189 * @return Returns 0 on success, -1 on error.
190 */
mod_attribute_to_element(const char * name,json_object * map,void * buf)191 int mod_attribute_to_element(const char *name, json_object *map, void *buf)
192 {
193 json_object *jval; /* json object values */
194
195 /* clear buffer */
196 memset((char *) buf, 0, MAX_KEY_SIZE);
197
198 /* attempt to map attribute */
199 if (json_object_object_get_ex(map, name, &jval)) {
200 /* copy and check size */
201 if (strlcpy(buf, json_object_get_string(jval), MAX_KEY_SIZE) >= MAX_KEY_SIZE) {
202 /* oops ... this value is bigger than our buffer ... error out */
203 ERROR("rlm_couchbase: json map value larger than MAX_KEY_SIZE - %d", MAX_KEY_SIZE);
204 /* return fail */
205 return -1;
206 }
207 /* looks good */
208 return 0;
209 }
210
211 /* debugging */
212 DEBUG("rlm_couchbase: skipping attribute with no map entry - %s", name);
213
214 /* default return */
215 return -1;
216 }
217
218 /** Build value pairs from the passed JSON object and add to the request
219 *
220 * Parse the passed JSON object and create value pairs that will be injected into
221 * the given request for authorization.
222 *
223 * Example JSON document structure:
224 * @code{.json}
225 * {
226 * "docType": "raduser",
227 * "userName": "test",
228 * "config": {
229 * "SHA-Password": {
230 * "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
231 * "op": ":="
232 * }
233 * },
234 * "reply": {
235 * "Reply-Message": {
236 * "value": "Hidey Ho!",
237 * "op": "="
238 * }
239 * }
240 * }
241 * @endcode
242 *
243 * @param json The JSON object representation of the user documnent.
244 * @param section The pair section ("config" or "reply").
245 * @param request The request to which the generated pairs should be added.
246 */
mod_json_object_to_value_pairs(json_object * json,const char * section,REQUEST * request)247 void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request)
248 {
249 json_object *jobj, *jval, *jop; /* json object pointers */
250 TALLOC_CTX *ctx; /* talloc context for fr_pair_make */
251 VALUE_PAIR *vp, **ptr; /* value pair and value pair pointer for fr_pair_make */
252
253 /* assign ctx and vps for fr_pair_make based on section */
254 if (strcmp(section, "config") == 0) {
255 ctx = request;
256 ptr = &(request->config);
257 } else if (strcmp(section, "reply") == 0) {
258 ctx = request->reply;
259 ptr = &(request->reply->vps);
260 } else {
261 /* log error - this shouldn't happen */
262 RERROR("invalid section passed for fr_pair_make");
263 /* return */
264 return NULL;
265 }
266
267 /* get config payload */
268 if (json_object_object_get_ex(json, section, &jobj)) {
269 /* make sure we have the correct type */
270 if ((jobj == NULL) || !json_object_is_type(jobj, json_type_object)) {
271 /* log error */
272 RERROR("invalid json type for '%s' section - sections must be json objects", section);
273 /* reuturn */
274 return NULL;
275 }
276 /* loop through object */
277 json_object_object_foreach(jobj, attribute, json_vp) {
278 /* check for appropriate type in value and op */
279 if ((jobj == NULL) || !json_object_is_type(json_vp, json_type_object)) {
280 /* log error */
281 RERROR("invalid json type for '%s' attribute - attributes must be json objects",
282 attribute);
283 /* return */
284 return NULL;
285 }
286 /* debugging */
287 RDEBUG("parsing '%s' attribute: %s => %s", section, attribute,
288 json_object_to_json_string(json_vp));
289 /* create pair from json object */
290 if (json_object_object_get_ex(json_vp, "value", &jval) &&
291 json_object_object_get_ex(json_vp, "op", &jop)) {
292 /* check for null before getting type */
293 if (jval == NULL) return NULL;
294 /* make correct pairs based on json object type */
295 switch (json_object_get_type(jval)) {
296 case json_type_double:
297 case json_type_int:
298 case json_type_string:
299 /* debugging */
300 RDEBUG("adding '%s' attribute to '%s' section", attribute, section);
301 /* add pair */
302 vp = fr_pair_make(ctx, ptr, attribute, json_object_get_string(jval),
303 fr_str2int(fr_tokens, json_object_get_string(jop), 0));
304 /* check pair */
305 if (!vp) {
306 RERROR("could not build value pair for '%s' attribute (%s)",
307 attribute, fr_strerror());
308 /* return */
309 return NULL;
310 }
311 break;
312
313 case json_type_object:
314 case json_type_array:
315 /* log error - we want to handle these eventually */
316 RERROR("skipping unhandled nested json object or array value pair object");
317 break;
318
319 default:
320 /* log error - this shouldn't ever happen */
321 RERROR("skipping unhandled json type in value pair object");
322 break;
323 }
324 } else {
325 /* log error */
326 RERROR("failed to get 'value' or 'op' element for '%s' attribute", attribute);
327 }
328 }
329 /* return NULL */
330 return NULL;
331 }
332
333 /* debugging */
334 RDEBUG("couldn't find '%s' section in json object - not adding value pairs for this section", section);
335
336 /* return NULL */
337 return NULL;
338 }
339
340 /** Convert value pairs to json objects
341 *
342 * Take the passed value pair and convert it to a json-c JSON object.
343 * This code is heavily based on the vp_prints_value_json() function
344 * from src/lib/print.c.
345 *
346 * @param request The request object.
347 * @param vp The value pair to convert.
348 * @param raw_value Print all values as raw, even if enum values exist.
349 * @return Returns a JSON object.
350 */
mod_value_pair_to_json_object(REQUEST * request,VALUE_PAIR * vp,bool raw_value)351 json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp, bool raw_value)
352 {
353 char value[255]; /* radius attribute value */
354
355 /* add this attribute/value pair to our json output */
356 if (!vp->da->flags.has_tag) {
357 unsigned int i;
358
359 switch (vp->da->type) {
360 case PW_TYPE_INTEGER:
361 i = vp->vp_integer;
362 goto print_int;
363
364 case PW_TYPE_SHORT:
365 i = vp->vp_short;
366 goto print_int;
367
368 case PW_TYPE_BYTE:
369 i = vp->vp_byte;
370
371 print_int:
372 /* add a raw value to our json output - i.e. do not try resolve enum.
373 skip this if raw_value is false, and we have a value in the dictionary */
374 if (!raw_value && !vp->da->flags.has_value) break;
375 #ifdef HAVE_JSON_OBJECT_NEW_INT64
376 /* debug */
377 RDEBUG3("creating new int64 for unsigned 32 bit int/byte/short '%s'", vp->da->name);
378 /* return as 64 bit int - JSON spec does not support unsigned ints */
379 return json_object_new_int64(i);
380 #else
381 /* debug */
382 RDEBUG3("creating new int for unsigned 32 bit int/byte/short '%s'", vp->da->name);
383 /* return as 64 bit int - JSON spec does not support unsigned ints */
384 return json_object_new_int(i);
385 #endif
386
387 case PW_TYPE_SIGNED:
388 #ifdef HAVE_JSON_OBJECT_NEW_INT64
389 /* debug */
390 RDEBUG3("creating new int64 for signed 32 bit integer '%s'", vp->da->name);
391 /* return as 64 bit int - json-c represents all ints as 64 bits internally */
392 return json_object_new_int64(vp->vp_signed);
393 #else
394 RDEBUG3("creating new int for signed 32 bit integer '%s'", vp->da->name);
395 /* return as signed int */
396 return json_object_new_int(vp->vp_signed);
397 #endif
398
399 case PW_TYPE_INTEGER64:
400 #ifdef HAVE_JSON_OBJECT_NEW_INT64
401 /* debug */
402 RDEBUG3("creating new int64 for 64 bit integer '%s'", vp->da->name);
403 /* return as 64 bit int - because it is a 64 bit int */
404 return json_object_new_int64(vp->vp_integer64);
405 #else
406 /* warning */
407 RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
408 break;
409 #endif
410
411 default:
412 /* silence warnings - do nothing */
413 break;
414 }
415 }
416
417 /* keep going if not set above */
418 switch (vp->da->type) {
419 case PW_TYPE_STRING:
420 /* debug */
421 RDEBUG3("assigning string '%s' as string", vp->da->name);
422 /* return string value */
423 return json_object_new_string(vp->vp_strvalue);
424
425 default:
426 /* debug */
427 RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
428 /* get standard value */
429 vp_prints_value(value, sizeof(value), vp, 0);
430 /* return string value from above */
431 return json_object_new_string(value);
432 }
433 }
434
435 /** Ensure accounting documents always contain a valid timestamp
436 *
437 * Inspect the given JSON object representation of an accounting document
438 * fetched from Couchbase and ensuse it contains a valid (non NULL) timestamp value.
439 *
440 * @param json JSON object representation of an accounting document.
441 * @param vps The value pairs associated with the current accounting request.
442 * @return Returns 0 on success, -1 on error.
443 */
mod_ensure_start_timestamp(json_object * json,VALUE_PAIR * vps)444 int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
445 {
446 json_object *jval; /* json object value */
447 struct tm tm; /* struct to hold event time */
448 time_t ts = 0; /* values to hold time in seconds */
449 VALUE_PAIR *vp; /* values to hold value pairs */
450 char value[255]; /* store radius attribute values and our timestamp */
451
452 /* get our current start timestamp from our json body */
453 if (json_object_object_get_ex(json, "startTimestamp", &jval) == 0) {
454 /* debugging ... this shouldn't ever happen */
455 DEBUG("rlm_couchbase: failed to find 'startTimestamp' in current json body");
456 /* return */
457 return -1;
458 }
459
460 /* check for null value */
461 if (json_object_get_string(jval) != NULL) {
462 /* already set - nothing left to do */
463 return 0;
464 }
465
466 /* get current event timestamp */
467 if ((vp = fr_pair_find_by_num(vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
468 /* get seconds value from attribute */
469 ts = vp->vp_date;
470 } else {
471 /* debugging */
472 DEBUG("rlm_couchbase: failed to find event timestamp in current request");
473 /* return */
474 return -1;
475 }
476
477 /* clear value */
478 memset(value, 0, sizeof(value));
479
480 /* get elapsed session time */
481 if ((vp = fr_pair_find_by_num(vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL) {
482 /* calculate diff */
483 ts = (ts - vp->vp_integer);
484 /* calculate start time */
485 size_t length = strftime(value, sizeof(value), "%b %e %Y %H:%M:%S %Z", localtime_r(&ts, &tm));
486 /* check length */
487 if (length > 0) {
488 /* debugging */
489 DEBUG("rlm_couchbase: calculated start timestamp: %s", value);
490 /* store new value in json body */
491 json_object_object_add(json, "startTimestamp", json_object_new_string(value));
492 } else {
493 /* debugging */
494 DEBUG("rlm_couchbase: failed to format calculated timestamp");
495 /* return */
496 return -1;
497 }
498 }
499
500 /* default return */
501 return 0;
502 }
503
504 /** Handle client value processing for client_map_section()
505 *
506 * @param out Character output
507 * @param cp Configuration pair
508 * @param data The client data
509 * @return Returns 0 on success, -1 on error.
510 */
_get_client_value(char ** out,CONF_PAIR const * cp,void * data)511 static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
512 {
513 json_object *jval;
514
515 if (!json_object_object_get_ex((json_object *)data, cf_pair_value(cp), &jval)) {
516 *out = NULL;
517 return 0;
518 }
519
520 if (!jval) return -1;
521
522 *out = talloc_strdup(NULL, json_object_get_string(jval));
523 if (!*out) return -1;
524
525 return 0;
526 }
527
528 /** Load client entries from Couchbase client documents on startup
529 *
530 * This function executes the view defined in the module configuration and loops
531 * through all returned rows. The view is called with "stale=false" to ensure the
532 * most accurate data available when the view is called. This will force an index
533 * rebuild on this design document in Couchbase. However, since this function is only
534 * run once at sever startup this should not be a concern.
535 *
536 * @param inst The module instance.
537 * @param tmpl Default values for new clients.
538 * @param map The client attribute configuration section.
539 * @return Returns 0 on success, -1 on error.
540 */
mod_load_client_documents(rlm_couchbase_t * inst,CONF_SECTION * tmpl,CONF_SECTION * map)541 int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
542 {
543 rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
544 char vpath[256], vid[MAX_KEY_SIZE], vkey[MAX_KEY_SIZE]; /* view path and fields */
545 char error[512]; /* view error return */
546 size_t idx = 0; /* row array index counter */
547 int retval = 0; /* return value */
548 lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */
549 json_object *json, *jval; /* json object holders */
550 json_object *jrows = NULL; /* json object to hold view rows */
551 CONF_SECTION *client; /* freeradius config section */
552 RADCLIENT *c; /* freeradius client */
553 int slen;
554
555 /* get handle */
556 handle = fr_connection_get(inst->pool);
557
558 /* check handle */
559 if (!handle) return -1;
560
561 /* set couchbase instance */
562 lcb_t cb_inst = handle->handle;
563
564 /* set cookie */
565 cookie_t *cookie = handle->cookie;
566
567 /* build view path */
568 slen = snprintf(vpath, sizeof(vpath), "%s?stale=false", inst->client_view);
569 if (slen >= (int) sizeof(vpath) || slen < 0) {
570 ERROR("rlm_couchbase: view path too long");
571 retval=-1;
572 goto free_and_return;
573 }
574
575
576 /* query view for document */
577 cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL);
578
579 /* check error and object */
580 if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
581 /* log error */
582 ERROR("rlm_couchbase: failed to execute view request or parse return");
583 /* set return */
584 retval = -1;
585 /* return */
586 goto free_and_return;
587 }
588
589 /* debugging */
590 DEBUG3("rlm_couchbase: cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
591
592 /* check for error in json object */
593 if (json_object_object_get_ex(cookie->jobj, "error", &json)) {
594 /* build initial error buffer */
595 strlcpy(error, json_object_get_string(json), sizeof(error));
596 /* get error reason */
597 if (json_object_object_get_ex(cookie->jobj, "reason", &json)) {
598 /* append divider */
599 strlcat(error, " - ", sizeof(error));
600 /* append reason */
601 strlcat(error, json_object_get_string(json), sizeof(error));
602 }
603 /* log error */
604 ERROR("rlm_couchbase: view request failed with error: %s", error);
605 /* set return */
606 retval = -1;
607 /* return */
608 goto free_and_return;
609 }
610
611 /* check for document id in return */
612 if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) {
613 /* log error */
614 ERROR("rlm_couchbase: failed to fetch rows from view payload");
615 /* set return */
616 retval = -1;
617 /* return */
618 goto free_and_return;
619 }
620
621 /* get and hold rows */
622 jrows = json_object_get(json);
623
624 /* free cookie object */
625 if (cookie->jobj) {
626 json_object_put(cookie->jobj);
627 cookie->jobj = NULL;
628 }
629
630 /* debugging */
631 DEBUG3("rlm_couchbase: jrows == %s", json_object_to_json_string(jrows));
632
633 /* check for valid row value */
634 if ((jrows == NULL) || !json_object_is_type(jrows, json_type_array) || json_object_array_length(jrows) < 1) {
635 /* log error */
636 ERROR("rlm_couchbase: no valid rows returned from view: %s", vpath);
637 /* set return */
638 retval = -1;
639 /* return */
640 goto free_and_return;
641 }
642
643 /* loop across all row elements */
644 for (idx = 0; idx < (size_t)json_object_array_length(jrows); idx++) {
645 /* fetch current index */
646 json = json_object_array_get_idx(jrows, idx);
647
648 /* get view id */
649 if (json_object_object_get_ex(json, "id", &jval)) {
650 /* clear view id */
651 memset(vid, 0, sizeof(vid));
652 /* copy and check length */
653 if (strlcpy(vid, json_object_get_string(jval), sizeof(vid)) >= sizeof(vid)) {
654 ERROR("rlm_couchbase: id from row longer than MAX_KEY_SIZE (%d)",
655 MAX_KEY_SIZE);
656 continue;
657 }
658 } else {
659 WARN("rlm_couchbase: failed to fetch id from row - skipping");
660 continue;
661 }
662
663 /* get view key */
664 if (json_object_object_get_ex(json, "key", &jval)) {
665 /* clear view key */
666 memset(vkey, 0, sizeof(vkey));
667 /* copy and check length */
668 if (strlcpy(vkey, json_object_get_string(jval), sizeof(vkey)) >= sizeof(vkey)) {
669 ERROR("rlm_couchbase: key from row longer than MAX_KEY_SIZE (%d)",
670 MAX_KEY_SIZE);
671 continue;
672 }
673 } else {
674 WARN("rlm_couchbase: failed to fetch key from row - skipping");
675 continue;
676 }
677
678 /* fetch document */
679 cb_error = couchbase_get_key(cb_inst, cookie, vid);
680
681 /* check error and object */
682 if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
683 /* log error */
684 ERROR("rlm_couchbase: failed to execute get request or parse return");
685 /* set return */
686 retval = -1;
687 /* return */
688 goto free_and_return;
689 }
690
691 /* debugging */
692 DEBUG3("rlm_couchbase: cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
693
694 /* allocate conf section */
695 client = tmpl ? cf_section_dup(NULL, tmpl, "client", vkey, true) :
696 cf_section_alloc(NULL, "client", vkey);
697
698 if (client_map_section(client, map, _get_client_value, cookie->jobj) < 0) {
699 /* free config setion */
700 talloc_free(client);
701 /* set return */
702 retval = -1;
703 /* return */
704 goto free_and_return;
705 }
706
707 /*
708 * @todo These should be parented from something.
709 */
710 c = client_afrom_cs(NULL, client, false, false);
711 if (!c) {
712 ERROR("rlm_couchbase: failed to allocate client");
713 /* free config setion */
714 talloc_free(client);
715 /* set return */
716 retval = -1;
717 /* return */
718 goto free_and_return;
719 }
720
721 /*
722 * Client parents the CONF_SECTION which defined it.
723 */
724 talloc_steal(c, client);
725
726 /* attempt to add client */
727 if (!client_add(NULL, c)) {
728 ERROR("rlm_couchbase: failed to add client '%s' from '%s', possible duplicate?", vkey, vid);
729 /* free client */
730 client_free(c);
731 /* set return */
732 retval = -1;
733 /* return */
734 goto free_and_return;
735 }
736
737 /* debugging */
738 DEBUG("rlm_couchbase: client '%s' added", c->longname);
739
740 /* free json object */
741 if (cookie->jobj) {
742 json_object_put(cookie->jobj);
743 cookie->jobj = NULL;
744 }
745 }
746
747 free_and_return:
748
749 /* free rows */
750 if (jrows) {
751 json_object_put(jrows);
752 }
753
754 /* free json object */
755 if (cookie->jobj) {
756 json_object_put(cookie->jobj);
757 cookie->jobj = NULL;
758 }
759
760 /* release handle */
761 if (handle) {
762 fr_connection_release(inst->pool, handle);
763 }
764
765 /* return */
766 return retval;
767 }
768