The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sqlcounter.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: c0f1a6590cd92cd855d5d20af8818d2377042da0 $
19  * @file rlm_sqlcounter.c
20  * @brief Tracks data usage and other counters using SQL.
21  *
22  * @copyright 2001,2006 The FreeRADIUS server project
23  * @copyright 2001 Alan DeKok (aland@freeradius.org)
24  */
25 RCSID("$Id: c0f1a6590cd92cd855d5d20af8818d2377042da0 $")
26 
27 #define LOG_PREFIX "sqlcounter"
28 
29 #include <rlm_sql.h>
30 #include <freeradius-devel/server/base.h>
31 #include <freeradius-devel/server/module_rlm.h>
32 #include <freeradius-devel/util/debug.h>
33 #include <freeradius-devel/util/dict.h>
34 #include <freeradius-devel/unlang/function.h>
35 
36 #include <ctype.h>
37 
38 /*
39  * Note: When your counter spans more than 1 period (ie 3 months
40  * or 2 weeks), this module probably does NOT do what you want! It
41  * calculates the range of dates to count across by first calculating
42  * the End of the Current period and then subtracting the number of
43  * periods you specify from that to determine the beginning of the
44  * range.
45  *
46  * For example, if you specify a 3 month counter and today is June 15th,
47  * the end of the current period is June 30. Subtracting 3 months from
48  * that gives April 1st. So, the counter will sum radacct entries from
49  * April 1st to June 30. Then, next month, it will sum entries from
50  * May 1st to July 31st.
51  *
52  * To fix this behavior, we need to add some way of storing the Next
53  * Reset Time.
54  */
55 
56 /*
57  * Define a structure for our module configuration.
58  *
59  * These variables do not need to be in a structure, but it's
60  * a lot cleaner to do so, and a pointer to the structure can
61  * be used as the instance handle.
62  */
63 typedef struct {
64  tmpl_t *start_attr; //!< &control.${.:instance}-Start
65  tmpl_t *end_attr; //!< &control.${.:instance}-End
66 
67  tmpl_t *counter_attr; //!< Daily-Session-Time.
68  tmpl_t *limit_attr; //!< Max-Daily-Session.
69  tmpl_t *key; //!< User-Name
70 
71  char const *sql_name; //!< Instance of SQL module to use, usually just 'sql'.
72  char const *query; //!< SQL query to retrieve current session time.
73  char const *reset; //!< Daily, weekly, monthly, never or user defined.
74  bool auto_extend; //!< If the remaining allowance is sufficient to reach the next
75  ///< period allow for that in setting the reply attribute.
76  bool utc; //!< Use UTC time.
77 
81 
82 static const conf_parser_t module_config[] = {
83  { FR_CONF_OFFSET_FLAGS("sql_module_instance", CONF_FLAG_REQUIRED, rlm_sqlcounter_t, sql_name) },
84 
85 
88  { FR_CONF_OFFSET_FLAGS("auto_extend", CONF_FLAG_OK_MISSING, rlm_sqlcounter_t, auto_extend) },
90 
91  { FR_CONF_OFFSET_FLAGS("key", CONF_FLAG_NOT_EMPTY, rlm_sqlcounter_t, key), .dflt = "%{%{Stripped-User-Name} || %{User-Name}}", .quote = T_DOUBLE_QUOTED_STRING },
92 
93  { FR_CONF_OFFSET_FLAGS("reset_period_start_name", CONF_FLAG_ATTRIBUTE, rlm_sqlcounter_t, start_attr),
94  .dflt = "&control.${.:instance}-Reset-Start", .quote = T_BARE_WORD },
95  { FR_CONF_OFFSET_FLAGS("reset_period_end_name", CONF_FLAG_ATTRIBUTE, rlm_sqlcounter_t, end_attr),
96  .dflt = "&control.${.:instance}-Reset-End", .quote = T_BARE_WORD },
97 
98  /* Attribute to write counter value to*/
101 
103 };
104 
105 typedef struct {
106  xlat_exp_head_t *query_xlat; //!< Tokenized xlat to run query.
107  tmpl_t *reply_attr; //!< Attribute to write timeout to.
108  tmpl_t *reply_msg_attr; //!< Attribute to write reply message to.
110 
112 
115  { .out = &dict_freeradius, .proto = "freeradius" },
116  { NULL }
117 };
118 
120 {
121  int ret = 0;
122  size_t len;
123  unsigned int num = 1;
124  char last = '\0';
125  struct tm *tm, s_tm;
126  time_t time_s = fr_time_to_sec(now);
127 
128  if (inst->utc) {
129  tm = gmtime_r(&time_s, &s_tm);
130  } else {
131  tm = localtime_r(&time_s, &s_tm);
132  }
133  tm->tm_sec = tm->tm_min = 0;
134 
135  fr_assert(inst->reset != NULL);
136 
137  if (isdigit((uint8_t) inst->reset[0])){
138  len = strlen(inst->reset);
139  if (len == 0)
140  return -1;
141  last = inst->reset[len - 1];
142  if (!isalpha((uint8_t) last))
143  last = 'd';
144  num = atoi(inst->reset);
145  DEBUG3("num=%d, last=%c",num,last);
146  }
147  if (strcmp(inst->reset, "hourly") == 0 || last == 'h') {
148  /*
149  * Round up to the next nearest hour.
150  */
151  tm->tm_hour += num;
152  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
153  } else if (strcmp(inst->reset, "daily") == 0 || last == 'd') {
154  /*
155  * Round up to the next nearest day.
156  */
157  tm->tm_hour = 0;
158  tm->tm_mday += num;
159  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
160  } else if (strcmp(inst->reset, "weekly") == 0 || last == 'w') {
161  /*
162  * Round up to the next nearest week.
163  */
164  tm->tm_hour = 0;
165  tm->tm_mday += (7 - tm->tm_wday) +(7*(num-1));
166  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
167  } else if (strcmp(inst->reset, "monthly") == 0 || last == 'm') {
168  tm->tm_hour = 0;
169  tm->tm_mday = 1;
170  tm->tm_mon += num;
171  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
172  } else if (strcmp(inst->reset, "never") == 0) {
173  inst->reset_time = fr_time_wrap(0);
174  } else {
175  return -1;
176  }
177 
178  DEBUG2("Current Time: %pV, Next reset %pV", fr_box_time(now), fr_box_time(inst->reset_time));
179 
180  return ret;
181 }
182 
183 
184 /* I don't believe that this routine handles Daylight Saving Time adjustments
185  properly. Any suggestions?
186 */
188 {
189  int ret = 0;
190  size_t len;
191  unsigned int num = 1;
192  char last = '\0';
193  struct tm *tm, s_tm;
194  time_t time_s = fr_time_to_sec(now);
195 
196  if (inst->utc) {
197  tm = gmtime_r(&time_s, &s_tm);
198  } else {
199  tm = localtime_r(&time_s, &s_tm);
200  }
201  tm->tm_sec = tm->tm_min = 0;
202 
203  fr_assert(inst->reset != NULL);
204 
205  if (isdigit((uint8_t) inst->reset[0])){
206  len = strlen(inst->reset);
207  if (len == 0)
208  return -1;
209  last = inst->reset[len - 1];
210  if (!isalpha((uint8_t) last))
211  last = 'd';
212  num = atoi(inst->reset);
213  DEBUG3("num=%d, last=%c", num, last);
214  }
215  if (strcmp(inst->reset, "hourly") == 0 || last == 'h') {
216  /*
217  * Round down to the prev nearest hour.
218  */
219  tm->tm_hour -= num - 1;
220  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
221  } else if (strcmp(inst->reset, "daily") == 0 || last == 'd') {
222  /*
223  * Round down to the prev nearest day.
224  */
225  tm->tm_hour = 0;
226  tm->tm_mday -= num - 1;
227  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
228  } else if (strcmp(inst->reset, "weekly") == 0 || last == 'w') {
229  /*
230  * Round down to the prev nearest week.
231  */
232  tm->tm_hour = 0;
233  tm->tm_mday -= tm->tm_wday +(7*(num-1));
234  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
235  } else if (strcmp(inst->reset, "monthly") == 0 || last == 'm') {
236  tm->tm_hour = 0;
237  tm->tm_mday = 1;
238  tm->tm_mon -= num - 1;
239  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
240  } else if (strcmp(inst->reset, "never") == 0) {
241  inst->reset_time = fr_time_wrap(0);
242  } else {
243  return -1;
244  }
245 
246  DEBUG2("Current Time: %pV, Prev reset %pV", fr_box_time(now), fr_box_time(inst->last_reset));
247 
248  return ret;
249 }
250 
251 typedef struct {
253  fr_value_box_list_t result;
258 
259 /** Handle the result of calling the SQL query to retrieve the `counter` value.
260  *
261  * Create / update the `counter` attribute in the control list
262  * If `counter` > `limit`, optionally populate a reply message and return RLM_MODULE_REJECT.
263  * Otherwise, optionally populate a reply attribute with the value of `limit` - `counter` and return RLM_MODULE_UPDATED.
264  * If no reply attribute is set, return RLM_MODULE_OK.
265  */
266 static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
267 {
268  sqlcounter_rctx_t *rctx = talloc_get_type_abort(uctx, sqlcounter_rctx_t);
269  rlm_sqlcounter_t *inst = rctx->inst;
270  sqlcounter_call_env_t *env = rctx->env;
271  fr_value_box_t *sql_result = fr_value_box_list_pop_head(&rctx->result);
272  uint64_t counter, res;
273  fr_pair_t *vp, *limit = rctx->limit;
274  int ret;
275  char msg[128];
276 
277  if (!sql_result || (sscanf(sql_result->vb_strvalue, "%" PRIu64, &counter) != 1)) {
278  RDEBUG2("No integer found in result string \"%pV\". May be first session, setting counter to 0",
279  sql_result);
280  counter = 0;
281  }
282 
283  /*
284  * Add the counter to the control list
285  */
286  MEM(pair_update_control(&vp, tmpl_attr_tail_da(inst->counter_attr)) >= 0);
287  vp->vp_uint64 = counter;
288 
289  /*
290  * Check if check item > counter
291  */
292  if (limit->vp_uint64 <= counter) {
293  if (env->reply_msg_attr) {
294  /* User is denied access, send back a reply message */
295  snprintf(msg, sizeof(msg), "Your maximum %s usage has been reached", inst->reset);
296 
298  fr_pair_value_strdup(vp, msg, false);
299  }
300 
301  REDEBUG2("Maximum %s usage reached", inst->reset);
302  REDEBUG2("Rejecting user, %s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")",
303  inst->limit_attr->name, limit->vp_uint64, counter);
304 
306  }
307 
308  res = limit->vp_uint64 - counter;
309  RDEBUG2("Allowing user, %s value (%" PRIu64 ") is greater than counter value (%" PRIu64 ")",
310  inst->limit_attr->name, limit->vp_uint64, counter);
311 
312  /*
313  * We are assuming that simultaneous-use=1. But
314  * even if that does not happen then our user
315  * could login at max for 2*max-usage-time Is
316  * that acceptable?
317  */
318  if (env->reply_attr) {
319  fr_value_box_t vb;
320 
321  /*
322  * If we are near a reset then add the next
323  * limit, so that the user will not need to login
324  * again. Do this only if auto_extend is set.
325  */
326  if (inst->auto_extend &&
327  fr_time_gt(inst->reset_time, fr_time_wrap(0)) &&
328  ((int64_t)res >= fr_time_delta_to_sec(fr_time_sub(inst->reset_time, request->packet->timestamp)))) {
329  fr_time_delta_t to_reset = fr_time_sub(inst->reset_time, request->packet->timestamp);
330 
331  RDEBUG2("Time remaining (%pV) is greater than time to reset (%" PRIu64 "s). "
332  "Adding %pV to reply value",
333  fr_box_time_delta(to_reset), res, fr_box_time_delta(to_reset));
334  res = fr_time_delta_to_sec(to_reset) + limit->vp_uint64;
335  }
336 
337  fr_value_box_init(&vb, FR_TYPE_UINT64, NULL, false);
338  vb.vb_uint64 = res;
339 
340  /*
341  * Limit the reply attribute to the minimum of the existing value, or this new one.
342  */
343  ret = tmpl_find_or_add_vp(&vp, request, env->reply_attr);
344  switch (ret) {
345  case 1: /* new */
346  break;
347 
348  case 0: /* found */
349  {
350  fr_value_box_t existing;
351  fr_value_box_cast(NULL, &existing, FR_TYPE_UINT64, NULL, &vp->data);
352  if (fr_value_box_cmp(&vb, &existing) == 1) {
353  RDEBUG2("Leaving existing %s value of %pV" , env->reply_attr->name,
354  &vp->data);
356  }
357  }
358  break;
359 
360  case -1: /* alloc failed */
361  REDEBUG("Error allocating attribute %s", env->reply_attr->name);
363 
364  default: /* request or list unavailable */
365  RDEBUG2("List or request context not available for %s, skipping...", env->reply_attr->name);
367  }
368 
369  fr_value_box_cast(vp, &vp->data, vp->data.type, NULL, &vb);
370 
371  RDEBUG2("&%pP", vp);
372 
374  }
375 
377 }
378 
379 /** Check the value of a `counter` retrieved from an SQL query with a `limit`
380  *
381  * Module specific attributes containing the start / end times are created / updated,
382  * the query is tokenized as an xlat call to the relevant SQL module and then
383  * pushed on the stack for evaluation.
384  */
385 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
386 {
387  rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlcounter_t);
388  sqlcounter_call_env_t *env = talloc_get_type_abort(mctx->env_data, sqlcounter_call_env_t);
389  fr_pair_t *limit, *vp;
390  sqlcounter_rctx_t *rctx;
391 
392  /*
393  * Before doing anything else, see if we have to reset
394  * the counters.
395  */
396  if (fr_time_neq(inst->reset_time, fr_time_wrap(0)) &&
397  (fr_time_lteq(inst->reset_time, request->packet->timestamp))) {
398  /*
399  * Re-set the next time and prev_time for this counters range
400  */
401  inst->last_reset = inst->reset_time;
402  find_next_reset(inst, request->packet->timestamp);
403  }
404 
405  if (tmpl_find_vp(&limit, request, inst->limit_attr) < 0) {
406  RWDEBUG2("Couldn't find %s, doing nothing...", inst->limit_attr->name);
408  }
409 
410  /*
411  * Populate start and end attributes for use in query expansion
412  */
413  if (tmpl_find_or_add_vp(&vp, request, inst->start_attr) < 0) {
414  REDEBUG("Couldn't create %s", inst->start_attr->name);
416  }
417  vp->vp_uint64 = fr_time_to_sec(inst->last_reset);
418 
419  if (tmpl_find_or_add_vp(&vp, request, inst->end_attr) < 0) {
420  REDEBUG2("Couldn't create %s", inst->end_attr->name);
422  }
423  vp->vp_uint64 = fr_time_to_sec(inst->reset_time);
424 
425  MEM(rctx = talloc(unlang_interpret_frame_talloc_ctx(request), sqlcounter_rctx_t));
426  *rctx = (sqlcounter_rctx_t) {
427  .inst = inst,
428  .env = env,
429  .limit = limit
430  };
431 
432  if (unlang_function_push(request, NULL, mod_authorize_resume, NULL, 0, UNLANG_SUB_FRAME, rctx) < 0) {
433  error:
434  talloc_free(rctx);
436  }
437 
438  fr_value_box_list_init(&rctx->result);
439  if (unlang_xlat_push(rctx, &rctx->last_success, &rctx->result, request, env->query_xlat, UNLANG_SUB_FRAME) < 0) goto error;
440 
442 }
443 
444 /** Custom call_env parser to tokenize the SQL query xlat used for counter retrieval
445  */
446 static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
447  call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
448 {
450  CONF_PAIR const *to_parse = cf_item_to_pair(ci);
451  char *query;
452  xlat_exp_head_t *ex;
453 
454  query = talloc_asprintf(NULL, "%%%s(\"%s\")", inst->sql_name, cf_pair_value(to_parse));
455 
456  if (xlat_tokenize(ctx, &ex,
457  &FR_SBUFF_IN(query, talloc_array_length(query)),
458  &(fr_sbuff_parse_rules_t){
459  .escapes = &(fr_sbuff_unescape_rules_t) {
460  .name = "xlat",
461  .chr = '\\',
462  .subs = {
463  ['%'] = '%',
464  ['\\'] = '\\',
465  },
466  }}, t_rules, 0) < 0) {
467  talloc_free(query);
468  return -1;
469  }
470  talloc_free(query);
471 
472  if (xlat_needs_resolving(ex) &&
473  (xlat_resolve(ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
474  talloc_free(ex);
475  return -1;
476  }
477 
478  *(void**)out = ex;
479  return 0;
480 }
481 
484  .env = (call_env_parser_t[]){
486  .pair.func = call_env_query_parse },
490  }
491 };
492 
493 
494 /*
495  * Do any per-module initialization that is separate to each
496  * configured instance of the module. e.g. set up connections
497  * to external databases, read configuration files, set up
498  * dictionary entries, etc.
499  *
500  * If configuration information is given in the config section
501  * that must be referenced in later calls, store a handle to it
502  * in *instance otherwise put a null pointer there.
503  */
504 static int mod_instantiate(module_inst_ctx_t const *mctx)
505 {
506  rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlcounter_t);
507  CONF_SECTION *conf = mctx->mi->conf;
509  fr_assert(inst->query && *inst->query);
510 
511  sql_inst = module_rlm_static_by_name(NULL, inst->sql_name);
512  if (!sql_inst) {
513  cf_log_err(conf, "Module \"%s\" not found", inst->sql_name);
514  return -1;
515  }
516 
517  if (!talloc_get_type(sql_inst->data, rlm_sql_t)) {
518  cf_log_err(conf, "\"%s\" is not an instance of rlm_sql", inst->sql_name);
519  return -1;
520  }
521 
522  inst->reset_time = fr_time_wrap(0);
523 
524  if (find_next_reset(inst, fr_time()) == -1) {
525  cf_log_err(conf, "Invalid reset '%s'", inst->reset);
526  return -1;
527  }
528 
529  /*
530  * Discover the beginning of the current time period.
531  */
532  inst->last_reset = fr_time_wrap(0);
533 
534  if (find_prev_reset(inst, fr_time()) < 0) {
535  cf_log_err(conf, "Invalid reset '%s'", inst->reset);
536  return -1;
537  }
538 
539  return 0;
540 }
541 
542 static inline int attr_check(CONF_SECTION *conf, tmpl_t *tmpl, char const *name, fr_dict_attr_flags_t *flags)
543 {
546  tmpl_attr_tail_unresolved(tmpl), FR_TYPE_UINT64, flags) < 0) {
547  cf_log_perr(conf, "Failed defining %s attribute", name);
548  return -1;
549  }
550  } else if (tmpl_is_attr(tmpl)) {
551  if (tmpl_attr_tail_da(tmpl)->type != FR_TYPE_UINT64) {
552  cf_log_err(conf, "%s attribute %s must be uint64", name, tmpl_attr_tail_da(tmpl)->name);
553  return -1;
554  }
555  }
556 
557  return 0;
558 }
559 
560 static int mod_bootstrap(module_inst_ctx_t const *mctx)
561 {
562  rlm_sqlcounter_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlcounter_t);
563  CONF_SECTION *conf = mctx->mi->conf;
564  fr_dict_attr_flags_t flags = { .internal = 1, .length = 8, .name_only = 1 };
565 
566  if (unlikely(attr_check(conf, inst->start_attr, "reset_period_start", &flags) < 0)) return -1;
567  if (unlikely(attr_check(conf, inst->end_attr, "reset_period_end", &flags) < 0)) return -1;
568  if (unlikely(attr_check(conf, inst->counter_attr, "counter", &flags) < 0)) return -1;
569  if (unlikely(attr_check(conf, inst->limit_attr, "check", &flags) < 0)) return -1;
570 
571  return 0;
572 }
573 
574 /*
575  * The module name should be the only globally exported symbol.
576  * That is, everything else should be 'static'.
577  *
578  * If the module needs to temporarily modify it's instantiation
579  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
580  * The server will then take care of ensuring that the module
581  * is single-threaded.
582  */
585  .common = {
586  .magic = MODULE_MAGIC_INIT,
587  .name = "sqlcounter",
588  .inst_size = sizeof(rlm_sqlcounter_t),
590  .bootstrap = mod_bootstrap,
592  },
593  .method_group = {
594  .bindings = (module_method_binding_t[]){
595  { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_authorize, .method_env = &sqlcounter_call_env },
597  }
598  }
599 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition: action.h:39
log_entry msg
Definition: acutest.h:794
#define RCSID(id)
Definition: build.h:481
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define CALL_ENV_TERMINATOR
Definition: call_env.h:231
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition: call_env.h:235
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition: call_env.h:85
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:75
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition: call_env.h:224
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition: call_env.h:384
Per method call config.
Definition: call_env.h:175
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: cf_parse.h:405
@ CONF_FLAG_ATTRIBUTE
Value must resolve to attribute in dict (deprecated, use CONF_FLAG_TMPL).
Definition: cf_parse.h:407
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition: cf_parse.h:420
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition: cf_parse.h:416
@ CONF_FLAG_OK_MISSING
OK if it's missing.
Definition: cf_parse.h:427
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
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:101
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:664
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1594
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:289
#define cf_log_perr(_cf, _fmt,...)
Definition: cf_util.h:296
#define CF_IDENT_ANY
Definition: cf_util.h:78
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
int fr_dict_attr_add_name_only(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition: dict_util.c:1738
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:3263
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
Definition: dict.h:86
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:2400
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition: dict_util.c:4585
Values of the encryption flags.
Definition: merged_model.c:139
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1403
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define RWDEBUG2(fmt,...)
Definition: log.h:362
#define REDEBUG2(fmt,...)
Definition: log.h:372
talloc_free(reap)
@ FR_TYPE_UINT64
64 Bit unsigned integer.
Definition: merged_model.c:100
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
unsigned char uint8_t
Definition: merged_model.c:30
struct tm * gmtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:201
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:163
void * env_data
Per call environment data.
Definition: module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
module_instance_t * module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name)
Definition: module_rlm.c:828
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition: pair.c:2634
static const conf_parser_t config[]
Definition: base.c:183
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
static rs_t * conf
Definition: radsniff.c:53
#define RETURN_MODULE_REJECT
Definition: rcode.h:55
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_OK
Definition: rcode.h:57
#define RETURN_MODULE_UPDATED
Definition: rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static char const * name
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
Prototypes and functions for the SQL module.
static int attr_check(CONF_SECTION *conf, tmpl_t *tmpl, char const *name, fr_dict_attr_flags_t *flags)
bool auto_extend
If the remaining allowance is sufficient to reach the next period allow for that in setting the reply...
module_rlm_t rlm_sqlcounter
char const * sql_name
Instance of SQL module to use, usually just 'sql'.
char const * reset
Daily, weekly, monthly, never or user defined.
static fr_dict_t const * dict_freeradius
static int mod_bootstrap(module_inst_ctx_t const *mctx)
xlat_exp_head_t * query_xlat
Tokenized xlat to run query.
tmpl_t * start_attr
&control.${.:instance}-Start
tmpl_t * reply_msg_attr
Attribute to write reply message to.
static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Custom call_env parser to tokenize the SQL query xlat used for counter retrieval.
tmpl_t * counter_attr
Daily-Session-Time.
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Check the value of a counter retrieved from an SQL query with a limit
sqlcounter_call_env_t * env
char const * query
SQL query to retrieve current session time.
fr_value_box_list_t result
tmpl_t * limit_attr
Max-Daily-Session.
tmpl_t * end_attr
&control.${.:instance}-End
rlm_sqlcounter_t * inst
tmpl_t * reply_attr
Attribute to write timeout to.
static int find_next_reset(rlm_sqlcounter_t *inst, fr_time_t now)
static const call_env_method_t sqlcounter_call_env
bool utc
Use UTC time.
static const conf_parser_t module_config[]
static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Handle the result of calling the SQL query to retrieve the counter value.
fr_dict_autoload_t rlm_sqlcounter_dict[]
fr_time_t reset_time
static int mod_instantiate(module_inst_ctx_t const *mctx)
fr_time_t last_reset
static int find_prev_reset(rlm_sqlcounter_t *inst, fr_time_t now)
tmpl_t * key
User-Name.
#define FR_SBUFF_IN(_start, _len_or_end)
Set of parsing rules for *unescape_until functions.
Definition: merged_model.c:163
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
Module instance data.
Definition: module.h:265
Named methods exported by a module.
Definition: module.h:173
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition: pair.h:129
#define pair_update_control(_attr, _da)
Return or allocate a fr_pair_t in the control list.
Definition: pair.h:140
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition: tmpl_eval.c:889
#define tmpl_is_attr_unresolved(vpt)
Definition: tmpl.h:224
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
#define tmpl_is_attr(vpt)
Definition: tmpl.h:213
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition: tmpl.h:880
int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
Returns the first VP matching a tmpl_t, or if no VPs match, creates a new one.
Definition: tmpl_eval.c:918
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
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define talloc_get_type_abort_const
Definition: talloc.h:282
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition: time.h:731
#define fr_time_wrap(_time)
Definition: time.h:145
#define fr_time_lteq(_a, _b)
Definition: time.h:240
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:647
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
Definition: time.h:858
#define fr_time_gt(_a, _b)
Definition: time.h:237
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition: time.h:229
#define fr_time_neq(_a, _b)
Definition: time.h:242
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
"server local" time.
Definition: time.h:69
@ T_BARE_WORD
Definition: token.h:120
@ T_DOUBLE_QUOTED_STRING
Definition: token.h:121
int unlang_xlat_push(TALLOC_CTX *ctx, bool *p_success, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition: xlat.c:274
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t literals_safe_for)
Tokenize an xlat expansion.
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition: value.c:3352
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition: value.c:676
#define fr_box_time_delta(_val)
Definition: value.h:343
int nonnull(2, 5))
#define fr_box_time(_val)
Definition: value.h:326
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:587
static size_t char ** out
Definition: value.h:997