All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_attr_filter.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: 0885e7a56bbfac0950a25e354794ad3429e9ab44 $
19  * @file rlm_attr_filter.c
20  * @brief Filter the contents of a list, allowing only certain attributes.
21  *
22  * @copyright (C) 2001,2006 The FreeRADIUS server project
23  * @copyright (C) 2001 Chris Parker <cparker@starnetusa.net>
24  */
25 RCSID("$Id: 0885e7a56bbfac0950a25e354794ad3429e9ab44 $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
30 
31 #include <sys/stat.h>
32 
33 #include <ctype.h>
34 #include <fcntl.h>
35 
36 /*
37  * Define a structure with the module configuration, so it can
38  * be used as the instance handle.
39  */
40 typedef struct rlm_attr_filter {
41  char const *filename;
43  bool relaxed;
46 
47 static const CONF_PARSER module_config[] = {
49  { FR_CONF_OFFSET("key", PW_TYPE_TMPL, rlm_attr_filter_t, key), .dflt = "&Realm", .quote = T_BARE_WORD },
50  { FR_CONF_OFFSET("relaxed", PW_TYPE_BOOLEAN, rlm_attr_filter_t, relaxed), .dflt = "no" },
52 };
53 
54 static void check_pair(REQUEST *request, VALUE_PAIR *check_item, VALUE_PAIR *reply_item, int *pass, int *fail)
55 {
56  int compare;
57 
58  if (check_item->op == T_OP_SET) return;
59 
60  compare = fr_pair_cmp(check_item, reply_item);
61  if (compare < 0) {
62  REDEBUG("Comparison failed: %s", fr_strerror());
63  }
64 
65  if (compare == 1) {
66  ++*(pass);
67  } else {
68  ++*(fail);
69  }
70 
71  if (RDEBUG_ENABLED3) {
72  char rule[1024], pair[1024];
73 
74  fr_pair_snprint(rule, sizeof(rule), check_item);
75  fr_pair_snprint(pair, sizeof(pair), reply_item);
76  RDEBUG3("%s %s %s", pair, compare == 1 ? "allowed by" : "disallowed by", rule);
77  }
78 
79  return;
80 }
81 
82 static int attr_filter_getfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list)
83 {
84  vp_cursor_t cursor;
85  int rcode;
86  PAIR_LIST *attrs = NULL;
87  PAIR_LIST *entry;
88  VALUE_PAIR *vp;
89 
90  rcode = pairlist_read(ctx, filename, &attrs, 1);
91  if (rcode < 0) {
92  return -1;
93  }
94 
95  /*
96  * Walk through the 'attrs' file list.
97  */
98 
99  entry = attrs;
100  while (entry) {
101  entry->check = entry->reply;
102  entry->reply = NULL;
103 
104  for (vp = fr_cursor_init(&cursor, &entry->check);
105  vp;
106  vp = fr_cursor_next(&cursor)) {
107  /*
108  * If it's NOT a vendor attribute,
109  * and it's NOT a wire protocol
110  * and we ignore Fall-Through,
111  * then bitch about it, giving a good warning message.
112  */
113  if ((vp->da->vendor == 0) &&
114  (vp->da->attr > 1000)) {
115  WARN("[%s]:%d Check item \"%s\"\n\tfound in filter list for realm \"%s\".\n",
116  filename, entry->lineno, vp->da->name, entry->name);
117  }
118  }
119 
120  entry = entry->next;
121  }
122 
123  *pair_list = attrs;
124  return 0;
125 }
126 
127 
128 /*
129  * (Re-)read the "attrs" file into memory.
130  */
131 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
132 {
133  rlm_attr_filter_t *inst = instance;
134  int rcode;
135 
136  rcode = attr_filter_getfile(inst, inst->filename, &inst->attrs);
137  if (rcode != 0) {
138  ERROR("Errors reading %s", inst->filename);
139 
140  return -1;
141  }
142 
143  return 0;
144 }
145 
146 
147 /*
148  * Common attr_filter checks
149  */
150 static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQUEST *request, RADIUS_PACKET *packet)
151 {
152  rlm_attr_filter_t *inst = instance;
153  VALUE_PAIR *vp;
154  vp_cursor_t input, check, out;
155  VALUE_PAIR *input_item, *check_item, *output;
156  PAIR_LIST *pl;
157  int found = 0;
158  int pass, fail = 0;
159  char const *keyname = NULL;
160  char buffer[256];
161  ssize_t slen;
162 
163  if (!packet) return RLM_MODULE_NOOP;
164 
165  slen = tmpl_expand(&keyname, buffer, sizeof(buffer), request, inst->key, NULL, NULL);
166  if (slen < 0) return RLM_MODULE_FAIL;
167  if ((keyname == buffer) && is_truncated((size_t)slen, sizeof(buffer))) {
168  REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen);
169  return RLM_MODULE_FAIL;
170  }
171 
172  /*
173  * Head of the output list
174  */
175  output = NULL;
176  fr_cursor_init(&out, &output);
177 
178  /*
179  * Find the attr_filter profile entry for the entry.
180  */
181  for (pl = inst->attrs; pl; pl = pl->next) {
182  int fall_through = 0;
183  int relax_filter = inst->relaxed;
184 
185  /*
186  * If the current entry is NOT a default,
187  * AND the realm does NOT match the current entry,
188  * then skip to the next entry.
189  */
190  if ((strcmp(pl->name, "DEFAULT") != 0) &&
191  (strcmp(keyname, pl->name) != 0)) {
192  continue;
193  }
194 
195  RDEBUG2("Matched entry %s at line %d", pl->name, pl->lineno);
196  found = 1;
197 
198  for (check_item = fr_cursor_init(&check, &pl->check);
199  check_item;
200  check_item = fr_cursor_next(&check)) {
201  if (!check_item->da->vendor &&
202  (check_item->da->attr == PW_FALL_THROUGH) &&
203  (check_item->vp_integer == 1)) {
204  fall_through = 1;
205  continue;
206  }
207  else if (!check_item->da->vendor && check_item->da->attr == PW_RELAX_FILTER) {
208  relax_filter = check_item->vp_integer;
209  continue;
210  }
211 
212  /*
213  * If it is a SET operator, add the attribute to
214  * the output list without checking it.
215  */
216  if (check_item->op == T_OP_SET ) {
217  vp = fr_pair_copy(packet, check_item);
218  if (!vp) {
219  goto error;
220  }
221  radius_xlat_do(request, vp);
222  fr_cursor_insert(&out, vp);
223  }
224  }
225 
226  /*
227  * Iterate through the input items, comparing
228  * each item to every rule, then moving it to the
229  * output list only if it matches all rules
230  * for that attribute. IE, Idle-Timeout is moved
231  * only if it matches all rules that describe an
232  * Idle-Timeout.
233  */
234  for (input_item = fr_cursor_init(&input, &packet->vps);
235  input_item;
236  input_item = fr_cursor_next(&input)) {
237  pass = fail = 0; /* reset the pass,fail vars for each reply item */
238 
239  /*
240  * Reset the check_item pointer to beginning of the list
241  */
242  for (check_item = fr_cursor_first(&check);
243  check_item;
244  check_item = fr_cursor_next(&check)) {
245  /*
246  * Vendor-Specific is special, and matches any VSA if the
247  * comparison is always true.
248  */
249  if ((check_item->da->attr == PW_VENDOR_SPECIFIC) && (input_item->da->vendor != 0) &&
250  (check_item->op == T_OP_CMP_TRUE)) {
251  pass++;
252  continue;
253  }
254 
255  if (input_item->da == check_item->da) {
256  check_pair(request, check_item, input_item, &pass, &fail);
257  }
258  }
259 
260  RDEBUG3("Attribute \"%s\" allowed by %i rules, disallowed by %i rules",
261  input_item->da->name, pass, fail);
262  /*
263  * Only move attribute if it passed all rules, or if the config says we
264  * should copy unmatched attributes ('relaxed' mode).
265  */
266  if (fail == 0 && (pass > 0 || relax_filter)) {
267  if (!pass) {
268  RDEBUG3("Attribute \"%s\" allowed by relaxed mode", input_item->da->name);
269  }
270  vp = fr_pair_copy(packet, input_item);
271  if (!vp) {
272  goto error;
273  }
274  fr_cursor_insert(&out, vp);
275  }
276  }
277 
278  /* If we shouldn't fall through, break */
279  if (!fall_through) {
280  break;
281  }
282  }
283 
284  /*
285  * No entry matched. We didn't do anything.
286  */
287  if (!found) {
288  rad_assert(!output);
289  return RLM_MODULE_NOOP;
290  }
291 
292  /*
293  * Replace the existing request list with our filtered one
294  */
295  fr_pair_list_free(&packet->vps);
296  packet->vps = output;
297 
298  if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
299  request->username = fr_pair_find_by_num(request->packet->vps, 0, PW_STRIPPED_USER_NAME, TAG_ANY);
300  if (!request->username) {
301  request->username = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_NAME, TAG_ANY);
302  }
303  request->password = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY);
304  }
305 
306  return RLM_MODULE_UPDATED;
307 
308  error:
309  fr_pair_list_free(&output);
310  return RLM_MODULE_FAIL;
311 }
312 
313 #define RLM_AF_FUNC(_x, _y) static rlm_rcode_t CC_HINT(nonnull) mod_##_x(void *instance, REQUEST *request) \
314  { \
315  return attr_filter_common(instance, request, request->_y); \
316  }
317 
318 RLM_AF_FUNC(authorize, packet)
319 RLM_AF_FUNC(post_auth, reply)
320 
321 RLM_AF_FUNC(preacct, packet)
322 RLM_AF_FUNC(accounting, reply)
323 
324 #ifdef WITH_PROXY
325 RLM_AF_FUNC(pre_proxy, proxy)
326 RLM_AF_FUNC(post_proxy, proxy_reply)
327 #endif
328 
329 #ifdef WITH_COA
330 RLM_AF_FUNC(recv_coa, packet)
331 RLM_AF_FUNC(send_coa, reply)
332 #endif
333 
334 /* globally exported name */
336 module_t rlm_attr_filter = {
338  .name = "attr_filter",
339  .type = RLM_TYPE_HUP_SAFE,
340  .inst_size = sizeof(rlm_attr_filter_t),
341  .config = module_config,
342  .instantiate = mod_instantiate,
343  .methods = {
345  [MOD_PREACCT] = mod_preacct,
347 #ifdef WITH_PROXY
348  [MOD_PRE_PROXY] = mod_pre_proxy,
349  [MOD_POST_PROXY] = mod_post_proxy,
350 #endif
352 #ifdef WITH_COA
353  [MOD_RECV_COA] = mod_recv_coa,
354  [MOD_SEND_COA] = mod_send_coa
355 #endif
356  },
357 };
358 
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
Definition: pair.c:544
5 methods index for preproxy section.
Definition: modules.h:46
#define PW_TYPE_FILE_INPUT
File matching value must exist, and must be readable.
Definition: conffile.h:204
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
static int attr_filter_getfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list)
VALUE_PAIR * fr_cursor_first(vp_cursor_t *cursor)
Rewind cursor to the start of the list.
Definition: cursor.c:105
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:239
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
char const * name
Definition: tmpl.h:120
VALUE_PAIR * check
Definition: tmpl.h:121
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull)
Metadata exported by the module.
Definition: modules.h:134
static rlm_rcode_t CC_HINT(nonnull(1, 2))
module_t rlm_attr_filter
7 methods index for postauth section.
Definition: modules.h:48
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
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
#define inst
#define RLM_TYPE_HUP_SAFE
Will be restarted on HUP.
Definition: modules.h:79
static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
#define is_truncated(_ret, _max)
Definition: libradius.h:204
#define rad_assert(expr)
Definition: rad_assert.h:38
RFC2865 - Access-Request.
Definition: radius.h:92
struct pair_list * next
Definition: tmpl.h:124
struct rlm_attr_filter rlm_attr_filter_t
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
Definition: cursor.c:321
#define STRINGIFY(x)
Definition: build.h:34
unsigned int attr
Attribute number.
Definition: dict.h:79
static int fall_through(VALUE_PAIR *vp)
Definition: rlm_files.c:73
char const * filename
3 methods index for accounting section.
Definition: modules.h:44
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
A truth value.
Definition: radius.h:56
#define RLM_AF_FUNC(_x, _y)
Definition: token.h:45
FR_TOKEN op
Operator to use when moving or inserting valuepair into a list.
Definition: pair.h:118
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
Expands an attribute marked with fr_pair_mark_xlat.
Definition: pair.c:655
static rs_t * conf
Definition: radsniff.c:46
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
Module succeeded without doing anything.
Definition: radiusd.h:96
#define RDEBUG2(fmt,...)
Definition: log.h:244
char name[1]
Attribute name.
Definition: dict.h:89
vp_tmpl_t * key
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
int lineno
Definition: tmpl.h:123
static const CONF_PARSER module_config[]
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
#define WARN(fmt,...)
Definition: log.h:144
#define REDEBUG(fmt,...)
Definition: log.h:254
6 methods index for postproxy section.
Definition: modules.h:47
2 methods index for preacct section.
Definition: modules.h:43
VALUE_PAIR * fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
Copy a single valuepair.
Definition: pair.c:129
static rbtree_t * pl
Definition: process.c:53
size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
Print one attribute and value to a string.
Definition: pair.c:2189
int fr_pair_cmp(VALUE_PAIR *a, VALUE_PAIR *b)
Compare two pairs, using the operator from "a".
Definition: pair.c:850
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: conffile.h:200
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
8 methods index for recvcoa section.
Definition: modules.h:50
PAIR_LIST * attrs
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
9 methods index for sendcoa section.
Definition: modules.h:51
#define PW_TYPE_TMPL
CONF_PAIR should be parsed as a template.
Definition: conffile.h:208
1 methods index for authorize section.
Definition: modules.h:42
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
VALUE_PAIR * reply
Definition: tmpl.h:122
int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain)
Definition: files.c:80
A source or sink of value data.
Definition: tmpl.h:187
#define ERROR(fmt,...)
Definition: log.h:145
static void check_pair(REQUEST *request, VALUE_PAIR *check_item, VALUE_PAIR *reply_item, int *pass, int *fail)
#define RDEBUG3(fmt,...)
Definition: log.h:245