All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_exec.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: 2315b5b1a0548766d942c01dac61be07dedaaf6d $
19  * @file rlm_exec.c
20  * @brief Execute commands and parse the results.
21  *
22  * @copyright 2002,2006 The FreeRADIUS server project
23  * @copyright 2002 Alan DeKok <aland@ox.org>
24  */
25 RCSID("$Id: 2315b5b1a0548766d942c01dac61be07dedaaf6d $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
30 
31 /*
32  * Define a structure for our module configuration.
33  */
34 typedef struct rlm_exec_t {
35  char const *xlat_name;
36  int bare;
37  bool wait;
38  char const *program;
39  char const *input;
40  char const *output;
43  char const *packet_type;
44  unsigned int packet_code;
46  uint32_t timeout;
47 } rlm_exec_t;
48 
49 /*
50  * A mapping of configuration file names to internal variables.
51  *
52  * Note that the string is dynamically allocated, so it MUST
53  * be freed. When the configuration file parse re-reads the string,
54  * it free's the old one, and strdup's the new one, placing the pointer
55  * to the strdup'd string into 'config.string'. This gets around
56  * buffer over-flows.
57  */
58 static const CONF_PARSER module_config[] = {
59  { FR_CONF_OFFSET("wait", PW_TYPE_BOOLEAN, rlm_exec_t, wait), .dflt = "yes" },
60  { FR_CONF_OFFSET("program", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_exec_t, program) },
61  { FR_CONF_OFFSET("input_pairs", PW_TYPE_STRING, rlm_exec_t, input) },
62  { FR_CONF_OFFSET("output_pairs", PW_TYPE_STRING, rlm_exec_t, output) },
63  { FR_CONF_OFFSET("packet_type", PW_TYPE_STRING, rlm_exec_t, packet_type) },
64  { FR_CONF_OFFSET("shell_escape", PW_TYPE_BOOLEAN, rlm_exec_t, shell_escape), .dflt = "yes" },
67 };
68 
69 static char const special[] = "\\'\"`<>|; \t\r\n()[]?#$^&*=";
70 
71 /*
72  * Escape special characters
73  */
74 static size_t rlm_exec_shell_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in,
75  UNUSED void *inst)
76 {
77  char *q, *end;
78  char const *p;
79 
80  q = out;
81  end = out + outlen;
82  p = in;
83 
84  while (*p) {
85  if ((q + 3) >= end) break;
86 
87  if (strchr(special, *p) != NULL) {
88  *(q++) = '\\';
89  }
90  *(q++) = *(p++);
91  }
92 
93  *q = '\0';
94  return q - out;
95 }
96 
97 /** Process the exit code returned by one of the exec functions
98  *
99  * @param request Current request.
100  * @param answer Output string from exec call.
101  * @param len length of data in answer.
102  * @param status code returned by exec call.
103  * @return One of the RLM_MODULE_* values.
104  */
105 static rlm_rcode_t rlm_exec_status2rcode(REQUEST *request, char *answer, size_t len, int status)
106 {
107  if (status < 0) {
108  return RLM_MODULE_FAIL;
109  }
110 
111  /*
112  * Exec'd programs are meant to return exit statuses that correspond
113  * to the standard RLM_MODULE_* + 1.
114  *
115  * This frees up 0, for success where it'd normally be reject.
116  */
117  if (status == 0) {
118  RDEBUG("Program executed successfully");
119 
120  return RLM_MODULE_OK;
121  }
122 
123  if (status > RLM_MODULE_NUMCODES) {
124  REDEBUG("Program returned invalid code (greater than max rcode) (%i > %i): %s",
125  status, RLM_MODULE_NUMCODES, answer);
126  goto fail;
127  }
128 
129  status--; /* Lets hope no one ever re-enumerates RLM_MODULE_* */
130 
131  if (status == RLM_MODULE_FAIL) {
132  fail:
133 
134  if (len > 0) {
135  char *p = &answer[len - 1];
136 
137  /*
138  * Trim off trailing returns
139  */
140  while((p > answer) && ((*p == '\r') || (*p == '\n'))) {
141  *p-- = '\0';
142  }
143 
144  module_failure_msg(request, "%s", answer);
145  }
146 
147  return RLM_MODULE_FAIL;
148  }
149 
150  return status;
151 }
152 
153 /*
154  * Do xlat of strings.
155  */
156 static ssize_t exec_xlat(char **out, size_t outlen,
157  void const *mod_inst, UNUSED void const *xlat_inst,
158  REQUEST *request, char const *fmt)
159 {
160  int result;
161  rlm_exec_t const *inst = mod_inst;
162  VALUE_PAIR **input_pairs = NULL;
163  char *p;
164 
165  if (!inst->wait) {
166  REDEBUG("'wait' must be enabled to use exec xlat");
167  return -1;
168  }
169 
170  if (inst->input_list) {
171  input_pairs = radius_list(request, inst->input_list);
172  if (!input_pairs) {
173  REDEBUG("Failed to find input pairs for xlat");
174  return -1;
175  }
176  }
177 
178  /*
179  * This function does it's own xlat of the input program
180  * to execute.
181  */
182  result = radius_exec_program(request, *out, outlen, NULL, request, fmt, input_pairs ? *input_pairs : NULL,
183  inst->wait, inst->shell_escape, inst->timeout);
184  if (result != 0) return -1;
185 
186  for (p = *out; *p != '\0'; p++) {
187  if (*p < ' ') *p = ' ';
188  }
189 
190  return strlen(*out);
191 }
192 
193 /*
194  * Do any per-module initialization that is separate to each
195  * configured instance of the module. e.g. set up connections
196  * to external databases, read configuration files, set up
197  * dictionary entries, etc.
198  *
199  * If configuration information is given in the config section
200  * that must be referenced in later calls, store a handle to it
201  * in *instance otherwise put a null pointer there.
202  */
203 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
204 {
205  char const *p;
206  rlm_exec_t *inst = instance;
207 
208  inst->xlat_name = cf_section_name2(conf);
209  if (!inst->xlat_name) {
210  inst->xlat_name = cf_section_name1(conf);
211  inst->bare = 1;
212  }
213 
215 
216  if (inst->input) {
217  p = inst->input;
219  if ((inst->input_list == PAIR_LIST_UNKNOWN) || (*p != '\0')) {
220  cf_log_err_cs(conf, "Invalid input list '%s'", inst->input);
221  return -1;
222  }
223  }
224 
225  if (inst->output) {
226  p = inst->output;
228  if ((inst->output_list == PAIR_LIST_UNKNOWN) || (*p != '\0')) {
229  cf_log_err_cs(conf, "Invalid output list '%s'", inst->output);
230  return -1;
231  }
232  }
233 
234  /*
235  * Sanity check the config. If we're told to NOT wait,
236  * then the output pairs must not be defined.
237  */
238  if (!inst->wait && (inst->output != NULL)) {
239  cf_log_err_cs(conf, "Cannot read output pairs if wait = no");
240  return -1;
241  }
242 
243  /*
244  * Get the packet type on which to execute
245  */
246  if (!inst->packet_type) {
247  inst->packet_code = 0;
248  } else {
249  fr_dict_enum_t *dval;
250 
251  dval = fr_dict_enum_by_name(NULL, fr_dict_attr_by_num(NULL, 0, PW_PACKET_TYPE), inst->packet_type);
252  if (!dval) {
253  cf_log_err_cs(conf, "Unknown packet type %s: See list of VALUEs for Packet-Type in "
254  "share/dictionary", inst->packet_type);
255  return -1;
256  }
257  inst->packet_code = dval->value;
258  }
259 
260  /*
261  * Get the time to wait before killing the child
262  */
263  if (!inst->timeout) {
264  inst->timeout = EXEC_TIMEOUT;
265  }
266  if (inst->timeout < 1) {
267  cf_log_err_cs(conf, "Timeout '%d' is too small (minimum: 1)", inst->timeout);
268  return -1;
269  }
270  /*
271  * Blocking a request longer than max_request_time isn't going to help anyone.
272  */
273  if (inst->timeout > main_config.max_request_time) {
274  cf_log_err_cs(conf, "Timeout '%d' is too large (maximum: %d)", inst->timeout, main_config.max_request_time);
275  return -1;
276  }
277 
278  return 0;
279 }
280 
281 
282 /*
283  * Dispatch an exec method
284  */
285 static rlm_rcode_t CC_HINT(nonnull) mod_exec_dispatch(void *instance, REQUEST *request)
286 {
287  rlm_exec_t *inst = (rlm_exec_t *)instance;
288  rlm_rcode_t rcode;
289  int status;
290 
291  VALUE_PAIR **input_pairs = NULL, **output_pairs = NULL;
292  VALUE_PAIR *answer = NULL;
293  TALLOC_CTX *ctx = NULL;
294  char out[1024];
295 
296  /*
297  * We need a program to execute.
298  */
299  if (!inst->program) {
300  ERROR("rlm_exec (%s): We require a program to execute", inst->xlat_name);
301  return RLM_MODULE_FAIL;
302  }
303 
304  /*
305  * See if we're supposed to execute it now.
306  */
307  if (!((inst->packet_code == 0) || (request->packet->code == inst->packet_code) ||
308  (request->reply->code == inst->packet_code)
309 #ifdef WITH_PROXY
310  || (request->proxy && (request->proxy->code == inst->packet_code)) ||
311  (request->proxy_reply && (request->proxy_reply->code == inst->packet_code))
312 #endif
313  )) {
314  RDEBUG2("Packet type is not %s. Not executing.", inst->packet_type);
315 
316  return RLM_MODULE_NOOP;
317  }
318 
319  /*
320  * Decide what input/output the program takes.
321  */
322  if (inst->input) {
323  input_pairs = radius_list(request, inst->input_list);
324  if (!input_pairs) {
325  return RLM_MODULE_INVALID;
326  }
327  }
328 
329  if (inst->output) {
330  output_pairs = radius_list(request, inst->output_list);
331  if (!output_pairs) {
332  return RLM_MODULE_INVALID;
333  }
334 
335  ctx = radius_list_ctx(request, inst->output_list);
336  }
337 
338  /*
339  * This function does it's own xlat of the input program
340  * to execute.
341  */
342  status = radius_exec_program(ctx, out, sizeof(out), inst->output ? &answer : NULL, request,
343  inst->program, inst->input ? *input_pairs : NULL,
344  inst->wait, inst->shell_escape, inst->timeout);
345  rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
346 
347  /*
348  * Move the answer over to the output pairs.
349  *
350  * If we're not waiting, then there are no output pairs.
351  */
352  if (inst->output) {
353  fr_pair_list_move(request, output_pairs, &answer);
354  }
355  fr_pair_list_free(&answer);
356 
357  return rcode;
358 }
359 
360 
361 /*
362  * First, look for Exec-Program && Exec-Program-Wait.
363  *
364  * Then, call exec_dispatch.
365  */
366 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
367 {
368  rlm_exec_t *inst = (rlm_exec_t *) instance;
369  rlm_rcode_t rcode;
370  int status;
371 
372  char out[1024];
373  bool we_wait = false;
374  VALUE_PAIR *vp, *tmp;
375 
376  vp = fr_pair_find_by_num(request->reply->vps, 0, PW_EXEC_PROGRAM, TAG_ANY);
377  if (vp) {
378  we_wait = false;
379  } else if ((vp = fr_pair_find_by_num(request->reply->vps, 0, PW_EXEC_PROGRAM_WAIT, TAG_ANY)) != NULL) {
380  we_wait = true;
381  }
382  if (!vp) {
383  if (!inst->program) {
384  return RLM_MODULE_NOOP;
385  }
386 
387  rcode = mod_exec_dispatch(instance, request);
388  goto finish;
389  }
390 
391  tmp = NULL;
392  status = radius_exec_program(request, out, sizeof(out), &tmp, request, vp->vp_strvalue, request->packet->vps,
393  we_wait, inst->shell_escape, inst->timeout);
394  rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
395 
396  /*
397  * Always add the value-pairs to the reply.
398  */
399  fr_pair_list_move(request->reply, &request->reply->vps, &tmp);
400  fr_pair_list_free(&tmp);
401 
402  finish:
403  switch (rcode) {
404  case RLM_MODULE_FAIL:
405  case RLM_MODULE_INVALID:
406  case RLM_MODULE_REJECT:
407  request->reply->code = PW_CODE_ACCESS_REJECT;
408  break;
409 
410  default:
411  break;
412  }
413 
414  return rcode;
415 }
416 
417 /*
418  * First, look for Exec-Program && Exec-Program-Wait.
419  *
420  * Then, call exec_dispatch.
421  */
422 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
423 {
424  rlm_exec_t *inst = (rlm_exec_t *) instance;
425  int status;
426 
427  char out[1024];
428  bool we_wait = false;
429  VALUE_PAIR *vp;
430 
431  /*
432  * The "bare" exec module takes care of handling
433  * Exec-Program and Exec-Program-Wait.
434  */
435  if (!inst->bare) {
436  return mod_exec_dispatch(instance, request);
437  }
438 
439  vp = fr_pair_find_by_num(request->reply->vps, 0, PW_EXEC_PROGRAM, TAG_ANY);
440  if (vp) {
441  we_wait = true;
442  } else if ((vp = fr_pair_find_by_num(request->reply->vps, 0, PW_EXEC_PROGRAM_WAIT, TAG_ANY)) != NULL) {
443  we_wait = false;
444  }
445  if (!vp) {
446  return RLM_MODULE_NOOP;
447  }
448 
449  status = radius_exec_program(request, out, sizeof(out), NULL, request, vp->vp_strvalue, request->packet->vps,
450  we_wait, inst->shell_escape, inst->timeout);
451  return rlm_exec_status2rcode(request, out, strlen(out), status);
452 }
453 
454 /*
455  * The module name should be the only globally exported symbol.
456  * That is, everything else should be 'static'.
457  *
458  * If the module needs to temporarily modify it's instantiation
459  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
460  * The server will then take care of ensuring that the module
461  * is single-threaded.
462  */
463 extern module_t rlm_exec;
464 module_t rlm_exec = {
466  .name = "exec",
467  .type = RLM_TYPE_THREAD_SAFE,
468  .inst_size = sizeof(rlm_exec_t),
469  .config = module_config,
470  .bootstrap = mod_bootstrap,
471  .methods = {
472  [MOD_AUTHENTICATE] = mod_exec_dispatch,
473  [MOD_AUTHORIZE] = mod_exec_dispatch,
474  [MOD_PREACCT] = mod_exec_dispatch,
476  [MOD_PRE_PROXY] = mod_exec_dispatch,
477  [MOD_POST_PROXY] = mod_exec_dispatch,
479 #ifdef WITH_COA
480  [MOD_RECV_COA] = mod_exec_dispatch,
481  [MOD_SEND_COA] = mod_exec_dispatch
482 #endif
483  },
484 };
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
static rlm_rcode_t rlm_exec_status2rcode(REQUEST *request, char *answer, size_t len, int status)
Process the exit code returned by one of the exec functions.
Definition: rlm_exec.c:105
module_t rlm_exec
Definition: rlm_exec.c:464
int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, VALUE_PAIR **output_pairs, REQUEST *request, char const *cmd, VALUE_PAIR *input_pairs, bool exec_wait, bool shell_escape, int timeout) CC_HINT(nonnull(5
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
Main server configuration.
Definition: radiusd.h:108
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
pair_lists_t output_list
Definition: rlm_exec.c:42
The module is OK, continue.
Definition: radiusd.h:91
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull)
Metadata exported by the module.
Definition: modules.h:134
static int mod_bootstrap(CONF_SECTION *conf, void *instance)
Definition: rlm_exec.c:203
uint32_t timeout
Definition: rlm_exec.c:46
7 methods index for postauth section.
Definition: modules.h:48
unsigned int packet_code
Definition: rlm_exec.c:44
TALLOC_CTX * radius_list_ctx(REQUEST *request, pair_lists_t list_name)
Return the correct TALLOC_CTX to alloc VALUE_PAIR in, for a list.
Definition: tmpl.c:331
VALUE_PAIR ** radius_list(REQUEST *request, pair_lists_t list)
Resolve attribute pair_lists_t value to an attribute list.
Definition: tmpl.c:195
char const * xlat_name
Definition: rlm_exec.c:35
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_exec.c:285
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
static float timeout
Definition: radclient.c:43
#define inst
char const * input
Definition: rlm_exec.c:39
The module considers the request invalid.
Definition: radiusd.h:93
#define XLAT_DEFAULT_BUF_LEN
Definition: xlat.h:89
char const * packet_type
Definition: rlm_exec.c:43
char const * output
Definition: rlm_exec.c:40
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
fr_dict_enum_t * fr_dict_enum_by_name(fr_dict_t *dict, fr_dict_attr_t const *da, char const *val)
Definition: dict.c:3703
char const * program
Definition: rlm_exec.c:38
RFC2865 - Access-Reject.
Definition: radius.h:94
bool shell_escape
Definition: rlm_exec.c:45
static char const special[]
Definition: rlm_exec.c:69
static ssize_t exec_xlat(char **out, size_t outlen, void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt)
Definition: rlm_exec.c:156
static const CONF_PARSER module_config[]
Definition: rlm_exec.c:58
uint32_t max_request_time
How long a request can be processed for before timing out.
Definition: radiusd.h:132
#define PW_TYPE_XLAT
string will be dynamically expanded.
Definition: conffile.h:207
Immediately reject the request.
Definition: radiusd.h:89
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
void void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt,...) CC_HINT(format(printf
0 methods index for authenticate section.
Definition: modules.h:41
A truth value.
Definition: radius.h:56
32 Bit unsigned integer.
Definition: radius.h:34
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
Module succeeded without doing anything.
Definition: radiusd.h:96
#define RDEBUG2(fmt,...)
Definition: log.h:244
int bare
Definition: rlm_exec.c:36
void fr_pair_list_move(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
Move pairs from source list to destination list respecting operator.
Definition: pair.c:1508
pair_lists_t input_list
Definition: rlm_exec.c:41
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
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
size_t radius_list_name(pair_lists_t *out, char const *name, pair_lists_t default_list)
Resolve attribute name to a pair_lists_t value.
Definition: tmpl.c:120
void module_failure_msg(REQUEST *request, char const *fmt,...) CC_HINT(format(printf
Unknown list.
Definition: tmpl.h:81
static size_t rlm_exec_shell_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *inst)
Definition: rlm_exec.c:74
enum pair_lists pair_lists_t
int value
Enum value.
Definition: dict.h:96
#define REDEBUG(fmt,...)
Definition: log.h:254
bool wait
Definition: rlm_exec.c:37
struct rlm_exec_t rlm_exec_t
6 methods index for postproxy section.
Definition: modules.h:47
2 methods index for preacct section.
Definition: modules.h:43
#define EXEC_TIMEOUT
Definition: radiusd.h:329
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
9 methods index for sendcoa section.
Definition: modules.h:51
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
How many valid return codes there are.
Definition: radiusd.h:98
String of printable characters.
Definition: radius.h:33
1 methods index for authorize section.
Definition: modules.h:42
#define RCSID(id)
Definition: build.h:135
#define RDEBUG(fmt,...)
Definition: log.h:243
#define ERROR(fmt,...)
Definition: log.h:145
Value of an enumerated attribute.
Definition: dict.h:94
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601