All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_sql.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: 4748ef6ccf12f9b13ea80124dabefe524a86aa19 $
19  * @file rlm_sql.c
20  * @brief Implements SQL 'users' file, and SQL accounting.
21  *
22  * @copyright 2012-2014 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23  * @copyright 2000,2006 The FreeRADIUS server project
24  * @copyright 2000 Mike Machado <mike@innercite.com>
25  * @copyright 2000 Alan DeKok <aland@ox.org>
26  */
27 RCSID("$Id: 4748ef6ccf12f9b13ea80124dabefe524a86aa19 $")
28 
29 #include <ctype.h>
30 
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/modules.h>
33 #include <freeradius-devel/map_proc.h>
34 #include <freeradius-devel/token.h>
35 #include <freeradius-devel/rad_assert.h>
36 #include <freeradius-devel/exfile.h>
37 
38 #include <sys/stat.h>
39 
40 #include "rlm_sql.h"
41 
42 /*
43  * So we can do pass2 xlat checks on the queries.
44  */
45 static const CONF_PARSER query_config[] = {
46  { FR_CONF_OFFSET("query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_MULTI, rlm_sql_config_t, accounting.query) },
48 };
49 
50 /*
51  * For now hard-code the subsections. This isn't perfect, but it
52  * helps the average case.
53  */
54 static const CONF_PARSER type_config[] = {
55  { FR_CONF_POINTER("accounting-on", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) query_config },
56  { FR_CONF_POINTER("accounting-off", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) query_config },
57  { FR_CONF_POINTER("start", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) query_config },
58  { FR_CONF_POINTER("interim-update", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) query_config },
59  { FR_CONF_POINTER("stop", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) query_config },
61 };
62 
63 static const CONF_PARSER acct_config[] = {
64  { FR_CONF_OFFSET("reference", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sql_config_t, accounting.reference), .dflt = ".query" },
65  { FR_CONF_OFFSET("logfile", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sql_config_t, accounting.logfile) },
66 
67  { FR_CONF_POINTER("type", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) type_config },
69 };
70 
71 static const CONF_PARSER postauth_config[] = {
72  { FR_CONF_OFFSET("reference", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sql_config_t, postauth.reference), .dflt = ".query" },
74 
75  { FR_CONF_OFFSET("query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_MULTI, rlm_sql_config_t, postauth.query) },
77 };
78 
79 static const CONF_PARSER module_config[] = {
80  { FR_CONF_OFFSET("driver", PW_TYPE_STRING, rlm_sql_config_t, sql_driver_name), .dflt = "rlm_sql_null" },
81  { FR_CONF_OFFSET("server", PW_TYPE_STRING, rlm_sql_config_t, sql_server), .dflt = "" }, /* Must be zero length so drivers can determine if it was set */
82  { FR_CONF_OFFSET("port", PW_TYPE_INTEGER, rlm_sql_config_t, sql_port), .dflt = "0" },
83  { FR_CONF_OFFSET("login", PW_TYPE_STRING, rlm_sql_config_t, sql_login), .dflt = "" },
84  { FR_CONF_OFFSET("password", PW_TYPE_STRING | PW_TYPE_SECRET, rlm_sql_config_t, sql_password), .dflt = "" },
85  { FR_CONF_OFFSET("radius_db", PW_TYPE_STRING, rlm_sql_config_t, sql_db), .dflt = "radius" },
86  { FR_CONF_OFFSET("read_groups", PW_TYPE_BOOLEAN, rlm_sql_config_t, read_groups), .dflt = "yes" },
87  { FR_CONF_OFFSET("read_profiles", PW_TYPE_BOOLEAN, rlm_sql_config_t, read_profiles), .dflt = "yes" },
88  { FR_CONF_OFFSET("read_clients", PW_TYPE_BOOLEAN, rlm_sql_config_t, do_clients), .dflt = "no" },
89  { FR_CONF_OFFSET("delete_stale_sessions", PW_TYPE_BOOLEAN, rlm_sql_config_t, delete_stale_sessions), .dflt = "yes" },
90  { FR_CONF_OFFSET("sql_user_name", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sql_config_t, query_user), .dflt = "" },
91  { FR_CONF_OFFSET("group_attribute", PW_TYPE_STRING, rlm_sql_config_t, group_attribute) },
92  { FR_CONF_OFFSET("logfile", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sql_config_t, logfile) },
93  { FR_CONF_OFFSET("default_user_profile", PW_TYPE_STRING, rlm_sql_config_t, default_profile), .dflt = "" },
94  { FR_CONF_OFFSET("client_query", PW_TYPE_STRING, rlm_sql_config_t, client_query), .dflt = "SELECT id,nasname,shortname,type,secret FROM nas" },
95  { FR_CONF_OFFSET("open_query", PW_TYPE_STRING, rlm_sql_config_t, connect_query) },
96 
97  { FR_CONF_OFFSET("authorize_check_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, authorize_check_query) },
98  { FR_CONF_OFFSET("authorize_reply_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, authorize_reply_query) },
99 
100  { FR_CONF_OFFSET("authorize_group_check_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, authorize_group_check_query) },
101  { FR_CONF_OFFSET("authorize_group_reply_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, authorize_group_reply_query) },
102  { FR_CONF_OFFSET("group_membership_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, groupmemb_query) },
103 #ifdef WITH_SESSION_MGMT
104  { FR_CONF_OFFSET("simul_count_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, simul_count_query) },
105  { FR_CONF_OFFSET("simul_verify_query", PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_NOT_EMPTY, rlm_sql_config_t, simul_verify_query) },
106 #endif
107  { FR_CONF_OFFSET("safe_characters", PW_TYPE_STRING, rlm_sql_config_t, allowed_chars), .dflt = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
108 
109  /*
110  * This only works for a few drivers.
111  */
112  { FR_CONF_OFFSET("query_timeout", PW_TYPE_INTEGER, rlm_sql_config_t, query_timeout) },
113 
114  { FR_CONF_POINTER("accounting", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) acct_config },
115 
116  { FR_CONF_POINTER("post-auth", PW_TYPE_SUBSECTION, NULL), .dflt = (void const *) postauth_config },
118 };
119 
120 /*
121  * Fall-Through checking function from rlm_files.c
122  */
124 {
125  VALUE_PAIR *tmp;
126  tmp = fr_pair_find_by_num(vp, 0, PW_FALL_THROUGH, TAG_ANY);
127 
128  return tmp ? tmp->vp_integer : FALL_THROUGH_DEFAULT;
129 }
130 
131 /*
132  * Yucky prototype.
133  */
134 static int generate_sql_clients(rlm_sql_t *inst);
135 static size_t sql_escape_func(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
136 
137 /** Execute an arbitrary SQL query
138  *
139  * For selects the first value of the first column will be returned,
140  * for inserts, updates and deletes the number of rows affected will be
141  * returned instead.
142  */
143 static ssize_t sql_xlat(char **out, UNUSED size_t outlen,
144  void const *mod_inst, UNUSED void const *xlat_inst,
145  REQUEST *request, char const *fmt)
146 {
147  rlm_sql_handle_t *handle = NULL;
148  rlm_sql_row_t row;
149  rlm_sql_t const *inst = mod_inst;
150  sql_rcode_t rcode;
151  ssize_t ret = 0;
152 
153  /*
154  * Add SQL-User-Name attribute just in case it is needed
155  * We could search the string fmt for SQL-User-Name to see if this is
156  * needed or not
157  */
158  sql_set_user(inst, request, NULL);
159 
160  handle = fr_connection_get(inst->pool); /* connection pool should produce error */
161  if (!handle) return 0;
162 
163  rlm_sql_query_log(inst, request, NULL, fmt);
164 
165  /*
166  * If the query starts with any of the following prefixes,
167  * then return the number of rows affected
168  */
169  if ((strncasecmp(fmt, "insert", 6) == 0) ||
170  (strncasecmp(fmt, "update", 6) == 0) ||
171  (strncasecmp(fmt, "delete", 6) == 0)) {
172  int numaffected;
173 
174  rcode = rlm_sql_query(inst, request, &handle, fmt);
175  if (rcode != RLM_SQL_OK) {
176  query_error:
177  RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, rcode, "<INVALID>"));
178 
179  ret = -1;
180  goto finish;
181  }
182 
183  numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
184  if (numaffected < 1) {
185  RDEBUG("SQL query affected no rows");
186 
187  goto finish;
188  }
189 
190  MEM(*out = talloc_asprintf(request, "%d", numaffected));
191  ret = talloc_array_length(*out) - 1;
192 
193  (inst->module->sql_finish_query)(handle, inst->config);
194 
195  goto finish;
196  } /* else it's a SELECT statement */
197 
198  rcode = rlm_sql_select_query(inst, request, &handle, fmt);
199  if (rcode != RLM_SQL_OK) goto query_error;
200 
201  rcode = rlm_sql_fetch_row(&row, inst, request, &handle);
202  if (rcode) {
203  (inst->module->sql_finish_select_query)(handle, inst->config);
204  goto query_error;
205  }
206 
207  if (!row) {
208  RDEBUG("SQL query returned no results");
209  (inst->module->sql_finish_select_query)(handle, inst->config);
210  ret = -1;
211 
212  goto finish;
213  }
214 
215  if (!row[0]){
216  RDEBUG("NULL value in first column of result");
217  (inst->module->sql_finish_select_query)(handle, inst->config);
218  ret = -1;
219 
220  goto finish;
221  }
222 
223  *out = talloc_bstrndup(request, row[0], strlen(row[0]));
224  ret = talloc_array_length(*out) - 1;
225 
226  (inst->module->sql_finish_select_query)(handle, inst->config);
227 
228 finish:
229  fr_connection_release(inst->pool, handle);
230 
231  return ret;
232 }
233 
234 /** Converts a string value into a #VALUE_PAIR
235  *
236  * @param[in,out] ctx to allocate #VALUE_PAIR (s).
237  * @param[out] out where to write the resulting #VALUE_PAIR.
238  * @param[in] request The current request.
239  * @param[in] map to process.
240  * @param[in] uctx The value to parse.
241  * @return
242  * - 0 on success.
243  * - -1 on failure.
244  */
245 static int _sql_map_proc_get_value(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
246 {
247  VALUE_PAIR *vp;
248  char const *value = uctx;
249 
250  vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
251  /*
252  * Buffer not always talloced, sometimes it's
253  * just a pointer to a field in a result struct.
254  */
255  if (fr_pair_value_from_str(vp, value, strlen(value)) < 0) {
256  char *escaped;
257 
258  escaped = fr_asprint(vp, value, talloc_array_length(value), '"');
259  REDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped,
260  map->lhs->tmpl_da->name, fr_strerror());
261  talloc_free(vp);
262 
263  return -1;
264  }
265 
266  vp->op = map->op;
267  *out = vp;
268 
269  return 0;
270 }
271 
272 /** Executes a SELECT query and maps the result to server attributes
273  *
274  * @param mod_inst #rlm_sql_t instance.
275  * @param proc_inst Instance data for this specific mod_proc call (unused).
276  * @param request The current request.
277  * @param query string to execute.
278  * @param maps Head of the map list.
279  * @return
280  * - #RLM_MODULE_NOOP no rows were returned or columns matched.
281  * - #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST.
282  * - #RLM_MODULE_FAIL if a fault occurred.
283  */
284 static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request,
285  char const *query, vp_map_t const *maps)
286 {
287  rlm_sql_t *inst = talloc_get_type_abort(mod_inst, rlm_sql_t);
288  rlm_sql_handle_t *handle = NULL;
289 
290  int i, j;
291 
293  sql_rcode_t ret;
294 
295  vp_map_t const *map;
296 
297  rlm_sql_row_t row;
298 
299  int rows;
300  int field_cnt;
301  char const **fields = NULL, *map_rhs;
302  char map_rhs_buff[128];
303 
304 #define MAX_SQL_FIELD_INDEX (64)
305 
306  int field_index[MAX_SQL_FIELD_INDEX];
307  bool found_field = false; /* Did we find any matching fields in the result set ? */
308 
309  rad_assert(inst->module->sql_fields); /* Should have been caught during validation... */
310 
311  for (i = 0; i < MAX_SQL_FIELD_INDEX; i++) field_index[i] = -1;
312 
313  /*
314  * Add SQL-User-Name attribute just in case it is needed
315  * We could search the string fmt for SQL-User-Name to see if this is
316  * needed or not
317  */
318  sql_set_user(inst, request, NULL);
319 
320  handle = fr_connection_get(inst->pool); /* connection pool should produce error */
321  if (!handle) return 0;
322 
323  rlm_sql_query_log(inst, request, NULL, query);
324 
325  ret = rlm_sql_select_query(inst, request, &handle, query);
326  if (ret != RLM_SQL_OK) {
327  RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, ret, "<INVALID>"));
328  rcode = RLM_MODULE_FAIL;
329  goto finish;
330  }
331 
332  ret = (inst->module->sql_fields)(&fields, handle, inst->config);
333  if (ret != RLM_SQL_OK) {
334  RERROR("Failed retrieving field names: %s", fr_int2str(sql_rcode_table, ret, "<INVALID>"));
335  error:
336  rcode = RLM_MODULE_FAIL;
337  (inst->module->sql_finish_select_query)(handle, inst->config);
338  goto finish;
339  }
340  rad_assert(fields);
341  field_cnt = talloc_array_length(fields);
342 
343  if (RDEBUG_ENABLED3) for (j = 0; j < field_cnt; j++) RDEBUG3("Got field: %s", fields[j]);
344 
345  /*
346  * Iterate over the maps, it's O(N2)ish but probably
347  * faster than building a radix tree each time the
348  * map set is evaluated (map->rhs can be dynamic).
349  */
350  for (map = maps, i = 0;
351  map && (i < MAX_SQL_FIELD_INDEX);
352  map = map->next, i++) {
353  /*
354  * Expand the RHS to get the name of the SQL field
355  */
356  if (tmpl_expand(&map_rhs, map_rhs_buff, sizeof(map_rhs_buff),
357  request, map->rhs, NULL, NULL) < 0) {
358  RERROR("Failed getting field name: %s", fr_strerror());
359  goto error;
360  }
361 
362  for (j = 0; j < field_cnt; j++) {
363  if (strcmp(fields[j], map_rhs) != 0) continue;
364  field_index[i] = j;
365  found_field = true;
366  }
367  }
368 
369  /*
370  * Couldn't resolve any map RHS values to fields
371  * in the result set.
372  */
373  if (!found_field) {
374  RDEBUG("No fields matching map found in query result");
375  rcode = RLM_MODULE_NOOP;
376  (inst->module->sql_finish_select_query)(handle, inst->config);
377  goto finish;
378  }
379 
380  /*
381  * We've resolved all the maps to result indexes, now convert
382  * the values at those indexes into VALUE_PAIRs.
383  *
384  * Note: Not all SQL client libraries provide a row count,
385  * so we have to do the count here.
386  */
387  for (ret = rlm_sql_fetch_row(&row, inst, request, &handle), rows = 0;
388  (ret == RLM_SQL_OK) && row;
389  ret = rlm_sql_fetch_row(&row, inst, request, &handle), rows++) {
390  for (map = maps, j = 0;
391  map && (j < MAX_SQL_FIELD_INDEX);
392  map = map->next, j++) {
393  if (field_index[j] < 0) continue; /* We didn't find the map RHS in the field set */
394  if (map_to_request(request, map, _sql_map_proc_get_value, row[field_index[j]]) < 0) goto error;
395  }
396  }
397 
398  if (ret == RLM_SQL_ERROR) goto error;
399 
400  if (!rows) {
401  RDEBUG("SQL query returned no results");
402  rcode = RLM_MODULE_NOOP;
403  }
404 
405  (inst->module->sql_finish_select_query)(handle, inst->config);
406 
407 finish:
408  talloc_free(fields);
409  fr_connection_release(inst->pool, handle);
410 
411  return rcode;
412 }
413 
415 {
416  rlm_sql_handle_t *handle;
417  rlm_sql_row_t row;
418  unsigned int i = 0;
419  int ret = 0;
420  RADCLIENT *c;
421 
422  DEBUG("rlm_sql (%s): Processing generate_sql_clients",
423  inst->name);
424 
425  DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
426  inst->name, inst->config->client_query);
427 
428  handle = fr_connection_get(inst->pool);
429  if (!handle) return -1;
430 
431  if (rlm_sql_select_query(inst, NULL, &handle, inst->config->client_query) != RLM_SQL_OK) return -1;
432 
433  while ((rlm_sql_fetch_row(&row, inst, NULL, &handle) == 0) && (row = handle->row)) {
434  char *server = NULL;
435  i++;
436 
437  /*
438  * The return data for each row MUST be in the following order:
439  *
440  * 0. Row ID (currently unused)
441  * 1. Name (or IP address)
442  * 2. Shortname
443  * 3. Type
444  * 4. Secret
445  * 5. Virtual Server (optional)
446  */
447  if (!row[0]){
448  ERROR("rlm_sql (%s): No row id found on pass %d",inst->name,i);
449  continue;
450  }
451  if (!row[1]){
452  ERROR("rlm_sql (%s): No nasname found for row %s",inst->name,row[0]);
453  continue;
454  }
455  if (!row[2]){
456  ERROR("rlm_sql (%s): No short name found for row %s",inst->name,row[0]);
457  continue;
458  }
459  if (!row[4]){
460  ERROR("rlm_sql (%s): No secret found for row %s",inst->name,row[0]);
461  continue;
462  }
463 
464  if (((inst->module->sql_num_fields)(handle, inst->config) > 5) && (row[5] != NULL) && *row[5]) {
465  server = row[5];
466  }
467 
468  DEBUG("rlm_sql (%s): Adding client %s (%s) to %s clients list",
469  inst->name,
470  row[1], row[2], server ? server : "global");
471 
472  /* FIXME: We should really pass a proper ctx */
473  c = client_afrom_query(NULL,
474  row[1], /* identifier */
475  row[4], /* secret */
476  row[2], /* shortname */
477  row[3], /* type */
478  server, /* server */
479  false); /* require message authenticator */
480  if (!c) {
481  continue;
482  }
483 
484  if (!client_add(NULL, c)) {
485  WARN("Failed to add client, possible duplicate?");
486 
487  client_free(c);
488  ret = -1;
489  break;
490  }
491 
492  DEBUG("rlm_sql (%s): Client \"%s\" (%s) added", c->longname, c->shortname,
493  inst->name);
494  }
495 
496  (inst->module->sql_finish_select_query)(handle, inst->config);
497  fr_connection_release(inst->pool, handle);
498 
499  return ret;
500 }
501 
502 /** xlat escape function for drivers which do not provide their own
503  *
504  */
505 static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, void *arg)
506 {
507  rlm_sql_handle_t *handle = arg;
508  rlm_sql_t *inst = handle->inst;
509  size_t len = 0;
510 
511  while (in[0]) {
512  size_t utf8_len;
513 
514  /*
515  * Allow all multi-byte UTF8 characters.
516  */
517  utf8_len = fr_utf8_char((uint8_t const *) in, -1);
518  if (utf8_len > 1) {
519  if (outlen <= utf8_len) break;
520 
521  memcpy(out, in, utf8_len);
522  in += utf8_len;
523  out += utf8_len;
524 
525  outlen -= utf8_len;
526  len += utf8_len;
527  continue;
528  }
529 
530  /*
531  * Because we register our own escape function
532  * we're now responsible for escaping all special
533  * chars in an xlat expansion or attribute value.
534  */
535  switch (in[0]) {
536  case '\n':
537  if (outlen <= 2) break;
538  out[0] = '\\';
539  out[1] = 'n';
540 
541  in++;
542  out += 2;
543  outlen -= 2;
544  len += 2;
545  break;
546 
547  case '\r':
548  if (outlen <= 2) break;
549  out[0] = '\\';
550  out[1] = 'r';
551 
552  in++;
553  out += 2;
554  outlen -= 2;
555  len += 2;
556  break;
557 
558  case '\t':
559  if (outlen <= 2) break;
560  out[0] = '\\';
561  out[1] = 't';
562 
563  in++;
564  out += 2;
565  outlen -= 2;
566  len += 2;
567  break;
568  }
569 
570  /*
571  * Non-printable characters get replaced with their
572  * mime-encoded equivalents.
573  */
574  if ((in[0] < 32) ||
575  strchr(inst->config->allowed_chars, *in) == NULL) {
576  /*
577  * Only 3 or less bytes available.
578  */
579  if (outlen <= 3) {
580  break;
581  }
582 
583  snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
584  in++;
585  out += 3;
586  outlen -= 3;
587  len += 3;
588  continue;
589  }
590 
591  /*
592  * Only one byte left.
593  */
594  if (outlen <= 1) {
595  break;
596  }
597 
598  /*
599  * Allowed character.
600  */
601  *out = *in;
602  out++;
603  in++;
604  outlen--;
605  len++;
606  }
607  *out = '\0';
608  return len;
609 }
610 
611 /** Passed as the escape function to map_proc and sql xlat methods
612  *
613  * The variant reserves a connection for the escape functions to use, and releases it after
614  * escaping is complete.
615  */
616 static size_t sql_escape_for_xlat_func(REQUEST *request, char *out, size_t outlen, char const *in, void *arg)
617 {
618  size_t ret;
619  rlm_sql_t *inst = talloc_get_type_abort(arg, rlm_sql_t);
620  rlm_sql_handle_t *handle;
621 
622  handle = fr_connection_get(inst->pool);
623  if (!handle) {
624  out[0] = '\0';
625  return 0;
626  }
627  ret = inst->sql_escape_func(request, out, outlen, in, handle);
628  fr_connection_release(inst->pool, handle);
629 
630  return ret;
631 }
632 
633 /*
634  * Set the SQL user name.
635  *
636  * We don't call the escape function here. The resulting string
637  * will be escaped later in the queries xlat so we don't need to
638  * escape it twice. (it will make things wrong if we have an
639  * escape candidate character in the username)
640  */
641 int sql_set_user(rlm_sql_t const *inst, REQUEST *request, char const *username)
642 {
643  char *expanded = NULL;
644  VALUE_PAIR *vp = NULL;
645  char const *sqluser;
646  ssize_t len;
647 
648  rad_assert(request->packet != NULL);
649 
650  if (username != NULL) {
651  sqluser = username;
652  } else if (inst->config->query_user[0] != '\0') {
653  sqluser = inst->config->query_user;
654  } else {
655  return 0;
656  }
657 
658  len = radius_axlat(&expanded, request, sqluser, NULL, NULL);
659  if (len < 0) {
660  return -1;
661  }
662 
663  vp = fr_pair_afrom_da(request->packet, inst->sql_user);
664  if (!vp) {
665  talloc_free(expanded);
666  return -1;
667  }
668 
669  fr_pair_value_strsteal(vp, expanded);
670  RDEBUG2("SQL-User-Name set to '%s'", vp->vp_strvalue);
671  vp->op = T_OP_SET;
672  radius_pairmove(request, &request->packet->vps, vp, false); /* needs to be pair move else op is not respected */
673 
674  return 0;
675 }
676 
677 /*
678  * Do a set/unset user, so it's a bit clearer what's going on.
679  */
680 #define sql_unset_user(_i, _r) fr_pair_delete_by_num(&_r->packet->vps, _i->sql_user->vendor, _i->sql_user->attr, TAG_ANY)
681 
682 static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t **handle, REQUEST *request,
683  rlm_sql_grouplist_t **phead)
684 {
685  char *expanded = NULL;
686  int num_groups = 0;
687  rlm_sql_row_t row;
688  rlm_sql_grouplist_t *entry;
689  int ret;
690 
691  /* NOTE: sql_set_user should have been run before calling this function */
692 
693  entry = *phead = NULL;
694 
695  if (!inst->config->groupmemb_query || !*inst->config->groupmemb_query) return 0;
696  if (radius_axlat(&expanded, request, inst->config->groupmemb_query, inst->sql_escape_func, *handle) < 0) return -1;
697 
698  ret = rlm_sql_select_query(inst, request, handle, expanded);
699  talloc_free(expanded);
700  if (ret != RLM_SQL_OK) return -1;
701 
702  while (rlm_sql_fetch_row(&row, inst, request, handle) == 0) {
703  row = (*handle)->row;
704  if (!row)
705  break;
706 
707  if (!row[0]){
708  RDEBUG("row[0] returned NULL");
709  (inst->module->sql_finish_select_query)(*handle, inst->config);
710  talloc_free(entry);
711  return -1;
712  }
713 
714  if (!*phead) {
715  *phead = talloc_zero(*handle, rlm_sql_grouplist_t);
716  entry = *phead;
717  } else {
718  entry->next = talloc_zero(*phead, rlm_sql_grouplist_t);
719  entry = entry->next;
720  }
721  entry->next = NULL;
722  entry->name = talloc_typed_strdup(entry, row[0]);
723 
724  num_groups++;
725  }
726 
727  (inst->module->sql_finish_select_query)(*handle, inst->config);
728 
729  return num_groups;
730 }
731 
732 
733 /*
734  * sql groupcmp function. That way we can do group comparisons (in the users file for example)
735  * with the group memberships reciding in sql
736  * The group membership query should only return one element which is the username. The returned
737  * username will then be checked with the passed check string.
738  */
739 static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp,
740  VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs,
741  UNUSED VALUE_PAIR **reply_pairs) CC_HINT(nonnull (1, 2, 4));
742 
743 static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp,
744  VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs,
745  UNUSED VALUE_PAIR **reply_pairs)
746 {
747  rlm_sql_handle_t *handle;
748  rlm_sql_t *inst = instance;
749  rlm_sql_grouplist_t *head, *entry;
750 
751  /*
752  * No group queries, don't do group comparisons.
753  */
754  if (!inst->config->groupmemb_query) {
755  RWARN("Cannot do group comparison when group_membership_query is not set");
756  return 1;
757  }
758 
759  RDEBUG("sql_groupcmp");
760 
761  if (check->vp_length == 0){
762  RDEBUG("sql_groupcmp: Illegal group name");
763  return 1;
764  }
765 
766  /*
767  * Set, escape, and check the user attr here
768  */
769  if (sql_set_user(inst, request, NULL) < 0)
770  return 1;
771 
772  /*
773  * Get a socket for this lookup
774  */
775  handle = fr_connection_get(inst->pool);
776  if (!handle) {
777  return 1;
778  }
779 
780  /*
781  * Get the list of groups this user is a member of
782  */
783  if (sql_get_grouplist(inst, &handle, request, &head) < 0) {
784  REDEBUG("Error getting group membership");
785  fr_connection_release(inst->pool, handle);
786  return 1;
787  }
788 
789  for (entry = head; entry != NULL; entry = entry->next) {
790  if (strcmp(entry->name, check->vp_strvalue) == 0){
791  RDEBUG("sql_groupcmp finished: User is a member of group %s",
792  check->vp_strvalue);
793  talloc_free(head);
794  fr_connection_release(inst->pool, handle);
795  return 0;
796  }
797  }
798 
799  /* Free the grouplist */
800  talloc_free(head);
801  fr_connection_release(inst->pool, handle);
802 
803  RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue);
804 
805  return 1;
806 }
807 
809  sql_fall_through_t *do_fall_through)
810 {
812  VALUE_PAIR *check_tmp = NULL, *reply_tmp = NULL, *sql_group = NULL;
813  rlm_sql_grouplist_t *head = NULL, *entry = NULL;
814 
815  char *expanded = NULL;
816  int rows;
817 
818  rad_assert(request->packet != NULL);
819 
820  if (!inst->config->groupmemb_query) {
821  RWARN("Cannot do check groups when group_membership_query is not set");
822 
823  do_nothing:
824  *do_fall_through = FALL_THROUGH_DEFAULT;
825 
826  /*
827  * Didn't add group attributes or allocate
828  * memory, so don't do anything else.
829  */
830  return RLM_MODULE_NOTFOUND;
831  }
832 
833  /*
834  * Get the list of groups this user is a member of
835  */
836  rows = sql_get_grouplist(inst, handle, request, &head);
837  if (rows < 0) {
838  REDEBUG("Error retrieving group list");
839 
840  return RLM_MODULE_FAIL;
841  }
842  if (rows == 0) {
843  RDEBUG2("User not found in any groups");
844  goto do_nothing;
845  }
846  rad_assert(head);
847 
848  RDEBUG2("User found in the group table");
849 
850  /*
851  * Add the Sql-Group attribute to the request list so we know
852  * which group we're retrieving attributes for
853  */
854  sql_group = pair_make_request(inst->group_da->name, NULL, T_OP_EQ);
855  if (!sql_group) {
856  REDEBUG("Error creating %s attribute", inst->group_da->name);
857  rcode = RLM_MODULE_FAIL;
858  goto finish;
859  }
860 
861  entry = head;
862  do {
863  next:
864  rad_assert(entry != NULL);
865  fr_pair_value_strcpy(sql_group, entry->name);
866 
867  if (inst->config->authorize_group_check_query) {
868  vp_cursor_t cursor;
869  VALUE_PAIR *vp;
870 
871  /*
872  * Expand the group query
873  */
874  if (radius_axlat(&expanded, request, inst->config->authorize_group_check_query,
875  inst->sql_escape_func, *handle) < 0) {
876  REDEBUG("Error generating query");
877  rcode = RLM_MODULE_FAIL;
878  goto finish;
879  }
880 
881  rows = sql_getvpdata(request, inst, request, handle, &check_tmp, expanded);
882  TALLOC_FREE(expanded);
883  if (rows < 0) {
884  REDEBUG("Error retrieving check pairs for group %s", entry->name);
885  rcode = RLM_MODULE_FAIL;
886  goto finish;
887  }
888 
889  /*
890  * If we got check rows we need to process them before we decide to
891  * process the reply rows
892  */
893  if ((rows > 0) &&
894  (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0)) {
895  fr_pair_list_free(&check_tmp);
896  entry = entry->next;
897 
898  if (!entry) break;
899 
900  goto next; /* != continue */
901  }
902 
903  RDEBUG2("Group \"%s\": Conditional check items matched", entry->name);
904  rcode = RLM_MODULE_OK;
905 
906  RDEBUG2("Group \"%s\": Merging assignment check items", entry->name);
907  RINDENT();
908  for (vp = fr_cursor_init(&cursor, &check_tmp);
909  vp;
910  vp = fr_cursor_next(&cursor)) {
911  if (!fr_assignment_op[vp->op]) continue;
912 
913  rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
914  }
915  REXDENT();
916  radius_pairmove(request, &request->config, check_tmp, true);
917  check_tmp = NULL;
918  }
919 
920  if (inst->config->authorize_group_reply_query) {
921  /*
922  * Now get the reply pairs since the paircompare matched
923  */
924  if (radius_axlat(&expanded, request, inst->config->authorize_group_reply_query,
925  inst->sql_escape_func, *handle) < 0) {
926  REDEBUG("Error generating query");
927  rcode = RLM_MODULE_FAIL;
928  goto finish;
929  }
930 
931  rows = sql_getvpdata(request->reply, inst, request, handle, &reply_tmp, expanded);
932  TALLOC_FREE(expanded);
933  if (rows < 0) {
934  REDEBUG("Error retrieving reply pairs for group %s", entry->name);
935  rcode = RLM_MODULE_FAIL;
936  goto finish;
937  }
938  *do_fall_through = fall_through(reply_tmp);
939 
940  RDEBUG2("Group \"%s\": Merging reply items", entry->name);
941  rcode = RLM_MODULE_OK;
942 
943  rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp, NULL);
944 
945  radius_pairmove(request, &request->reply->vps, reply_tmp, true);
946  reply_tmp = NULL;
947  /*
948  * If there's no reply query configured, then we assume
949  * FALL_THROUGH_NO, which is the same as the users file if you
950  * had no reply attributes.
951  */
952  } else {
953  *do_fall_through = FALL_THROUGH_DEFAULT;
954  }
955 
956  entry = entry->next;
957  } while (entry != NULL && (*do_fall_through == FALL_THROUGH_YES));
958 
959 finish:
960  talloc_free(head);
961  fr_pair_delete_by_num(&request->packet->vps, 0, inst->group_da->attr, TAG_ANY);
962 
963  return rcode;
964 }
965 
966 
967 static int mod_detach(void *instance)
968 {
969  rlm_sql_t *inst = instance;
970 
971  if (inst->pool) fr_connection_pool_free(inst->pool);
972 
973  /*
974  * We need to explicitly free all children, so if the driver
975  * parented any memory off the instance, their destructors
976  * run before we unload the bytecode for them.
977  *
978  * If we don't do this, we get a SEGV deep inside the talloc code
979  * when it tries to call a destructor that no longer exists.
980  */
981  talloc_free_children(inst);
982 
983  /*
984  * Decrements the reference count. The driver object won't be unloaded
985  * until all instances of rlm_sql that use it have been destroyed.
986  */
987  if (inst->handle) dlclose(inst->handle);
988 
989  return 0;
990 }
991 
992 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
993 {
994  rlm_sql_t *inst = instance;
995 
996  /*
997  * Hack...
998  */
999  inst->config = &inst->myconfig;
1000  inst->cs = conf;
1001 
1002  inst->name = cf_section_name2(conf);
1003  if (!inst->name) inst->name = cf_section_name1(conf);
1004 
1005  /*
1006  * Load the appropriate driver for our database.
1007  *
1008  * We need this to check if the sql_fields callback is provided.
1009  */
1010  inst->handle = lt_dlopenext(inst->config->sql_driver_name);
1011  if (!inst->handle) {
1012  ERROR("Could not link driver %s: %s", inst->config->sql_driver_name, fr_strerror());
1013  ERROR("Make sure it (and all its dependent libraries!) are in the search path of your system's ld");
1014  return -1;
1015  }
1016 
1017  inst->module = (rlm_sql_module_t *) dlsym(inst->handle, inst->config->sql_driver_name);
1018  if (!inst->module) {
1019  ERROR("Could not link symbol %s: %s", inst->config->sql_driver_name, dlerror());
1020  return -1;
1021  }
1022 
1023  INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->name,
1024  inst->config->sql_driver_name, inst->module->name);
1025 
1026  if (inst->config->groupmemb_query) {
1027  char buffer[256];
1028 
1029  char const *group_attribute;
1030 
1031  if (inst->config->group_attribute) {
1032  group_attribute = inst->config->group_attribute;
1033  } else if (cf_section_name2(conf)) {
1034  snprintf(buffer, sizeof(buffer), "%s-SQL-Group", inst->name);
1035  group_attribute = buffer;
1036  } else {
1037  group_attribute = "SQL-Group";
1038  }
1039 
1040  /*
1041  * Checks if attribute already exists.
1042  */
1043  if (paircompare_register_byname(group_attribute, fr_dict_attr_by_num(NULL, 0, PW_USER_NAME),
1044  false, sql_groupcmp, inst) < 0) {
1045  ERROR("Failed registering group comparison: %s", fr_strerror());
1046  return -1;
1047  }
1048 
1049  inst->group_da = fr_dict_attr_by_name(NULL, group_attribute);
1050  if (!inst->group_da) {
1051  ERROR("Failed resolving group attribute \"%s\"", group_attribute);
1052  return -1;
1053  }
1054  }
1055 
1056  /*
1057  * Register the SQL xlat function
1058  */
1059  xlat_register(inst, inst->name, sql_xlat, sql_escape_for_xlat_func, NULL, 0, 0);
1060 
1061  /*
1062  * Register the SQL map processor function
1063  */
1064  if (inst->module->sql_fields) map_proc_register(inst, inst->name, mod_map_proc, sql_escape_for_xlat_func, NULL, 0);
1065 
1066  return 0;
1067 }
1068 
1069 
1070 static int mod_instantiate(CONF_SECTION *conf, void *instance)
1071 {
1072  rlm_sql_t *inst = instance;
1073 
1074  /*
1075  * Sanity check for crazy people.
1076  */
1077  if (strncmp(inst->config->sql_driver_name, "rlm_sql_", 8) != 0) {
1078  ERROR("rlm_sql (%s): \"%s\" is NOT an SQL driver!", inst->name, inst->config->sql_driver_name);
1079  return -1;
1080  }
1081 
1082  /*
1083  * We need authorize_group_check_query or authorize_group_reply_query
1084  * if group_membership_query is set.
1085  *
1086  * Or we need group_membership_query if authorize_group_check_query or
1087  * authorize_group_reply_query is set.
1088  */
1089  if (!inst->config->groupmemb_query) {
1090  if (inst->config->authorize_group_check_query) {
1091  WARN("rlm_sql (%s): Ignoring authorize_group_reply_query as group_membership_query "
1092  "is not configured", inst->name);
1093  }
1094 
1095  if (inst->config->authorize_group_reply_query) {
1096  WARN("rlm_sql (%s): Ignoring authorize_group_check_query as group_membership_query "
1097  "is not configured", inst->name);
1098  }
1099 
1100  if (!inst->config->read_groups) {
1101  WARN("rlm_sql (%s): Ignoring read_groups as group_membership_query "
1102  "is not configured", inst->name);
1103  inst->config->read_groups = false;
1104  }
1105  } /* allow the group check / reply queries to be NULL */
1106 
1107  /*
1108  * This will always exist, as cf_section_parse_init()
1109  * will create it if it doesn't exist. However, the
1110  * "reference" config item won't exist in an auto-created
1111  * configuration. So if that doesn't exist, we ignore
1112  * the whole subsection.
1113  */
1114  inst->config->accounting.cs = cf_section_sub_find(conf, "accounting");
1115  inst->config->accounting.reference_cp = (cf_pair_find(inst->config->accounting.cs, "reference") != NULL);
1116 
1117  inst->config->postauth.cs = cf_section_sub_find(conf, "post-auth");
1118  inst->config->postauth.reference_cp = (cf_pair_find(inst->config->postauth.cs, "reference") != NULL);
1119 
1120  /*
1121  * Cache the SQL-User-Name fr_dict_attr_t, so we can be slightly
1122  * more efficient about creating SQL-User-Name attributes.
1123  */
1124  inst->sql_user = fr_dict_attr_by_name(NULL, "SQL-User-Name");
1125  if (!inst->sql_user) {
1126  return -1;
1127  }
1128 
1129  /*
1130  * Export these methods, too. This avoids RTDL_GLOBAL.
1131  */
1132  inst->sql_set_user = sql_set_user;
1133  inst->sql_query = rlm_sql_query;
1136 
1137  if (inst->module->mod_instantiate) {
1138  CONF_SECTION *cs;
1139  char const *name;
1140 
1141  name = strrchr(inst->config->sql_driver_name, '_');
1142  if (!name) {
1143  name = inst->config->sql_driver_name;
1144  } else {
1145  name++;
1146  }
1147 
1148  cs = cf_section_sub_find(conf, name);
1149  if (!cs) {
1150  cs = cf_section_alloc(conf, name, NULL);
1151  if (!cs) {
1152  return -1;
1153  }
1154  }
1155 
1156  /*
1157  * It's up to the driver to register a destructor
1158  */
1159  if (inst->module->mod_instantiate(cs, inst->config) < 0) {
1160  return -1;
1161  }
1162  }
1163 
1164  /*
1165  * Either use the module specific escape function
1166  * or our default one.
1167  */
1168  inst->sql_escape_func = inst->module->sql_escape_func ?
1169  inst->module->sql_escape_func :
1171 
1172  inst->ef = exfile_init(inst, 64, 30, true);
1173  if (!inst->ef) {
1174  cf_log_err_cs(conf, "Failed creating log file context");
1175  return -1;
1176  }
1177 
1178  /*
1179  * Initialise the connection pool for this instance
1180  */
1181  INFO("rlm_sql (%s): Attempting to connect to database \"%s\"", inst->name, inst->config->sql_db);
1182 
1183  inst->pool = module_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, NULL);
1184  if (!inst->pool) return -1;
1185 
1186  if (inst->config->do_clients) {
1187  if (generate_sql_clients(inst) == -1){
1188  ERROR("Failed to load clients from SQL");
1189  return -1;
1190  }
1191  }
1192 
1193  return RLM_MODULE_OK;
1194 }
1195 
1196 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) CC_HINT(nonnull);
1197 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
1198 {
1199  rlm_rcode_t rcode = RLM_MODULE_NOOP;
1200 
1201  rlm_sql_t *inst = instance;
1202  rlm_sql_handle_t *handle;
1203 
1204  VALUE_PAIR *check_tmp = NULL;
1205  VALUE_PAIR *reply_tmp = NULL;
1206  VALUE_PAIR *user_profile = NULL;
1207 
1208  bool user_found = false;
1209 
1210  sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;
1211 
1212  int rows;
1213 
1214  char *expanded = NULL;
1215 
1216  rad_assert(request->packet != NULL);
1217  rad_assert(request->reply != NULL);
1218 
1219  if (!inst->config->authorize_check_query && !inst->config->authorize_reply_query &&
1220  !inst->config->read_groups && !inst->config->read_profiles) {
1221  RWDEBUG("No authorization checks configured, returning noop");
1222 
1223  return RLM_MODULE_NOOP;
1224  }
1225 
1226  /*
1227  * Set, escape, and check the user attr here
1228  */
1229  if (sql_set_user(inst, request, NULL) < 0) {
1230  return RLM_MODULE_FAIL;
1231  }
1232 
1233  /*
1234  * Reserve a socket
1235  *
1236  * After this point use goto error or goto release to cleanup socket temporary pairlists and
1237  * temporary attributes.
1238  */
1239  handle = fr_connection_get(inst->pool);
1240  if (!handle) {
1241  rcode = RLM_MODULE_FAIL;
1242  goto error;
1243  }
1244 
1245  /*
1246  * Query the check table to find any conditions associated with this user/realm/whatever...
1247  */
1248  if (inst->config->authorize_check_query) {
1249  vp_cursor_t cursor;
1250  VALUE_PAIR *vp;
1251 
1252  if (radius_axlat(&expanded, request, inst->config->authorize_check_query,
1253  inst->sql_escape_func, handle) < 0) {
1254  REDEBUG("Error generating query");
1255  rcode = RLM_MODULE_FAIL;
1256  goto error;
1257  }
1258 
1259  rows = sql_getvpdata(request, inst, request, &handle, &check_tmp, expanded);
1260  TALLOC_FREE(expanded);
1261  if (rows < 0) {
1262  REDEBUG("Error getting check attributes");
1263  rcode = RLM_MODULE_FAIL;
1264  goto error;
1265  }
1266 
1267  if (rows == 0) goto skipreply; /* Don't need to free VPs we don't have */
1268 
1269  /*
1270  * Only do this if *some* check pairs were returned
1271  */
1272  RDEBUG2("User found in radcheck table");
1273  user_found = true;
1274  if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) {
1275  fr_pair_list_free(&check_tmp);
1276  check_tmp = NULL;
1277  goto skipreply;
1278  }
1279 
1280  RDEBUG2("Conditional check items matched, merging assignment check items");
1281  RINDENT();
1282  for (vp = fr_cursor_init(&cursor, &check_tmp);
1283  vp;
1284  vp = fr_cursor_next(&cursor)) {
1285  if (!fr_assignment_op[vp->op]) continue;
1286 
1287  rdebug_pair(2, request, vp, NULL);
1288  }
1289  REXDENT();
1290  radius_pairmove(request, &request->config, check_tmp, true);
1291 
1292  rcode = RLM_MODULE_OK;
1293  check_tmp = NULL;
1294  }
1295 
1296  if (inst->config->authorize_reply_query) {
1297  /*
1298  * Now get the reply pairs since the paircompare matched
1299  */
1300  if (radius_axlat(&expanded, request, inst->config->authorize_reply_query,
1301  inst->sql_escape_func, handle) < 0) {
1302  REDEBUG("Error generating query");
1303  rcode = RLM_MODULE_FAIL;
1304  goto error;
1305  }
1306 
1307  rows = sql_getvpdata(request->reply, inst, request, &handle, &reply_tmp, expanded);
1308  TALLOC_FREE(expanded);
1309  if (rows < 0) {
1310  REDEBUG("SQL query error getting reply attributes");
1311  rcode = RLM_MODULE_FAIL;
1312  goto error;
1313  }
1314 
1315  if (rows == 0) goto skipreply;
1316 
1317  do_fall_through = fall_through(reply_tmp);
1318 
1319  RDEBUG2("User found in radreply table, merging reply items");
1320  user_found = true;
1321 
1322  rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp, NULL);
1323 
1324  radius_pairmove(request, &request->reply->vps, reply_tmp, true);
1325 
1326  rcode = RLM_MODULE_OK;
1327  reply_tmp = NULL;
1328  }
1329 
1330  /*
1331  * Neither group checks or profiles will work without
1332  * a group membership query.
1333  */
1334  if (!inst->config->groupmemb_query) goto release;
1335 
1336 skipreply:
1337  if ((do_fall_through == FALL_THROUGH_YES) ||
1338  (inst->config->read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1339  rlm_rcode_t ret;
1340 
1341  RDEBUG3("... falling-through to group processing");
1342  ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
1343  switch (ret) {
1344  /*
1345  * Nothing bad happened, continue...
1346  */
1347  case RLM_MODULE_UPDATED:
1348  rcode = RLM_MODULE_UPDATED;
1349  /* FALL-THROUGH */
1350  case RLM_MODULE_OK:
1351  if (rcode != RLM_MODULE_UPDATED) {
1352  rcode = RLM_MODULE_OK;
1353  }
1354  /* FALL-THROUGH */
1355  case RLM_MODULE_NOOP:
1356  user_found = true;
1357  break;
1358 
1359  case RLM_MODULE_NOTFOUND:
1360  break;
1361 
1362  default:
1363  rcode = ret;
1364  goto release;
1365  }
1366  }
1367 
1368  /*
1369  * Repeat the above process with the default profile or User-Profile
1370  */
1371  if ((do_fall_through == FALL_THROUGH_YES) ||
1372  (inst->config->read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
1373  rlm_rcode_t ret;
1374 
1375  /*
1376  * Check for a default_profile or for a User-Profile.
1377  */
1378  RDEBUG3("... falling-through to profile processing");
1379  user_profile = fr_pair_find_by_num(request->config, 0, PW_USER_PROFILE, TAG_ANY);
1380 
1381  char const *profile = user_profile ?
1382  user_profile->vp_strvalue :
1383  inst->config->default_profile;
1384 
1385  if (!profile || !*profile) {
1386  goto release;
1387  }
1388 
1389  RDEBUG2("Checking profile %s", profile);
1390 
1391  if (sql_set_user(inst, request, profile) < 0) {
1392  REDEBUG("Error setting profile");
1393  rcode = RLM_MODULE_FAIL;
1394  goto error;
1395  }
1396 
1397  ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
1398  switch (ret) {
1399  /*
1400  * Nothing bad happened, continue...
1401  */
1402  case RLM_MODULE_UPDATED:
1403  rcode = RLM_MODULE_UPDATED;
1404  /* FALL-THROUGH */
1405  case RLM_MODULE_OK:
1406  if (rcode != RLM_MODULE_UPDATED) {
1407  rcode = RLM_MODULE_OK;
1408  }
1409  /* FALL-THROUGH */
1410  case RLM_MODULE_NOOP:
1411  user_found = true;
1412  break;
1413 
1414  case RLM_MODULE_NOTFOUND:
1415  break;
1416 
1417  default:
1418  rcode = ret;
1419  goto release;
1420  }
1421  }
1422 
1423  /*
1424  * At this point the key (user) hasn't be found in the check table, the reply table
1425  * or the group mapping table, and there was no matching profile.
1426  */
1427 release:
1428  if (!user_found) {
1429  rcode = RLM_MODULE_NOTFOUND;
1430  }
1431 
1432  fr_connection_release(inst->pool, handle);
1433  sql_unset_user(inst, request);
1434 
1435  return rcode;
1436 
1437 error:
1438  fr_pair_list_free(&check_tmp);
1439  fr_pair_list_free(&reply_tmp);
1440  sql_unset_user(inst, request);
1441 
1442  fr_connection_release(inst->pool, handle);
1443 
1444  return rcode;
1445 }
1446 
1447 /*
1448  * Generic function for failing between a bunch of queries.
1449  *
1450  * Uses the same principle as rlm_linelog, expanding the 'reference' config
1451  * item using xlat to figure out what query it should execute.
1452  *
1453  * If the reference matches multiple config items, and a query fails or
1454  * doesn't update any rows, the next matching config item is used.
1455  *
1456  */
1457 static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section)
1458 {
1459  rlm_rcode_t rcode = RLM_MODULE_OK;
1460 
1461  rlm_sql_handle_t *handle = NULL;
1462  int sql_ret;
1463  int numaffected = 0;
1464 
1465  CONF_ITEM *item;
1466  CONF_PAIR *pair;
1467  char const *attr = NULL;
1468  char const *value;
1469 
1470  char path[MAX_STRING_LEN];
1471  char *p = path;
1472  char *expanded = NULL;
1473 
1474  rad_assert(section);
1475 
1476  if (section->reference[0] != '.') {
1477  *p++ = '.';
1478  }
1479 
1480  if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) {
1481  rcode = RLM_MODULE_FAIL;
1482 
1483  goto finish;
1484  }
1485 
1486  /*
1487  * If we can't find a matching config item we do
1488  * nothing so return RLM_MODULE_NOOP.
1489  */
1490  item = cf_reference_item(NULL, section->cs, path);
1491  if (!item) {
1492  RWDEBUG("No such configuration item %s", path);
1493  rcode = RLM_MODULE_NOOP;
1494 
1495  goto finish;
1496  }
1497  if (cf_item_is_section(item)){
1498  RWDEBUG("Sections are not supported as references");
1499  rcode = RLM_MODULE_NOOP;
1500 
1501  goto finish;
1502  }
1503 
1504  pair = cf_item_to_pair(item);
1505  attr = cf_pair_attr(pair);
1506 
1507  RDEBUG2("Using query template '%s'", attr);
1508 
1509  handle = fr_connection_get(inst->pool);
1510  if (!handle) {
1511  rcode = RLM_MODULE_FAIL;
1512 
1513  goto finish;
1514  }
1515 
1516  sql_set_user(inst, request, NULL);
1517 
1518  while (true) {
1519  value = cf_pair_value(pair);
1520  if (!value) {
1521  RDEBUG("Ignoring null query");
1522  rcode = RLM_MODULE_NOOP;
1523 
1524  goto finish;
1525  }
1526 
1527  if (radius_axlat(&expanded, request, value, inst->sql_escape_func, handle) < 0) {
1528  rcode = RLM_MODULE_FAIL;
1529 
1530  goto finish;
1531  }
1532 
1533  if (!*expanded) {
1534  RDEBUG("Ignoring null query");
1535  rcode = RLM_MODULE_NOOP;
1536  talloc_free(expanded);
1537 
1538  goto finish;
1539  }
1540 
1541  rlm_sql_query_log(inst, request, section, expanded);
1542 
1543  sql_ret = rlm_sql_query(inst, request, &handle, expanded);
1544  TALLOC_FREE(expanded);
1545  RDEBUG("SQL query returned: %s", fr_int2str(sql_rcode_table, sql_ret, "<INVALID>"));
1546 
1547  switch (sql_ret) {
1548  /*
1549  * Query was a success! Now we just need to check if it did anything.
1550  */
1551  case RLM_SQL_OK:
1552  break;
1553 
1554  /*
1555  * A general, unrecoverable server fault.
1556  */
1557  case RLM_SQL_ERROR:
1558  /*
1559  * If we get RLM_SQL_RECONNECT it means all connections in the pool
1560  * were exhausted, and we couldn't create a new connection,
1561  * so we do not need to call fr_connection_release.
1562  */
1563  case RLM_SQL_RECONNECT:
1564  rcode = RLM_MODULE_FAIL;
1565  goto finish;
1566 
1567  /*
1568  * Query was invalid, this is a terminal error, but we still need
1569  * to do cleanup, as the connection handle is still valid.
1570  */
1571  case RLM_SQL_QUERY_INVALID:
1572  rcode = RLM_MODULE_INVALID;
1573  goto finish;
1574 
1575  /*
1576  * Driver found an error (like a unique key constraint violation)
1577  * that hinted it might be a good idea to try an alternative query.
1578  */
1579  case RLM_SQL_ALT_QUERY:
1580  goto next;
1581  }
1582  rad_assert(handle);
1583 
1584  /*
1585  * We need to have updated something for the query to have been
1586  * counted as successful.
1587  */
1588  numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
1589  (inst->module->sql_finish_query)(handle, inst->config);
1590  RDEBUG("%i record(s) updated", numaffected);
1591 
1592  if (numaffected > 0) break; /* A query succeeded, were done! */
1593  next:
1594  /*
1595  * We assume all entries with the same name form a redundant
1596  * set of queries.
1597  */
1598  pair = cf_pair_find_next(section->cs, pair, attr);
1599 
1600  if (!pair) {
1601  RDEBUG("No additional queries configured");
1602  rcode = RLM_MODULE_NOOP;
1603 
1604  goto finish;
1605  }
1606 
1607  RDEBUG("Trying next query...");
1608  }
1609 
1610 
1611 finish:
1612  talloc_free(expanded);
1613  fr_connection_release(inst->pool, handle);
1614  sql_unset_user(inst, request);
1615 
1616  return rcode;
1617 }
1618 
1619 #ifdef WITH_ACCOUNTING
1620 
1621 /*
1622  * Accounting: Insert or update session data in our sql table
1623  */
1624 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) CC_HINT(nonnull);
1625 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
1626 {
1627  rlm_sql_t *inst = instance;
1628 
1629  if (inst->config->accounting.reference_cp) {
1630  return acct_redundant(inst, request, &inst->config->accounting);
1631  }
1632 
1633  return RLM_MODULE_NOOP;
1634 }
1635 
1636 #endif
1637 
1638 #ifdef WITH_SESSION_MGMT
1639 /*
1640  * See if a user is already logged in. Sets request->simul_count to the
1641  * current session count for this user.
1642  *
1643  * Check twice. If on the first pass the user exceeds his
1644  * max. number of logins, do a second pass and validate all
1645  * logins by querying the terminal server (using eg. SNMP).
1646  */
1647 static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) CC_HINT(nonnull);
1648 static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request)
1649 {
1650  rlm_rcode_t rcode = RLM_MODULE_OK;
1651  rlm_sql_handle_t *handle = NULL;
1652  rlm_sql_t *inst = instance;
1653  rlm_sql_row_t row;
1654  int check = 0;
1655  uint32_t ipno = 0;
1656  char const *call_num = NULL;
1657  VALUE_PAIR *vp;
1658  int ret;
1659  uint32_t nas_addr = 0;
1660  uint32_t nas_port = 0;
1661 
1662  char *expanded = NULL;
1663 
1664  /* If simul_count_query is not defined, we don't do any checking */
1665  if (!inst->config->simul_count_query) return RLM_MODULE_NOOP;
1666 
1667  if ((!request->username) || (request->username->vp_length == '\0')) {
1668  REDEBUG("Zero Length username not permitted");
1669 
1670  return RLM_MODULE_INVALID;
1671  }
1672 
1673  if (sql_set_user(inst, request, NULL) < 0) {
1674  return RLM_MODULE_FAIL;
1675  }
1676 
1677  /* initialize the sql socket */
1678  handle = fr_connection_get(inst->pool);
1679  if (!handle) {
1680  sql_unset_user(inst, request);
1681  return RLM_MODULE_FAIL;
1682  }
1683 
1684  if (radius_axlat(&expanded, request, inst->config->simul_count_query, inst->sql_escape_func, handle) < 0) {
1685  fr_connection_release(inst->pool, handle);
1686  sql_unset_user(inst, request);
1687  return RLM_MODULE_FAIL;
1688  }
1689 
1690  if (rlm_sql_select_query(inst, request, &handle, expanded) != RLM_SQL_OK) {
1691  rcode = RLM_MODULE_FAIL;
1692  goto finish;
1693  }
1694 
1695  ret = rlm_sql_fetch_row(&row, inst, request, &handle);
1696  if (ret != 0) {
1697  rcode = RLM_MODULE_FAIL;
1698  goto finish;
1699  }
1700  if (!row) {
1701  rcode = RLM_MODULE_FAIL;
1702  goto finish;
1703  }
1704 
1705  request->simul_count = atoi(row[0]);
1706 
1707  (inst->module->sql_finish_select_query)(handle, inst->config);
1708  TALLOC_FREE(expanded);
1709 
1710  if (request->simul_count < request->simul_max) {
1711  rcode = RLM_MODULE_OK;
1712  goto finish;
1713  }
1714 
1715  /*
1716  * Looks like too many sessions, so let's start verifying
1717  * them, unless told to rely on count query only.
1718  */
1719  if (!inst->config->simul_verify_query) {
1720  rcode = RLM_MODULE_OK;
1721 
1722  goto finish;
1723  }
1724 
1725  if (radius_axlat(&expanded, request, inst->config->simul_verify_query, inst->sql_escape_func, handle) < 0) {
1726  rcode = RLM_MODULE_FAIL;
1727 
1728  goto finish;
1729  }
1730 
1731  if (rlm_sql_select_query(inst, request, &handle, expanded) != RLM_SQL_OK) goto release;
1732 
1733  /*
1734  * Setup some stuff, like for MPP detection.
1735  */
1736  request->simul_count = 0;
1737 
1738  if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_FRAMED_IP_ADDRESS, TAG_ANY)) != NULL) {
1739  ipno = vp->vp_ipaddr;
1740  }
1741 
1742  if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_CALLING_STATION_ID, TAG_ANY)) != NULL) {
1743  call_num = vp->vp_strvalue;
1744  }
1745 
1746  while (rlm_sql_fetch_row(&row, inst, request, &handle) == 0) {
1747  row = handle->row;
1748  if (!row) {
1749  break;
1750  }
1751 
1752  if (!row[2]){
1753  RDEBUG("Cannot zap stale entry. No username present in entry");
1754  rcode = RLM_MODULE_FAIL;
1755 
1756  goto finish;
1757  }
1758 
1759  if (!row[1]){
1760  RDEBUG("Cannot zap stale entry. No session id in entry");
1761  rcode = RLM_MODULE_FAIL;
1762 
1763  goto finish;
1764  }
1765 
1766  if (row[3]) {
1767  nas_addr = inet_addr(row[3]);
1768  }
1769 
1770  if (row[4]) {
1771  nas_port = atoi(row[4]);
1772  }
1773 
1774  check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1775  if (check == 0) {
1776  /*
1777  * Stale record - zap it.
1778  */
1779  if (inst->config->delete_stale_sessions == true) {
1780  uint32_t framed_addr = 0;
1781  char proto = 0;
1782  int sess_time = 0;
1783 
1784  if (row[5])
1785  framed_addr = inet_addr(row[5]);
1786  if (row[7]){
1787  if (strcmp(row[7], "PPP") == 0)
1788  proto = 'P';
1789  else if (strcmp(row[7], "SLIP") == 0)
1790  proto = 'S';
1791  }
1792  if (row[8])
1793  sess_time = atoi(row[8]);
1794  session_zap(request, nas_addr, nas_port,
1795  row[2], row[1], framed_addr,
1796  proto, sess_time);
1797  }
1798  }
1799  else if (check == 1) {
1800  /*
1801  * User is still logged in.
1802  */
1803  ++request->simul_count;
1804 
1805  /*
1806  * Does it look like a MPP attempt?
1807  */
1808  if (row[5] && ipno && inet_addr(row[5]) == ipno) {
1809  request->simul_mpp = 2;
1810  } else if (row[6] && call_num && !strncmp(row[6],call_num,16)) {
1811  request->simul_mpp = 2;
1812  }
1813  } else {
1814  /*
1815  * Failed to check the terminal server for
1816  * duplicate logins: return an error.
1817  */
1818  REDEBUG("Failed to check the terminal server for user '%s'.", row[2]);
1819 
1820  rcode = RLM_MODULE_FAIL;
1821  goto finish;
1822  }
1823  }
1824 
1825 finish:
1826  (inst->module->sql_finish_select_query)(handle, inst->config);
1827 release:
1828  fr_connection_release(inst->pool, handle);
1829  talloc_free(expanded);
1830  sql_unset_user(inst, request);
1831 
1832  /*
1833  * The Auth module apparently looks at request->simul_count,
1834  * not the return value of this module when deciding to deny
1835  * a call for too many sessions.
1836  */
1837  return rcode;
1838 }
1839 #endif
1840 
1841 /*
1842  * Postauth: Write a record of the authentication attempt
1843  */
1844 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull);
1845 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
1846 {
1847  rlm_sql_t *inst = instance;
1848 
1849  if (inst->config->postauth.reference_cp) {
1850  return acct_redundant(inst, request, &inst->config->postauth);
1851  }
1852 
1853  return RLM_MODULE_NOOP;
1854 }
1855 
1856 /*
1857  * Execute postauth_query after authentication
1858  */
1859 
1860 
1861 /* globally exported name */
1862 extern module_t rlm_sql;
1863 module_t rlm_sql = {
1865  .name = "sql",
1866  .type = RLM_TYPE_THREAD_SAFE,
1867  .inst_size = sizeof(rlm_sql_t),
1868  .config = module_config,
1869  .bootstrap = mod_bootstrap,
1870  .instantiate = mod_instantiate,
1871  .detach = mod_detach,
1872  .methods = {
1874 #ifdef WITH_ACCOUNTING
1876 #endif
1877 #ifdef WITH_SESSION_MGMT
1879 #endif
1881  },
1882 };
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
Definition: pair.c:544
exfile_t * ef
Definition: rlm_sql.h:226
ssize_t ssize_t ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull(1
static size_t sql_escape_func(REQUEST *, char *out, size_t outlen, char const *in, void *arg)
rlm_sql_t * inst
The rlm_sql instance this connection belongs to.
Definition: rlm_sql.h:155
ssize_t tmpl_expand(char const **out, char *buff, size_t outlen, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a vp_tmpl_t to a string writing the result to a buffer.
Definition: tmpl.c:1479
void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat) CC_HINT(nonnull)
Definition: evaluate.c:774
const FR_NAME_NUMBER sql_rcode_table[]
Definition: sql.c:46
2nd highest priority debug messages (-xx | -X).
Definition: log.h:52
General connection/server error.
Definition: rlm_sql.h:46
char const * client_query
Query used to get FreeRADIUS client definitions.
Definition: rlm_sql.h:99
VALUE_PAIR * config
VALUE_PAIR (s) used to set per request parameters for modules and the server core at runtime...
Definition: radiusd.h:227
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:265
sql_rcode_t(* sql_fields)(char const **out[], rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:207
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
fr_dict_attr_t const * group_da
Group dictionary attribute.
Definition: rlm_sql.h:238
void sql_rcode_t sql_rcode_t rlm_sql_query(rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query) CC_HINT(nonnull(1
#define RERROR(fmt,...)
Definition: log.h:207
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
char const * authorize_group_check_query
Query used get check VPs for a group.
Definition: rlm_sql.h:104
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:239
int session_zap(REQUEST *request, uint32_t nasaddr, uint32_t nas_port, char const *user, char const *sessionid, uint32_t cliaddr, char proto, int session_time)
Definition: session.c:37
sql_acct_section_t postauth
Definition: rlm_sql.h:146
sql_rcode_t(* sql_finish_select_query)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:213
void rdebug_pair(log_lvl_t level, REQUEST *, VALUE_PAIR *, char const *)
Print a single valuepair to stderr or error log.
Definition: pair.c:739
Prototypes and functions for the SQL module.
The module is OK, continue.
Definition: radiusd.h:91
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
void sql_rcode_t sql_rcode_t int rlm_sql_fetch_row(rlm_sql_row_t *out, rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle)
Call the driver's sql_fetch_row function.
Definition: sql.c:249
Metadata exported by the module.
Definition: modules.h:134
char const * simul_count_query
Query used get number of active sessions for a user (basic simultaneous use check).
Definition: rlm_sql.h:106
char const * authorize_reply_query
Query used get reply VPs for a user.
Definition: rlm_sql.h:103
char const * name
Raw string used to create the template.
Definition: tmpl.h:190
lt_dlhandle lt_dlopenext(char const *name)
Definition: modules.c:151
#define RWARN(fmt,...)
Definition: log.h:206
#define MEM(x)
Definition: radiusd.h:396
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
7 methods index for postauth section.
Definition: modules.h:48
void sql_rcode_t rlm_sql_select_query(rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query) CC_HINT(nonnull(1
static int _sql_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_sql.c:245
#define INFO(fmt,...)
Definition: log.h:143
static char const * name
int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check, VALUE_PAIR **rep_list)
Compare two pair lists except for the password information.
Definition: pair.c:479
char const * logfile
Keep a log of all SQL queries executed Useful for batch insertion with the NULL drivers.
Definition: rlm_sql.h:121
static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t **handle, REQUEST *request, rlm_sql_grouplist_t **phead)
Definition: rlm_sql.c:682
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
static sql_fall_through_t fall_through(VALUE_PAIR *vp)
Definition: rlm_sql.c:123
#define CC_HINT(_x)
Definition: build.h:71
bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
Add a client to a RADCLIENT_LIST.
Definition: client.c:192
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:222
static size_t sql_escape_for_xlat_func(REQUEST *request, char *out, size_t outlen, char const *in, void *arg)
Passed as the escape function to map_proc and sql xlat methods.
Definition: rlm_sql.c:616
CONF_SECTION * cs
The CONF_SECTION representing the group of queries to process.
Definition: rlm_sql.h:71
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
sql_acct_section_t accounting
Definition: rlm_sql.h:147
char const * reference
Reference string, expanded to point to a group of queries.
Definition: rlm_sql.h:74
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
int sql_set_user(rlm_sql_t const *inst, REQUEST *request, char const *username)
Definition: rlm_sql.c:641
module_t rlm_sql
Definition: rlm_sql.c:1863
#define pair_make_request(_a, _b, _c)
Definition: radiusd.h:545
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
fr_dict_attr_t const * sql_user
Cached pointer to SQL-User-Name.
Definition: rlm_sql.h:224
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
int simul_max
Maximum number of concurrent sessions for this user.
Definition: radiusd.h:270
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
#define PW_TYPE_SECRET
Only print value if debug level >= 3.
Definition: conffile.h:202
#define inst
Definition: token.h:46
The module considers the request invalid.
Definition: radiusd.h:93
static expr_map_t map[]
Definition: rlm_expr.c:169
static const CONF_PARSER type_config[]
Definition: rlm_sql.c:54
bool do_clients
Read clients from SQL database.
Definition: rlm_sql.h:112
#define PW_TYPE_SUBSECTION
Definition: conffile.h:188
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
char const * simul_verify_query
Query to get active sessions for a user the result is fed to session_zap.
Definition: rlm_sql.h:108
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
CONF_PAIR * cf_pair_find(CONF_SECTION const *, char const *name)
Definition: conffile.c:3478
char const * cf_pair_value(CONF_PAIR const *pair)
Definition: conffile.c:3506
struct vp_map * next
The next valuepair map.
Definition: map.h:55
Key constraint violation.
Definition: rlm_sql.h:49
static char const * proto
Definition: radclient.c:63
sql_rcode_t(* sql_fetch_row)(rlm_sql_row_t *out, rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle)
Definition: rlm_sql.h:235
bool reference_cp
Definition: rlm_sql.h:76
struct sql_grouplist * next
Definition: rlm_sql.h:243
void fr_pair_value_strsteal(VALUE_PAIR *vp, char const *src)
Reparent an allocated char buffer to a VALUE_PAIR.
Definition: pair.c:1955
#define rad_assert(expr)
Definition: rad_assert.h:38
Stale connection, should reconnect.
Definition: rlm_sql.h:48
char const * name
Definition: rlm_sql.h:191
#define sql_unset_user(_i, _r)
Definition: rlm_sql.c:680
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) CC_HINT(nonnull)
Definition: rlm_sql.c:1197
CONF_SECTION * cs
Definition: rlm_sql.h:222
static int mod_detach(void *instance)
Definition: rlm_sql.c:967
static int mod_instantiate(CONF_SECTION *conf, void *instance)
Definition: rlm_sql.c:1070
fr_connection_pool_t * module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_connection_create_t c, fr_connection_alive_t a, char const *prefix)
Initialise a module specific connection pool.
Definition: modules.c:1759
char * name
Definition: rlm_sql.h:242
int(* sql_set_user)(rlm_sql_t const *inst, REQUEST *request, char const *username)
Definition: rlm_sql.h:231
char const * query_user
xlat expansion used to specify the user to use as the subject of queries.
Definition: rlm_sql.h:91
static int generate_sql_clients(rlm_sql_t *inst)
Definition: rlm_sql.c:414
char const * cf_pair_attr(CONF_PAIR const *pair)
Definition: conffile.c:3497
#define DEBUG(fmt,...)
Definition: log.h:175
int simul_count
The current number of sessions for this user.
Definition: radiusd.h:272
void fr_pair_value_strcpy(VALUE_PAIR *vp, char const *src)
Copy data into an "string" data type.
Definition: pair.c:2013
bool cf_item_is_section(CONF_ITEM const *item)
Definition: conffile.c:3923
sql_rcode_t
Definition: rlm_sql.h:44
bool read_groups
Read user groups by default.
Definition: rlm_sql.h:113
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Create a new memcached handle.
sql_rcode_t(* sql_select_query)(rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query)
Definition: rlm_sql.h:234
void rlm_sql_query_log(rlm_sql_t const *inst, REQUEST *request, sql_acct_section_t *section, char const *query) CC_HINT(nonnull(1
const bool fr_assignment_op[]
Definition: token.c:129
#define PW_TYPE_XLAT
string will be dynamically expanded.
Definition: conffile.h:207
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *item)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: conffile.c:181
4 methods index for checksimul section.
Definition: modules.h:45
unsigned int attr
Attribute number.
Definition: dict.h:79
struct sql_inst rlm_sql_t
Definition: rlm_sql.h:150
#define MAX_SQL_FIELD_INDEX
xlat_escape_t sql_escape_func
Definition: rlm_sql.h:232
3 methods index for accounting section.
Definition: modules.h:44
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
Success.
Definition: rlm_sql.h:47
RADIUS_PACKET * reply
Outgoing response.
Definition: radiusd.h:225
static ssize_t sql_xlat(char **out, UNUSED size_t outlen, void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt)
Execute an arbitrary SQL query.
Definition: rlm_sql.c:143
void void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt,...) CC_HINT(format(printf
int rad_check_ts(uint32_t nasaddr, uint32_t nas_port, char const *user, char const *sessionid)
Definition: session.c:126
xlat_escape_t sql_escape_func
Definition: rlm_sql.h:215
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *, CONF_PAIR const *, char const *name)
Find a pair with a name matching attr, after specified pair.
Definition: conffile.c:3673
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:272
A truth value.
Definition: radius.h:56
static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) CC_HINT(nonnull(1
Definition: rlm_sql.c:743
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
rlm_sql_config_t * config
Definition: rlm_sql.h:221
Definition: token.h:45
FR_TOKEN op
Operator to use when moving or inserting valuepair into a list.
Definition: pair.h:118
32 Bit unsigned integer.
Definition: radius.h:34
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull(1
int fr_pair_value_from_str(VALUE_PAIR *vp, char const *value, size_t len)
Convert string value to native attribute value.
Definition: pair.c:1840
static rs_t * conf
Definition: radsniff.c:46
static const CONF_PARSER module_config[]
Definition: rlm_sql.c:79
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
#define PW_TYPE_MULTI
CONF_PAIR can have multiple copies.
Definition: conffile.h:210
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
void rdebug_pair_list(log_lvl_t level, REQUEST *, VALUE_PAIR *, char const *)
Print a list of VALUE_PAIRs.
Definition: pair.c:757
void fr_pair_delete_by_num(VALUE_PAIR **head, unsigned int vendor, unsigned int attr, int8_t tag)
Delete matching pairs.
Definition: pair.c:797
char const * group_attribute
Name of the group attribute.
Definition: rlm_sql.h:94
static const CONF_PARSER query_config[]
Definition: rlm_sql.c:45
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:154
#define PW_TYPE_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition: conffile.h:211
Module succeeded without doing anything.
Definition: radiusd.h:96
int sql_getvpdata(TALLOC_CTX *ctx, rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle, VALUE_PAIR **pair, char const *query)
Definition: sql.c:504
Describes a host allowed to send packets to the server.
Definition: clients.h:35
#define RDEBUG2(fmt,...)
Definition: log.h:244
char name[1]
Attribute name.
Definition: dict.h:89
rlm_sql_config_t myconfig
Definition: rlm_sql.h:219
char * talloc_bstrndup(void const *t, char const *in, size_t inlen)
Binary safe strndup function.
Definition: missing.c:632
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) CC_HINT(nonnull)
Definition: rlm_sql.c:1648
#define TAG_ANY
Definition: pair.h:191
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
char const * allowed_chars
Chars which done need escaping..
Definition: rlm_sql.h:129
static int mod_bootstrap(CONF_SECTION *conf, void *instance)
Definition: rlm_sql.c:992
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
char const * default_profile
Default profile to use if no other profiles were configured.
Definition: rlm_sql.h:96
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
int paircompare_register_byname(char const *name, fr_dict_attr_t const *from, bool first_only, RAD_COMPARE_FUNC func, void *instance)
Register a function as compare function.
Definition: pair.c:351
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition: print.c:390
int strncasecmp(char *s1, char *s2, int n)
Definition: missing.c:43
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
Definition: connection.c:1291
static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, sql_fall_through_t *do_fall_through)
Definition: rlm_sql.c:808
Query syntax error.
Definition: rlm_sql.h:45
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
CONF_SECTION * cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2)
Allocate a CONF_SECTION.
Definition: conffile.c:626
bool delete_stale_sessions
Whether we should use session_zap to create a fake stop packet, to terminate any stale sessions...
Definition: rlm_sql.h:125
#define WARN(fmt,...)
Definition: log.h:144
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) CC_HINT(nonnull)
Definition: rlm_sql.c:1625
#define REDEBUG(fmt,...)
Definition: log.h:254
bool read_profiles
Read user profiles by default.
Definition: rlm_sql.h:117
char const * sql_db
Database to run queries against.
Definition: rlm_sql.h:89
sql_fall_through_t
Definition: rlm_sql.h:52
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
char const * name
Module instance name.
Definition: rlm_sql.h:237
char const * longname
Client identifier.
Definition: clients.h:40
void * handle
Definition: rlm_sql.h:228
sql_rcode_t(* sql_query)(rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query)
Definition: rlm_sql.h:233
VALUE_PAIR * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute.
Definition: pair.c:58
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
char const * sql_driver_name
SQL driver module name e.g. rlm_sql_sqlite.
Definition: rlm_sql.h:84
sql_rcode_t(* sql_finish_query)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:212
exfile_t * exfile_init(TALLOC_CTX *ctx, uint32_t entries, uint32_t idle, bool locking)
Initialize a way for multiple threads to log to one or more files.
Definition: exfile.c:100
int fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
Definition: print.c:34
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
Definition: connection.c:1305
#define MAX_STRING_LEN
Definition: libradius.h:120
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
String of printable characters.
Definition: radius.h:33
#define FR_CONF_POINTER(_n, _t, _p)
Definition: conffile.h:172
#define RWDEBUG(fmt,...)
Definition: log.h:251
1 methods index for authorize section.
Definition: modules.h:42
User not found.
Definition: radiusd.h:95
RADCLIENT * client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char const *secret, char const *shortname, char const *type, char const *server, bool require_ma) CC_HINT(nonnull(2
#define RCSID(id)
Definition: build.h:135
rlm_sql_module_t * module
Definition: rlm_sql.h:229
OK (pairs modified).
Definition: radiusd.h:97
char * talloc_typed_strdup(void const *t, char const *p)
Call talloc strdup, setting the type on the new chunk correctly.
Definition: missing.c:588
static const CONF_PARSER postauth_config[]
Definition: rlm_sql.c:71
Value pair map.
Definition: map.h:46
fr_connection_pool_t * pool
Definition: rlm_sql.h:220
sql_rcode_t(* mod_instantiate)(CONF_SECTION *conf, rlm_sql_config_t *config)
Definition: rlm_sql.h:194
int(* sql_num_fields)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:202
#define RDEBUG(fmt,...)
Definition: log.h:243
int(* sql_affected_rows)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:204
#define ERROR(fmt,...)
Definition: log.h:145
static const CONF_PARSER acct_config[]
Definition: rlm_sql.c:63
int simul_mpp
WEIRD: 1 is false, 2 is true.
Definition: radiusd.h:273
char const * authorize_check_query
Query used get check VPs for a user.
Definition: rlm_sql.h:102
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
char const * shortname
Client nickname.
Definition: clients.h:41
void fr_connection_pool_free(fr_connection_pool_t *pool)
Delete a connection pool.
Definition: connection.c:1226
static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section)
Definition: rlm_sql.c:1457
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull)
Definition: rlm_sql.c:1845
char const * groupmemb_query
Query to determine group membership.
Definition: rlm_sql.h:110
char const * authorize_group_reply_query
Query used get reply VPs for a group.
Definition: rlm_sql.h:105
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_t *dict, char const *attr)
Locate a fr_dict_attr_t by its name.
Definition: dict.c:3493
#define RDEBUG3(fmt,...)
Definition: log.h:245
static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request, char const *query, vp_map_t const *maps)
Executes a SELECT query and maps the result to server attributes.
Definition: rlm_sql.c:284
void client_free(RADCLIENT *client)
Definition: client.c:62
CONF_ITEM * cf_reference_item(CONF_SECTION const *parentcs, CONF_SECTION *outercs, char const *ptr)
Definition: conffile.c:906