The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_client.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: a6d3d24ae3a56d4e58240f819fd737b74c43c0fc $
19  * @file rlm_client.c
20  * @brief Reads client definitions from flat files as required.
21  *
22  * @copyright 2008 The FreeRADIUS server project
23  * @copyright 2008 Alan DeKok (aland@deployingradius.com)
24  * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25  */
26 #include "lib/server/cf_util.h"
27 RCSID("$Id: a6d3d24ae3a56d4e58240f819fd737b74c43c0fc $")
28 
29 #include <freeradius-devel/server/base.h>
30 #include <freeradius-devel/server/module_rlm.h>
31 #include <freeradius-devel/server/map_proc.h>
32 #include <freeradius-devel/util/debug.h>
33 #include <freeradius-devel/unlang/xlat_func.h>
34 
35 /** Client field
36  *
37  */
38 typedef struct {
39  CONF_SECTION *cs; //!< Client's CONF_SECTION.
40  CONF_PAIR *cp; //!< First instance of the field in the client's CONF_SECTION.
41  char const *field; //!< Field name.
43 
44 static int _map_proc_client_get_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request,
45  map_t const *map, void *uctx)
46 {
47  client_get_vp_ctx_t *client = uctx;
49  fr_pair_t *vp;
50  fr_dict_attr_t const *da;
51  CONF_PAIR const *cp;
52 
53  fr_assert(ctx != NULL);
54 
56 
57  /*
58  * FIXME: allow multiple entries.
59  */
60  if (tmpl_is_attr(map->lhs)) {
61  da = tmpl_attr_tail_da(map->lhs);
62  } else {
63  char *attr;
64 
65  if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) {
66  RWDEBUG("Failed expanding string");
67  error:
69  return -1;
70  }
71 
72  da = fr_dict_attr_by_name(NULL, fr_dict_root(request->dict), attr);
73  if (!da) {
74  RWDEBUG("No such attribute '%s'", attr);
75  talloc_free(attr);
76  goto error;
77  }
78 
79  talloc_free(attr);
80  }
81 
82  for (cp = client->cp;
83  cp;
84  cp = cf_pair_find_next(client->cs, cp, client->field)) {
85  char const *value = cf_pair_value(cp);
86 
87  MEM(vp = fr_pair_afrom_da(ctx, da));
88  if (fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, NULL, false) < 0) {
89  RWDEBUG("Failed parsing value \"%pV\" for attribute %s: %s", fr_box_strvalue(value),
90  tmpl_attr_tail_da(map->lhs)->name, fr_strerror());
91  talloc_free(vp);
92  goto error;
93  }
94 
96 
97  if (map->op != T_OP_ADD_EQ) break; /* Create multiple attribute for multiple CONF_PAIRs */
98  }
99 
101 
102  return 0;
103 }
104 
105 /** Map multiple attributes from a client into the request
106  *
107  * @param[out] p_result Result of applying the map:
108  * - #RLM_MODULE_NOOP no rows were returned.
109  * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
110  * - #RLM_MODULE_FAIL if an error occurred.
111  * @param[in] mod_inst NULL.
112  * @param[in] proc_inst NULL.
113  * @param[in] request The current request.
114  * @param[in] client_override If NULL, use the current client, else use the client matching
115  * the ip given.
116  * @param[in] maps Head of the map list.
117  * @return UNLANG_ACTION_CALCULATE_RESULT
118  */
119 static unlang_action_t map_proc_client(rlm_rcode_t *p_result, UNUSED void *mod_inst, UNUSED void *proc_inst,
120  request_t *request, fr_value_box_list_t *client_override, map_list_t const *maps)
121 {
122  rlm_rcode_t rcode = RLM_MODULE_OK;
123  map_t const *map = NULL;
124  fr_client_t *client;
125  client_get_vp_ctx_t uctx;
126 
127  if (!fr_value_box_list_empty(client_override)) {
128  fr_ipaddr_t ip;
129  char const *client_str;
130  fr_value_box_t *client_override_head = fr_value_box_list_head(client_override);
131 
132  /*
133  * Concat don't asprint, as this becomes a noop
134  * in the vast majority of cases.
135  */
137  client_override_head, client_override, FR_TYPE_STRING,
139  SIZE_MAX) < 0) {
140  REDEBUG("Failed concatenating input data");
142  }
143  client_str = client_override_head->vb_strvalue;
144 
145  if (fr_inet_pton(&ip, client_str, -1, AF_UNSPEC, false, true) < 0) {
146  REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", client_str);
147  rcode = RLM_MODULE_FAIL;
148  goto finish;
149  }
150 
151  client = client_find(NULL, &ip, IPPROTO_IP);
152  if (!client) {
153  RDEBUG("No client found with IP \"%s\"", client_str);
154  rcode = RLM_MODULE_NOTFOUND;
155  goto finish;
156  }
157 
158  if (client->cs) {
159  char const *filename;
160  int line;
161 
162  filename = cf_filename(client->cs);
163  line = cf_lineno(client->cs);
164 
165  if (filename) {
166  RDEBUG2("Found client matching \"%s\". Defined in \"%s\" line %i",
167  client_str, filename, line);
168  } else {
169  RDEBUG2("Found client matching \"%s\"", client_str);
170  }
171  }
172  } else {
173  client = client_from_request(request);
174  if (!client) {
175  REDEBUG("No client associated with this request");
177  }
178  }
179  uctx.cs = client->cs;
180 
181  RINDENT();
182  while ((map = map_list_next(maps, map))) {
183  char *field = NULL;
184 
185  if (tmpl_aexpand(request, &field, request, map->rhs, NULL, NULL) < 0) {
186  REDEBUG("Failed expanding RHS at %s", map->lhs->name);
187  rcode = RLM_MODULE_FAIL;
188  talloc_free(field);
189  break;
190  }
191 
192  uctx.cp = cf_pair_find(client->cs, field);
193  if (!uctx.cp) {
194  RDEBUG3("No matching client property \"%s\", skipping...", field);
195  goto next; /* No matching CONF_PAIR found */
196  }
197  uctx.field = field;
198 
199  /*
200  * Pass the raw data to the callback, which will
201  * create the VP and add it to the map.
202  */
203  if (map_to_request(request, map, _map_proc_client_get_vp, &uctx) < 0) {
204  rcode = RLM_MODULE_FAIL;
205  talloc_free(field);
206  break;
207  }
208  rcode = RLM_MODULE_UPDATED;
209 
210  next:
211  talloc_free(field);
212  }
213  REXDENT();
214 
215 finish:
216  RETURN_MODULE_RCODE(rcode);
217 }
218 
220  { .required = true, .single = true, .type = FR_TYPE_STRING },
221  { .single = true, .type = FR_TYPE_STRING },
223 };
224 
225 /** xlat to get client config data
226  *
227  * Example:
228 @verbatim
229 %client(foo, [<ipaddr>])
230 @endverbatim
231  *
232  * @ingroup xlat_functions
233  */
234 static xlat_action_t xlat_client(TALLOC_CTX *ctx, fr_dcursor_t *out,
235  UNUSED xlat_ctx_t const *xctx,
236  request_t *request, fr_value_box_list_t *in)
237 {
238  char const *value = NULL;
239  fr_ipaddr_t ip;
240  CONF_PAIR *cp;
241  fr_client_t *client = NULL;
242  fr_value_box_t *field = fr_value_box_list_head(in);
243  fr_value_box_t *client_ip = fr_value_box_list_next(in, field);
244  fr_value_box_t *vb;
245 
246  if (client_ip) {
247  if (fr_inet_pton(&ip, client_ip->vb_strvalue, -1, AF_UNSPEC, false, true) < 0) {
248  RDEBUG("Invalid client IP address \"%s\"", client_ip->vb_strvalue);
249  return XLAT_ACTION_FAIL;
250  }
251 
252  client = client_find(NULL, &ip, IPPROTO_IP);
253  if (!client) {
254  RDEBUG("No client found with IP \"%s\"", client_ip->vb_strvalue);
255  return XLAT_ACTION_FAIL;
256  }
257  } else {
258  client = client_from_request(request);
259  if (!client) {
260  REDEBUG("No client associated with this request");
261  return XLAT_ACTION_FAIL;
262  }
263  }
264 
265  cp = cf_pair_find(client->cs, field->vb_strvalue);
266  if (!cp || !(value = cf_pair_value(cp))) {
267  if (strcmp(field->vb_strvalue, "shortname") == 0 && client->shortname) {
268  value = client->shortname;
269  }
270  else if (strcmp(field->vb_strvalue, "nas_type") == 0 && client->nas_type) {
271  value = client->nas_type;
272  }
273  if (!value) return XLAT_ACTION_DONE;
274  }
275 
276  MEM(vb = fr_value_box_alloc_null(ctx));
277 
278  if (fr_value_box_strdup(ctx, vb, NULL, value, false) < 0) {
279  talloc_free(vb);
280  return XLAT_ACTION_FAIL;
281  }
282 
283  fr_dcursor_append(out, vb);
284  return XLAT_ACTION_DONE;
285 }
286 
287 
288 /*
289  * Find the client definition.
290  */
291 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, UNUSED module_ctx_t const *mctx, request_t *request)
292 {
293  size_t length;
294  char const *value;
295  CONF_PAIR *cp;
296  char buffer[2048];
297  fr_client_t *client;
298 
299  /*
300  * Ensure we're only being called from the main thread,
301  * with fake packets.
302  */
303  if ((request->packet->socket.inet.src_port != 0) || (!fr_pair_list_empty(&request->request_pairs)) ||
304  (request->parent != NULL)) {
305  REDEBUG("Improper configuration");
307  }
308 
309  client = client_from_request(request);
310  if (!client || !client->cs) {
311  REDEBUG("Unknown client definition");
313  }
314 
315  cp = cf_pair_find(client->cs, "directory");
316  if (!cp) {
317  REDEBUG("No directory configuration in the client");
319  }
320 
321  value = cf_pair_value(cp);
322  if (!value) {
323  REDEBUG("No value given for the directory entry in the client");
325  }
326 
327  length = strlen(value);
328  if (length > (sizeof(buffer) - 256)) {
329  REDEBUG("Directory name too long");
331  }
332 
333  memcpy(buffer, value, length + 1);
334  fr_inet_ntoh(&request->packet->socket.inet.src_ipaddr, buffer + length, sizeof(buffer) - length - 1);
335 
336  /*
337  * Read the buffer and generate the client.
338  */
339  if (!client->server) RETURN_MODULE_FAIL;
340 
341  client = client_read(buffer, client->server_cs, true);
342  if (!client) RETURN_MODULE_FAIL;
343 
344  /*
345  * Replace the client. This is more than a bit of a
346  * hack.
347  */
348  request->client = client;
349 
351 }
352 
353 static int mod_load(void)
354 {
355  xlat_t *xlat;
356 
357  if (unlikely((xlat = xlat_func_register(NULL, "client", xlat_client, FR_TYPE_STRING)) == NULL)) return -1;
359 
360  map_proc_register(NULL, "client", map_proc_client, NULL, 0, 0);
361 
362  return 0;
363 }
364 
365 static void mod_unload(void)
366 {
367  xlat_func_unregister("client");
368 }
369 
370 /*
371  * The module name should be the only globally exported symbol.
372  * That is, everything else should be 'static'.
373  *
374  * If the module needs to temporarily modify it's instantiation
375  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
376  * The server will then take care of ensuring that the module
377  * is single-threaded.
378  */
379 extern module_rlm_t rlm_client;
381  .common = {
382  .magic = MODULE_MAGIC_INIT,
383  .name = "dynamic_clients",
384  .flags = MODULE_TYPE_THREAD_SAFE, /* type */
385  .onload = mod_load,
386  .unload = mod_unload
387  },
388  .method_names = (module_method_name_t[]){
389  { .name1 = CF_IDENT_ANY, .name2 = CF_IDENT_ANY, .method = mod_authorize },
391  }
392 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
static int const char char buffer[256]
Definition: acutest.h:574
int const char int line
Definition: acutest.h:702
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition: cf_util.c:1356
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *cs, CONF_PAIR const *prev, char const *attr)
Find a pair with a name matching attr, after specified pair.
Definition: cf_util.c:1370
API to create and manipulate internal format configurations.
#define cf_lineno(_cf)
Definition: cf_util.h:101
#define cf_filename(_cf)
Definition: cf_util.h:104
#define CF_IDENT_ANY
Definition: cf_util.h:78
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition: dcursor.h:405
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition: dict_util.c:2860
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
static fr_slen_t in
Definition: dict.h:645
Test enumeration values.
Definition: dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
static xlat_action_t xlat_client(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
xlat to get client config data
Definition: rlm_client.c:234
char const * fr_inet_ntoh(fr_ipaddr_t const *src, char *out, size_t outlen)
Perform reverse resolution of an IP address.
Definition: inet.c:341
int fr_inet_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser.
Definition: inet.c:764
IPv4/6 prefix.
Definition: merged_model.c:272
char const * server
Name of the virtual server client is associated with.
Definition: client.h:101
char const * nas_type
Type of client (arbitrary).
Definition: client.h:99
CONF_SECTION * cs
CONF_SECTION that was parsed to generate the client.
Definition: client.h:106
char const * shortname
Client nickname.
Definition: client.h:85
CONF_SECTION * server_cs
Virtual server that the client is associated with.
Definition: client.h:102
Describes a host allowed to send packets to the server.
Definition: client.h:77
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition: map.c:1783
talloc_free(reap)
int map_proc_register(void *mod_inst, char const *name, map_proc_func_t evaluate, map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t literals_safe_for)
Register a map processor.
Definition: map_proc.c:111
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Specifies a module method identifier.
Definition: module_method.c:36
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition: pair.c:2586
static void client_read(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Definition: radclient-ng.c:829
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define RDEBUG(fmt,...)
Definition: radclient.h:53
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
module_rlm_t rlm_client
Definition: rlm_client.c:380
static int mod_load(void)
Definition: rlm_client.c:353
static unlang_action_t map_proc_client(rlm_rcode_t *p_result, UNUSED void *mod_inst, UNUSED void *proc_inst, request_t *request, fr_value_box_list_t *client_override, map_list_t const *maps)
Map multiple attributes from a client into the request.
Definition: rlm_client.c:119
static void mod_unload(void)
Definition: rlm_client.c:365
CONF_PAIR * cp
First instance of the field in the client's CONF_SECTION.
Definition: rlm_client.c:40
CONF_SECTION * cs
Client's CONF_SECTION.
Definition: rlm_client.c:39
static xlat_arg_parser_t const xlat_client_args[]
Definition: rlm_client.c:219
char const * field
Field name.
Definition: rlm_client.c:41
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, UNUSED module_ctx_t const *mctx, request_t *request)
Definition: rlm_client.c:291
static int _map_proc_client_get_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, void *uctx)
Definition: rlm_client.c:44
Client field.
Definition: rlm_client.c:38
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: module.h:49
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
#define tmpl_is_attr(vpt)
Definition: tmpl.h:213
#define tmpl_aexpand(_ctx, _out, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition: tmpl.h:1054
fr_client_t * client_find(fr_client_list_t const *clients, fr_ipaddr_t const *ipaddr, int proto)
Definition: client.c:375
fr_client_t * client_from_request(request_t *request)
Search up a list of requests trying to locate one which has a client.
Definition: client.c:1092
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Value pair map.
Definition: map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition: map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:79
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
@ T_OP_ADD_EQ
Definition: token.h:69
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
static fr_slen_t head
Definition: xlat.h:408
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition: value.c:3876
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition: value.c:5725
@ FR_VALUE_BOX_LIST_FREE
Definition: value.h:214
#define fr_box_strvalue(_val)
Definition: value.h:278
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition: value.h:619
static size_t char ** out
Definition: value.h:984
An xlat calling ctx.
Definition: xlat_ctx.h:42
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:360
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition: xlat_func.c:195
void xlat_func_unregister(char const *name)
Unregister an xlat function.
Definition: xlat_func.c:531