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: 3e12bb5987453cca307760a7d9ac4dc5790eac17 $
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: 3e12bb5987453cca307760a7d9ac4dc5790eac17 $")
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/unlang/function.h>
34 
35 #include <ctype.h>
36 
37 /*
38  * Note: When your counter spans more than 1 period (ie 3 months
39  * or 2 weeks), this module probably does NOT do what you want! It
40  * calculates the range of dates to count across by first calculating
41  * the End of the Current period and then subtracting the number of
42  * periods you specify from that to determine the beginning of the
43  * range.
44  *
45  * For example, if you specify a 3 month counter and today is June 15th,
46  * the end of the current period is June 30. Subtracting 3 months from
47  * that gives April 1st. So, the counter will sum radacct entries from
48  * April 1st to June 30. Then, next month, it will sum entries from
49  * May 1st to July 31st.
50  *
51  * To fix this behavior, we need to add some way of storing the Next
52  * Reset Time.
53  */
54 
55 /*
56  * Define a structure for our module configuration.
57  *
58  * These variables do not need to be in a structure, but it's
59  * a lot cleaner to do so, and a pointer to the structure can
60  * be used as the instance handle.
61  */
62 typedef struct {
63  tmpl_t *start_attr; //!< &control.${.:instance}-Start
64  tmpl_t *end_attr; //!< &control.${.:instance}-End
65 
66  tmpl_t *counter_attr; //!< Daily-Session-Time.
67  tmpl_t *limit_attr; //!< Max-Daily-Session.
68  tmpl_t *key; //!< User-Name
69 
70  char const *sql_name; //!< Instance of SQL module to use, usually just 'sql'.
71  char const *query; //!< SQL query to retrieve current session time.
72  char const *reset; //!< Daily, weekly, monthly, never or user defined.
73  bool auto_extend; //!< If the remaining allowance is sufficient to reach the next
74  ///< period allow for that in setting the reply attribute.
75  bool utc; //!< Use UTC time.
76 
80 
81 static const conf_parser_t module_config[] = {
82  { FR_CONF_OFFSET_FLAGS("sql_module_instance", CONF_FLAG_REQUIRED, rlm_sqlcounter_t, sql_name) },
83 
84 
87  { FR_CONF_OFFSET_FLAGS("auto_extend", CONF_FLAG_OK_MISSING, rlm_sqlcounter_t, auto_extend) },
89 
90  { FR_CONF_OFFSET_FLAGS("key", CONF_FLAG_NOT_EMPTY, rlm_sqlcounter_t, key), .dflt = "%{%{Stripped-User-Name} || %{User-Name}}", .quote = T_DOUBLE_QUOTED_STRING },
91 
92  { FR_CONF_OFFSET_FLAGS("reset_period_start_name", CONF_FLAG_ATTRIBUTE, rlm_sqlcounter_t, start_attr),
93  .dflt = "&control.${.:instance}-Reset-Start", .quote = T_BARE_WORD },
94  { FR_CONF_OFFSET_FLAGS("reset_period_end_name", CONF_FLAG_ATTRIBUTE, rlm_sqlcounter_t, end_attr),
95  .dflt = "&control.${.:instance}-Reset-End", .quote = T_BARE_WORD },
96 
97  /* Attribute to write counter value to*/
100 
102 };
103 
104 typedef struct {
105  xlat_exp_head_t *query_xlat; //!< Tokenized xlat to run query.
106  tmpl_t *reply_attr; //!< Attribute to write timeout to.
107  tmpl_t *reply_msg_attr; //!< Attribute to write reply message to.
109 
111 
114  { .out = &dict_freeradius, .proto = "freeradius" },
115  { NULL }
116 };
117 
119 {
120  int ret = 0;
121  size_t len;
122  unsigned int num = 1;
123  char last = '\0';
124  struct tm *tm, s_tm;
125  time_t time_s = fr_time_to_sec(now);
126 
127  if (inst->utc) {
128  tm = gmtime_r(&time_s, &s_tm);
129  } else {
130  tm = localtime_r(&time_s, &s_tm);
131  }
132  tm->tm_sec = tm->tm_min = 0;
133 
134  fr_assert(inst->reset != NULL);
135 
136  if (isdigit((uint8_t) inst->reset[0])){
137  len = strlen(inst->reset);
138  if (len == 0)
139  return -1;
140  last = inst->reset[len - 1];
141  if (!isalpha((uint8_t) last))
142  last = 'd';
143  num = atoi(inst->reset);
144  DEBUG3("num=%d, last=%c",num,last);
145  }
146  if (strcmp(inst->reset, "hourly") == 0 || last == 'h') {
147  /*
148  * Round up to the next nearest hour.
149  */
150  tm->tm_hour += num;
151  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
152  } else if (strcmp(inst->reset, "daily") == 0 || last == 'd') {
153  /*
154  * Round up to the next nearest day.
155  */
156  tm->tm_hour = 0;
157  tm->tm_mday += num;
158  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
159  } else if (strcmp(inst->reset, "weekly") == 0 || last == 'w') {
160  /*
161  * Round up to the next nearest week.
162  */
163  tm->tm_hour = 0;
164  tm->tm_mday += (7 - tm->tm_wday) +(7*(num-1));
165  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
166  } else if (strcmp(inst->reset, "monthly") == 0 || last == 'm') {
167  tm->tm_hour = 0;
168  tm->tm_mday = 1;
169  tm->tm_mon += num;
170  inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
171  } else if (strcmp(inst->reset, "never") == 0) {
172  inst->reset_time = fr_time_wrap(0);
173  } else {
174  return -1;
175  }
176 
177  DEBUG2("Current Time: %pV, Next reset %pV", fr_box_time(now), fr_box_time(inst->reset_time));
178 
179  return ret;
180 }
181 
182 
183 /* I don't believe that this routine handles Daylight Saving Time adjustments
184  properly. Any suggestions?
185 */
187 {
188  int ret = 0;
189  size_t len;
190  unsigned int num = 1;
191  char last = '\0';
192  struct tm *tm, s_tm;
193  time_t time_s = fr_time_to_sec(now);
194 
195  if (inst->utc) {
196  tm = gmtime_r(&time_s, &s_tm);
197  } else {
198  tm = localtime_r(&time_s, &s_tm);
199  }
200  tm->tm_sec = tm->tm_min = 0;
201 
202  fr_assert(inst->reset != NULL);
203 
204  if (isdigit((uint8_t) inst->reset[0])){
205  len = strlen(inst->reset);
206  if (len == 0)
207  return -1;
208  last = inst->reset[len - 1];
209  if (!isalpha((uint8_t) last))
210  last = 'd';
211  num = atoi(inst->reset);
212  DEBUG3("num=%d, last=%c", num, last);
213  }
214  if (strcmp(inst->reset, "hourly") == 0 || last == 'h') {
215  /*
216  * Round down to the prev nearest hour.
217  */
218  tm->tm_hour -= num - 1;
219  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
220  } else if (strcmp(inst->reset, "daily") == 0 || last == 'd') {
221  /*
222  * Round down to the prev nearest day.
223  */
224  tm->tm_hour = 0;
225  tm->tm_mday -= num - 1;
226  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
227  } else if (strcmp(inst->reset, "weekly") == 0 || last == 'w') {
228  /*
229  * Round down to the prev nearest week.
230  */
231  tm->tm_hour = 0;
232  tm->tm_mday -= tm->tm_wday +(7*(num-1));
233  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
234  } else if (strcmp(inst->reset, "monthly") == 0 || last == 'm') {
235  tm->tm_hour = 0;
236  tm->tm_mday = 1;
237  tm->tm_mon -= num - 1;
238  inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
239  } else if (strcmp(inst->reset, "never") == 0) {
240  inst->reset_time = fr_time_wrap(0);
241  } else {
242  return -1;
243  }
244 
245  DEBUG2("Current Time: %pV, Prev reset %pV", fr_box_time(now), fr_box_time(inst->last_reset));
246 
247  return ret;
248 }
249 
250 typedef struct {
252  fr_value_box_list_t result;
257 
258 /** Handle the result of calling the SQL query to retrieve the `counter` value.
259  *
260  * Create / update the `counter` attribute in the control list
261  * If `counter` > `limit`, optionally populate a reply message and return RLM_MODULE_REJECT.
262  * Otherwise, optionally populate a reply attribute with the value of `limit` - `counter` and return RLM_MODULE_UPDATED.
263  * If no reply attribute is set, return RLM_MODULE_OK.
264  */
265 static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
266 {
267  sqlcounter_rctx_t *rctx = talloc_get_type_abort(uctx, sqlcounter_rctx_t);
268  rlm_sqlcounter_t *inst = rctx->inst;
269  sqlcounter_call_env_t *env = rctx->env;
270  fr_value_box_t *sql_result = fr_value_box_list_pop_head(&rctx->result);
271  uint64_t counter, res;
272  fr_pair_t *vp, *limit = rctx->limit;
273  int ret;
274  char msg[128];
275 
276  if (!sql_result || (sscanf(sql_result->vb_strvalue, "%" PRIu64, &counter) != 1)) {
277  RDEBUG2("No integer found in result string \"%pV\". May be first session, setting counter to 0",
278  sql_result);
279  counter = 0;
280  }
281 
282  /*
283  * Add the counter to the control list
284  */
285  MEM(pair_update_control(&vp, tmpl_attr_tail_da(inst->counter_attr)) >= 0);
286  vp->vp_uint64 = counter;
287 
288  /*
289  * Check if check item > counter
290  */
291  if (limit->vp_uint64 <= counter) {
292  if (env->reply_msg_attr) {
293  /* User is denied access, send back a reply message */
294  snprintf(msg, sizeof(msg), "Your maximum %s usage has been reached", inst->reset);
295 
297  fr_pair_value_strdup(vp, msg, false);
298  }
299 
300  REDEBUG2("Maximum %s usage reached", inst->reset);
301  REDEBUG2("Rejecting user, %s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")",
302  inst->limit_attr->name, limit->vp_uint64, counter);
303 
305  }
306 
307  res = limit->vp_uint64 - counter;
308  RDEBUG2("Allowing user, %s value (%" PRIu64 ") is greater than counter value (%" PRIu64 ")",
309  inst->limit_attr->name, limit->vp_uint64, counter);
310 
311  /*
312  * We are assuming that simultaneous-use=1. But
313  * even if that does not happen then our user
314  * could login at max for 2*max-usage-time Is
315  * that acceptable?
316  */
317  if (env->reply_attr) {
318  fr_value_box_t vb;
319 
320  /*
321  * If we are near a reset then add the next
322  * limit, so that the user will not need to login
323  * again. Do this only if auto_extend is set.
324  */
325  if (inst->auto_extend &&
326  fr_time_gt(inst->reset_time, fr_time_wrap(0)) &&
327  ((int64_t)res >= fr_time_delta_to_sec(fr_time_sub(inst->reset_time, request->packet->timestamp)))) {
328  fr_time_delta_t to_reset = fr_time_sub(inst->reset_time, request->packet->timestamp);
329 
330  RDEBUG2("Time remaining (%pV) is greater than time to reset (%" PRIu64 "s). "
331  "Adding %pV to reply value",
332  fr_box_time_delta(to_reset), res, fr_box_time_delta(to_reset));
333  res = fr_time_delta_to_sec(to_reset) + limit->vp_uint64;
334  }
335 
336  fr_value_box_init(&vb, FR_TYPE_UINT64, NULL, false);
337  vb.vb_uint64 = res;
338 
339  /*
340  * Limit the reply attribute to the minimum of the existing value, or this new one.
341  */
342  ret = tmpl_find_or_add_vp(&vp, request, env->reply_attr);
343  switch (ret) {
344  case 1: /* new */
345  break;
346 
347  case 0: /* found */
348  {
349  fr_value_box_t existing;
350  fr_value_box_cast(NULL, &existing, FR_TYPE_UINT64, NULL, &vp->data);
351  if (fr_value_box_cmp(&vb, &existing) == 1) {
352  RDEBUG2("Leaving existing %s value of %pV" , env->reply_attr->name,
353  &vp->data);
355  }
356  }
357  break;
358 
359  case -1: /* alloc failed */
360  REDEBUG("Error allocating attribute %s", env->reply_attr->name);
362 
363  default: /* request or list unavailable */
364  RDEBUG2("List or request context not available for %s, skipping...", env->reply_attr->name);
366  }
367 
368  fr_value_box_cast(vp, &vp->data, vp->data.type, NULL, &vb);
369 
370  RDEBUG2("&%pP", vp);
371 
373  }
374 
376 }
377 
378 /** Check the value of a `counter` retrieved from an SQL query with a `limit`
379  *
380  * Module specific attributes containing the start / end times are created / updated,
381  * the query is tokenized as an xlat call to the relevant SQL module and then
382  * pushed on the stack for evaluation.
383  */
384 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
385 {
386  rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlcounter_t);
387  sqlcounter_call_env_t *env = talloc_get_type_abort(mctx->env_data, sqlcounter_call_env_t);
388  fr_pair_t *limit, *vp;
389  sqlcounter_rctx_t *rctx;
390 
391  /*
392  * Before doing anything else, see if we have to reset
393  * the counters.
394  */
395  if (fr_time_neq(inst->reset_time, fr_time_wrap(0)) &&
396  (fr_time_lteq(inst->reset_time, request->packet->timestamp))) {
397  /*
398  * Re-set the next time and prev_time for this counters range
399  */
400  inst->last_reset = inst->reset_time;
401  find_next_reset(inst, request->packet->timestamp);
402  }
403 
404  if (tmpl_find_vp(&limit, request, inst->limit_attr) < 0) {
405  RWDEBUG2("Couldn't find %s, doing nothing...", inst->limit_attr->name);
407  }
408 
409  /*
410  * Populate start and end attributes for use in query expansion
411  */
412  if (tmpl_find_or_add_vp(&vp, request, inst->start_attr) < 0) {
413  REDEBUG("Couldn't create %s", inst->start_attr->name);
415  }
416  vp->vp_uint64 = fr_time_to_sec(inst->last_reset);
417 
418  if (tmpl_find_or_add_vp(&vp, request, inst->end_attr) < 0) {
419  REDEBUG2("Couldn't create %s", inst->end_attr->name);
421  }
422  vp->vp_uint64 = fr_time_to_sec(inst->reset_time);
423 
424  MEM(rctx = talloc(unlang_interpret_frame_talloc_ctx(request), sqlcounter_rctx_t));
425  *rctx = (sqlcounter_rctx_t) {
426  .inst = inst,
427  .env = env,
428  .limit = limit
429  };
430 
431  if (unlang_function_push(request, NULL, mod_authorize_resume, NULL, 0, UNLANG_SUB_FRAME, rctx) < 0) {
432  error:
433  talloc_free(rctx);
435  }
436 
437  fr_value_box_list_init(&rctx->result);
438  if (unlang_xlat_push(rctx, &rctx->last_success, &rctx->result, request, env->query_xlat, UNLANG_SUB_FRAME) < 0) goto error;
439 
441 }
442 
443 /*
444  * Do any per-module initialization that is separate to each
445  * configured instance of the module. e.g. set up connections
446  * to external databases, read configuration files, set up
447  * dictionary entries, etc.
448  *
449  * If configuration information is given in the config section
450  * that must be referenced in later calls, store a handle to it
451  * in *instance otherwise put a null pointer there.
452  */
453 static int mod_instantiate(module_inst_ctx_t const *mctx)
454 {
455  rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlcounter_t);
456  CONF_SECTION *conf = mctx->inst->conf;
457 
458  fr_assert(inst->query && *inst->query);
459 
460  inst->reset_time = fr_time_wrap(0);
461 
462  if (find_next_reset(inst, fr_time()) == -1) {
463  cf_log_err(conf, "Invalid reset '%s'", inst->reset);
464  return -1;
465  }
466 
467  /*
468  * Discover the beginning of the current time period.
469  */
470  inst->last_reset = fr_time_wrap(0);
471 
472  if (find_prev_reset(inst, fr_time()) < 0) {
473  cf_log_err(conf, "Invalid reset '%s'", inst->reset);
474  return -1;
475  }
476 
477  return 0;
478 }
479 
480 #define ATTR_CHECK(_tmpl, _name) if (tmpl_is_attr_unresolved(inst->_tmpl)) { \
481  if (fr_dict_attr_add(fr_dict_unconst(dict_freeradius), fr_dict_root(dict_freeradius), tmpl_attr_tail_unresolved(inst->_tmpl), -1, FR_TYPE_UINT64, &flags) < 0) { \
482  cf_log_perr(conf, "Failed defining %s attribute", _name); \
483  return -1; \
484  } \
485 } else if (tmpl_is_attr(inst->_tmpl)) { \
486  if (tmpl_attr_tail_da(inst->_tmpl)->type != FR_TYPE_UINT64) { \
487  cf_log_err(conf, "%s attribute %s must be uint64", _name, tmpl_attr_tail_da(inst->_tmpl)->name); \
488  return -1; \
489  } \
490 }
491 
492 static int mod_bootstrap(module_inst_ctx_t const *mctx)
493 {
494  rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlcounter_t);
495  CONF_SECTION *conf = mctx->inst->conf;
496  fr_dict_attr_flags_t flags = (fr_dict_attr_flags_t) { .internal = 1, .length = 8 };
498 
499  ATTR_CHECK(start_attr, "reset_period_start")
500  ATTR_CHECK(end_attr, "reset_period_end")
501  ATTR_CHECK(counter_attr, "counter")
502  ATTR_CHECK(limit_attr, "check")
503 
504  sql_inst = module_rlm_by_name(NULL, inst->sql_name);
505  if (!sql_inst) {
506  cf_log_err(conf, "Module \"%s\" not found", inst->sql_name);
507  return -1;
508  }
509 
510  if (!talloc_get_type(sql_inst->dl_inst->data, rlm_sql_t)) {
511  cf_log_err(conf, "\"%s\" is not an instance of rlm_sql", inst->sql_name);
512  return -1;
513  }
514 
515  return 0;
516 }
517 
518 /** Custom call_env parser to tokenize the SQL query xlat used for counter retrieval
519  */
520 static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, void const *data,
521  UNUSED call_env_parser_t const *rule)
522 {
524  CONF_PAIR const *to_parse = cf_item_to_pair(ci);
525  char *query;
526  xlat_exp_head_t *ex;
527 
528  query = talloc_asprintf(NULL, "%%%s(\"%s\")", inst->sql_name, cf_pair_value(to_parse));
529 
530  if (xlat_tokenize(ctx, &ex,
531  &FR_SBUFF_IN(query, talloc_array_length(query)),
532  &(fr_sbuff_parse_rules_t){
533  .escapes = &(fr_sbuff_unescape_rules_t) {
534  .name = "xlat",
535  .chr = '\\',
536  .subs = {
537  ['%'] = '%',
538  ['\\'] = '\\',
539  },
540  }}, t_rules, 0) < 0) {
541  talloc_free(query);
542  return -1;
543  }
544  talloc_free(query);
545 
546  if (xlat_needs_resolving(ex) &&
547  (xlat_resolve(ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
548  talloc_free(ex);
549  return -1;
550  }
551 
552  *(void**)out = ex;
553  return 0;
554 }
555 
558  .env = (call_env_parser_t[]){
560  .pair.func = call_env_query_parse },
564  }
565 };
566 
567 /*
568  * The module name should be the only globally exported symbol.
569  * That is, everything else should be 'static'.
570  *
571  * If the module needs to temporarily modify it's instantiation
572  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
573  * The server will then take care of ensuring that the module
574  * is single-threaded.
575  */
578  .common = {
579  .magic = MODULE_MAGIC_INIT,
580  .name = "sqlcounter",
581  .flags = MODULE_TYPE_THREAD_SAFE,
582  .inst_size = sizeof(rlm_sqlcounter_t),
584  .bootstrap = mod_bootstrap,
586  },
587  .method_names = (module_method_name_t[]){
588  { .name1 = CF_IDENT_ANY, .name2 = CF_IDENT_ANY, .method = mod_authorize,
589  .method_env = &sqlcounter_call_env },
591  }
592 };
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:444
#define UNUSED
Definition: build.h:313
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#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:216
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition: call_env.h:83
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:73
#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:365
Per method call config.
Definition: call_env.h:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#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:406
@ CONF_FLAG_ATTRIBUTE
Value must resolve to attribute in dict (deprecated, use CONF_FLAG_TMPL).
Definition: cf_parse.h:408
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition: cf_parse.h:421
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition: cf_parse.h:417
@ CONF_FLAG_OK_MISSING
OK if it's missing.
Definition: cf_parse.h:428
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
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
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_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define CF_IDENT_ANY
Definition: cf_util.h:78
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:263
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:262
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
#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:1384
#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
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
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:51
Specifies a module method identifier.
Definition: module_method.c:36
module_instance_t * module_rlm_by_name(module_instance_t const *parent, char const *asked_name)
Definition: module_rlm.c:792
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition: pair.c:2631
static const conf_parser_t config[]
Definition: base.c:188
#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 int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
Prototypes and functions for the SQL module.
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 call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, void const *data, UNUSED call_env_parser_t const *rule)
Custom call_env parser to tokenize the SQL query xlat used for counter retrieval.
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.
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
#define ATTR_CHECK(_tmpl, _name)
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
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: module.h:49
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
Per instance data.
Definition: module.h:169
#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:887
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
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:916
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_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:270
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:729
#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:645
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:856
#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:3301
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition: value.c:640
static fr_slen_t data
Definition: value.h:1259
#define fr_box_time_delta(_val)
Definition: value.h:336
int nonnull(2, 5))
#define fr_box_time(_val)
Definition: value.h:319
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:574
static size_t char ** out
Definition: value.h:984