All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_json.c
Go to the documentation of this file.
1 /*
2  * This program is 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 (at
5  * 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: 4610c92d83fcf2d3fe6b0287bf7292cb8dc59675 $
19  * @file rlm_json.c
20  * @brief Parses JSON responses
21  *
22  * @author Arran Cudbard-Bell
23  *
24  * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
25  * @copyright 2015 Network RADIUS SARL <info@networkradius.com>
26  * @copyright 2015 The FreeRADIUS Server Project
27  */
28 RCSID("$Id: 4610c92d83fcf2d3fe6b0287bf7292cb8dc59675 $")
29 
30 #include <freeradius-devel/radiusd.h>
31 #include <freeradius-devel/modules.h>
32 #include <freeradius-devel/map_proc.h>
33 #include <freeradius-devel/rad_assert.h>
34 
35 #include <ctype.h>
36 #include "json.h"
37 
38 #ifndef HAVE_JSON
39 # error "rlm_json should not be built unless json-c is available"
40 #endif
41 
42 /** Forms a linked list of jpath head node pointers (a list of jpaths)
43  */
46  fr_jpath_node_t *jpath; //!< First node in jpath expression.
47  rlm_json_jpath_cache_t *next; //!< Next jpath cache entry.
48 };
49 
50 typedef struct rlm_json_jpath_to_eval {
52  json_object *root;
54 
55 /** Determine if a jpath expression is valid
56  *
57  * @param mod_inst data.
58  * @param xlat_inst data.
59  * @param out Where to write the output (in the format @verbatim<bytes parsed>[:error]@endverbatim).
60  * @param outlen How big out is.
61  * @param request The current request.
62  * @param fmt jpath expression to parse.
63  * @return number of bytes written to out.
64  */
65 static ssize_t jpath_validate_xlat(char **out, size_t outlen,
66  UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
67  REQUEST *request, char const *fmt)
68 {
69  fr_jpath_node_t *head;
70  ssize_t slen, ret;
71  char *jpath_str;
72 
73  slen = fr_jpath_parse(request, &head, fmt, strlen(fmt));
74  if (slen <= 0) {
75  rad_assert(head == NULL);
76  return snprintf(*out, outlen, "%zu:%s", -(slen), fr_strerror());
77  }
78  rad_assert(talloc_get_type_abort(head, fr_jpath_node_t));
79 
80  jpath_str = fr_jpath_asprint(request, head);
81  ret = snprintf(*out, outlen, "%zu:%s", slen, jpath_str);
82  talloc_free(head);
83  talloc_free(jpath_str);
84 
85  return ret;
86 }
87 
88 /** Pre-parse and validate literal jpath expressions for maps
89  *
90  * @param[out] proc_inst the cache structure to fill.
91  * @param[in] mod_inst module instance (unused).
92  * @param[in] src Where to get the JSON data from (unused).
93  * @param[in] maps set of maps to translate to jpaths.
94  * @return
95  * - 0 on success.
96  * - -1 on failure.
97  */
98 static int mod_map_proc_instantiate(void *proc_inst, UNUSED void *mod_inst,
99  UNUSED vp_tmpl_t const *src, vp_map_t const *maps)
100 {
101  rlm_json_jpath_cache_t *cache_inst = proc_inst;
102  vp_map_t const *map;
103  ssize_t slen;
104  rlm_json_jpath_cache_t *cache = cache_inst, **tail = &cache->next;
105 
106  for (map = maps; map; map = map->next) {
107  CONF_PAIR *cp = cf_item_to_pair(map->ci);
108  char const *p;
109 
110 #ifndef HAVE_JSON_OBJECT_GET_INT64
111  if ((map->lhs->type == TMPL_TYPE_ATTR) && (map->lhs->tmpl_da->type == PW_TYPE_INTEGER64)) {
112  cf_log_err_cp(cp, "64bit integers are not supported by linked json-c. "
113  "Upgrade to json-c >= 0.10 to use this feature");
114  return -1;
115  }
116 #endif
117 
118  switch (map->rhs->type) {
119  case TMPL_TYPE_UNPARSED:
120  p = map->rhs->name;
121  slen = fr_jpath_parse(cache, &cache->jpath, p, map->rhs->len);
122  if (slen <= 0) {
123  char *spaces, *text;
124 
125  error:
126  fr_canonicalize_error(cache, &spaces, &text, slen, fr_strerror());
127 
128  cf_log_err_cp(cp, "Syntax error");
129  cf_log_err_cp(cp, "%s", p);
130  cf_log_err_cp(cp, "%s^ %s", spaces, text);
131 
132  talloc_free(spaces);
133  talloc_free(text);
134  return -1;
135  }
136  break;
137 
138  case TMPL_TYPE_DATA:
139  if (map->rhs->tmpl_data_type != PW_TYPE_STRING) {
140  cf_log_err_cp(cp, "Right side of map must be a string");
141  return -1;
142  }
143  p = map->rhs->tmpl_data_value.strvalue;
144  slen = fr_jpath_parse(cache, &cache->jpath, p, map->rhs->tmpl_data_length);
145  if (slen <= 0) goto error;
146  break;
147 
148  default:
149  continue;
150  }
151 
152  /*
153  * Slightly weird... This is here because our first
154  * list member was pre-allocated and passed to the
155  * instantiation callback.
156  */
157  if (map->next) {
158  *tail = cache = talloc_zero(cache, rlm_json_jpath_cache_t);
159  tail = &cache->next;
160  }
161  }
162 
163  return 0;
164 }
165 
166 /** Converts a string value into a #VALUE_PAIR
167  *
168  * @param[in,out] ctx to allocate #VALUE_PAIR (s).
169  * @param[out] out where to write the resulting #VALUE_PAIR.
170  * @param[in] request The current request.
171  * @param[in] map to process.
172  * @param[in] uctx The json tree/jpath expression to evaluate.
173  * @return
174  * - 0 on success.
175  * - -1 on failure.
176  */
177 static int _json_map_proc_get_value(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request,
178  vp_map_t const *map, void *uctx)
179 {
180  VALUE_PAIR *vp;
181  vp_cursor_t cursor;
182  rlm_json_jpath_to_eval_t *to_eval = uctx;
183  value_data_t *head, *value;
184  int ret;
185 
186  *out = NULL;
187 
188  ret = fr_jpath_evaluate_leaf(request, &head, map->lhs->tmpl_da->type, map->lhs->tmpl_da,
189  to_eval->root, to_eval->jpath);
190  if (ret < 0) {
191  REDEBUG("Failed evaluating jpath: %s", fr_strerror());
192  return -1;
193  }
194  if (ret == 0) return 0;
195  rad_assert(head);
196 
197  for (fr_cursor_init(&cursor, out), value = head;
198  value;
199  fr_cursor_insert(&cursor, vp), value = value->next) {
200  vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
201  if (!vp) {
202  error:
203  talloc_free(*out);
204  return -1;
205  }
206  vp->op = map->op;
207 
208  if (value_data_steal(vp, &vp->data, vp->da->type, value) < 0) {
209  REDEBUG("Copying data to attribute failed: %s", fr_strerror());
210  talloc_free(vp);
211  goto error;
212  }
213  }
214  return 0;
215 }
216 
217 /** Parses a JSON string, and executes jpath queries against it to map values to attributes
218  *
219  * @param mod_inst unused.
220  * @param proc_inst cached jpath sequences.
221  * @param request The current request.
222  * @param src string to parse.
223  * @param maps Head of the map list.
224  * @return
225  * - #RLM_MODULE_NOOP no rows were returned or columns matched.
226  * - #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST.
227  * - #RLM_MODULE_FAIL if a fault occurred.
228  */
229 static rlm_rcode_t mod_map_proc(UNUSED void *mod_inst, void *proc_inst, REQUEST *request,
230  char const *src, vp_map_t const *maps)
231 {
232  struct json_tokener *tok;
233 
234  rlm_json_jpath_cache_t *cache = proc_inst;
235  vp_map_t const *map;
236 
237  rlm_json_jpath_to_eval_t to_eval;
238 
239  if ((talloc_array_length(src) - 1) == 0) {
240  REDEBUG("Zero length string is not valid JSON data");
241  return RLM_MODULE_FAIL;
242  }
243 
244  tok = json_tokener_new();
245  to_eval.root = json_tokener_parse_ex(tok, src, (int)(talloc_array_length(src) - 1));
246  if (!to_eval.root) {
247  REMARKER(src, tok->char_offset, json_tokener_error_desc(json_tokener_get_error(tok)));
248  json_tokener_free(tok);
249  return RLM_MODULE_FAIL;
250  }
251  json_tokener_free(tok);
252 
253  for (map = maps; map; map = map->next) {
254  switch (map->rhs->type) {
255  /*
256  * Cached types
257  */
258  case TMPL_TYPE_UNPARSED:
259  case TMPL_TYPE_DATA:
260  to_eval.jpath = cache->jpath;
261 
262  if (map_to_request(request, map, _json_map_proc_get_value, &to_eval) < 0) {
263  error:
264  json_object_put(to_eval.root);
265  return RLM_MODULE_FAIL;
266  }
267  cache = cache->next;
268  break;
269 
270  /*
271  * Dynamic types
272  */
273  default:
274  {
275  ssize_t slen;
276  fr_jpath_node_t *node;
277  char *to_parse;
278 
279  if (tmpl_aexpand(request, &to_parse, request, map->rhs, fr_jpath_escape_func, NULL) < 0) {
280  RERROR("Failed getting jpath data: %s", fr_strerror());
281  goto error;
282  }
283  slen = fr_jpath_parse(request, &node, to_parse, talloc_array_length(to_parse) - 1);
284  if (slen <= 0) {
285  REMARKER(to_parse, -(slen), fr_strerror());
286  talloc_free(to_parse);
287  goto error;
288  }
289  to_eval.jpath = node;
290 
291  if (map_to_request(request, map, _json_map_proc_get_value, &to_eval) < 0) {
292  talloc_free(node);
293  talloc_free(to_parse);
294  goto error;
295  }
296  talloc_free(node);
297  }
298  break;
299  }
300  }
301  json_object_put(to_eval.root);
302 
303  return RLM_MODULE_UPDATED;
304 }
305 
306 static int mod_bootstrap(UNUSED CONF_SECTION *conf, void *instance)
307 {
309 
310  xlat_register(instance, "jpathvalidate", jpath_validate_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN);
311 
312  if (map_proc_register(instance, "json", mod_map_proc, NULL,
313  mod_map_proc_instantiate, sizeof(rlm_json_jpath_cache_t)) < 0) return -1;
314  return 0;
315 }
316 
317 /*
318  * The module name should be the only globally exported symbol.
319  * That is, everything else should be 'static'.
320  *
321  * If the module needs to temporarily modify it's instantiation
322  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
323  * The server will then take care of ensuring that the module
324  * is single-threaded.
325  */
326 extern module_t rlm_json;
327 module_t rlm_json = {
329  .name = "json",
330  .type = RLM_TYPE_THREAD_SAFE,
331  .bootstrap = mod_bootstrap,
332 };
int int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert vp_map_t to VALUE_PAIR (s) and add them to a REQUEST.
Definition: map.c:1019
#define RERROR(fmt,...)
Definition: log.h:207
int fr_jpath_evaluate_leaf(TALLOC_CTX *ctx, value_data_t **out, PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv, json_object *root, fr_jpath_node_t const *jpath)
Evaluate a parsed jpath expression against a json-c tree.
Definition: jpath.c:379
int xlat_register(void *mod_inst, char const *name, xlat_func_t func, xlat_escape_t escape, xlat_instantiate_t instantiate, size_t inst_size, size_t buf_len)
Register an xlat function.
Definition: xlat.c:717
Metadata exported by the module.
Definition: modules.h:134
char const * name
Raw string used to create the template.
Definition: tmpl.h:190
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
Dictionary attribute.
Definition: tmpl.h:133
vp_tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:48
rlm_json_jpath_cache_t * next
Next jpath cache entry.
Definition: rlm_json.c:47
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
static int _json_map_proc_get_value(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
Converts a string value into a VALUE_PAIR.
Definition: rlm_json.c:177
#define REMARKER(_m, _i, _e)
Output string with error marker, showing where format error occurred.
Definition: log.h:306
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
Unparsed literal string.
Definition: tmpl.h:131
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
void void void cf_log_err_cp(CONF_PAIR const *cp, char const *fmt,...) CC_HINT(format(printf
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
ssize_t fr_jpath_parse(TALLOC_CTX *ctx, fr_jpath_node_t **head, char const *in, size_t inlen)
Parse a jpath string.
Definition: jpath.c:817
int map_proc_register(void *mod_inst, char const *name, map_proc_func_t evaluate, xlat_escape_t escape, map_proc_instantiate_t instantiate, size_t inst_size)
Register a map processor.
Definition: map_proc.c:132
static expr_map_t map[]
Definition: rlm_expr.c:169
#define XLAT_DEFAULT_BUF_LEN
Definition: xlat.h:89
static int mod_bootstrap(UNUSED CONF_SECTION *conf, void *instance)
Definition: rlm_json.c:306
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
struct vp_map * next
The next valuepair map.
Definition: map.h:55
#define rad_assert(expr)
Definition: rad_assert.h:38
static int mod_map_proc_instantiate(void *proc_inst, UNUSED void *mod_inst, UNUSED vp_tmpl_t const *src, vp_map_t const *maps)
Pre-parse and validate literal jpath expressions for maps.
Definition: rlm_json.c:98
Value in native format.
Definition: tmpl.h:138
static char spaces[]
Definition: proto.c:28
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
Definition: cursor.c:321
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *item)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: conffile.c:181
value_data_t * next
Next in a series of value_data.
Definition: pair.h:88
int value_data_steal(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type, value_data_t const *src)
Copy value data verbatim moving any buffers to the specified context.
Definition: value.c:1512
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
ssize_t tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a template to a string, allocing a new buffer to hold the string.
Definition: tmpl.c:1653
const char * json_tokener_error_desc(enum json_tokener_error jerr)
Definition: json_missing.c:68
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
FR_TOKEN op
Operator to use when moving or inserting valuepair into a list.
Definition: pair.h:118
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rlm_rcode_t mod_map_proc(UNUSED void *mod_inst, void *proc_inst, REQUEST *request, char const *src, vp_map_t const *maps)
Parses a JSON string, and executes jpath queries against it to map values to attributes.
Definition: rlm_json.c:229
tmpl_type_t type
What type of value tmpl refers to.
Definition: tmpl.h:188
static rs_t * conf
Definition: radsniff.c:46
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
size_t len
Length of the raw string used to create the template.
Definition: tmpl.h:191
char * fr_jpath_asprint(TALLOC_CTX *ctx, fr_jpath_node_t const *head)
Print a node list to a string for debugging.
Definition: jpath.c:411
Node in a jpath selector sequence.
Definition: jpath.c:91
void fr_json_version_print(void)
Print JSON-C version.
Definition: json.c:265
64 Bit unsigned integer.
Definition: radius.h:51
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
size_t fr_jpath_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Escapes special chars.
Definition: jpath.c:110
void void void fr_canonicalize_error(TALLOC_CTX *ctx, char **spaces, char **text, ssize_t slen, char const *msg)
Canonicalize error strings, removing tabs, and generate spaces for error marker.
Definition: log.c:359
static ssize_t jpath_validate_xlat(char **out, size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt)
Determine if a jpath expression is valid.
Definition: rlm_json.c:65
#define REDEBUG(fmt,...)
Definition: log.h:254
struct rlm_json_jpath_to_eval rlm_json_jpath_to_eval_t
enum json_tokener_error json_tokener_get_error(json_tokener *tok)
Definition: json_missing.c:62
fr_jpath_node_t * jpath
First node in jpath expression.
Definition: rlm_json.c:46
VALUE_PAIR * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute.
Definition: pair.c:58
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
String of printable characters.
Definition: radius.h:33
PW_TYPE type
Value type.
Definition: dict.h:80
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
Value pair map.
Definition: map.h:46
module_t rlm_json
Definition: rlm_json.c:327
A source or sink of value data.
Definition: tmpl.h:187
CONF_ITEM * ci
Config item that the map was created from.
Definition: map.h:52
fr_jpath_node_t const * jpath
Definition: rlm_json.c:51
json_object * root
Definition: rlm_json.c:52
value_data_t data
Definition: pair.h:133