The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_escape.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: 7cf275974c9a1e51205c5e09608e619b41dbd4dc $
19  * @file rlm_escape.c
20  * @brief Register escape/unescape xlat functions.
21  *
22  * @copyright 2018 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  */
24 #include "lib/unlang/xlat.h"
25 RCSID("$Id: 7cf275974c9a1e51205c5e09608e619b41dbd4dc $")
27 
28 #include <freeradius-devel/server/base.h>
29 
30 #include <freeradius-devel/server/module_rlm.h>
31 #include <freeradius-devel/util/debug.h>
32 #include <freeradius-devel/unlang/xlat_func.h>
33 
34 #include <ctype.h>
35 
36 /*
37  * Define a structure for our module configuration.
38  */
39 typedef struct {
40  char const *allowed_chars;
41 } rlm_escape_t;
42 
43 static const conf_parser_t module_config[] = {
44  { FR_CONF_OFFSET("safe_characters", rlm_escape_t, allowed_chars), .dflt = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
46 };
47 
48 static char const hextab[] = "0123456789abcdef";
49 
51  { .required = true, .concat = true, .type = FR_TYPE_STRING },
53 };
54 
55 /** Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support
56  *
57  * Example:
58 @verbatim
59 %escape('<img>foo.jpg</img>') == "=60img=62foo.jpg=60/img=62"
60 @endverbatim
61  *
62  * @ingroup xlat_functions
63  */
64 static xlat_action_t escape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
65  xlat_ctx_t const *xctx,
66  request_t *request, fr_value_box_list_t *in)
67 {
68  rlm_escape_t const *inst = talloc_get_type_abort(xctx->mctx->inst->data, rlm_escape_t);
69  fr_value_box_t *arg = fr_value_box_list_head(in);
70  char const *p = arg->vb_strvalue;
71  size_t len;
72  fr_value_box_t *vb;
73  fr_sbuff_t sbuff;
74  fr_sbuff_uctx_talloc_t sbuff_ctx;
75  int i;
76 
77  len = talloc_array_length(inst->allowed_chars) - 1;
78 
79  MEM(vb = fr_value_box_alloc_null(ctx));
80  /*
81  * We don't know how long the final escaped string
82  * will be - assign something twice as long as the input
83  * as a starting point. The maximum length would be 12
84  * times the original if every character is 4 byte UTF8.
85  */
86  if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, arg->vb_length * 2, arg->vb_length * 12)) {
87  error:
88  RPEDEBUG("Failed to allocated buffer for escaped string");
89  talloc_free(vb);
90  return XLAT_ACTION_FAIL;
91  }
92 
93  while (p[0]) {
94  int chr_len = 1;
95 
96  if (fr_utf8_strchr(&chr_len, inst->allowed_chars, len, p) == NULL) {
97  /*
98  * '=' 1 + ([hex]{2}) * chr_len)
99  */
100  for (i = 0; i < chr_len; i++) {
101  if (fr_sbuff_in_sprintf(&sbuff, "=%02X", (uint8_t)p[i]) < 0)
102  goto error;
103  }
104 
105  p += chr_len;
106  continue;
107  }
108 
109  /*
110  * Allowed character (copy whole mb chars at once)
111  */
112  if (fr_sbuff_in_bstrncpy(&sbuff, p, chr_len) < 0)
113  goto error;
114  p += chr_len;
115  }
116 
117  fr_sbuff_trim_talloc(&sbuff, SIZE_MAX);
118  fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), arg->tainted);
119 
120  fr_dcursor_append(out, vb);
121  return XLAT_ACTION_DONE;
122 }
123 
125  { .required = true, .concat = true, .type = FR_TYPE_STRING },
127 };
128 
129 /** Equivalent to the old safe_characters functionality in rlm_sql
130  *
131  * Example:
132 @verbatim
133 %unescape('=60img=62foo.jpg=60/img=62') == "<img>foo.jpg</img>"
134 @endverbatim
135  *
136  * @ingroup xlat_functions
137  */
138 static xlat_action_t unescape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
139  UNUSED xlat_ctx_t const *xctx,
140  request_t *request, fr_value_box_list_t *in)
141 {
142  fr_value_box_t *arg = fr_value_box_list_head(in);
143  char const *p, *end;
144  char *out_p;
145  char *c1, *c2, c3;
146  fr_sbuff_t sbuff;
147  fr_value_box_t *vb;
148 
149  MEM(vb = fr_value_box_alloc_null(ctx));
150  if (fr_value_box_bstr_alloc(ctx, &out_p, vb, NULL, arg->vb_length, arg->tainted) < 0) {
151  talloc_free(vb);
152  RPEDEBUG("Failed allocating space for unescaped string");
153  return XLAT_ACTION_FAIL;
154  }
155  sbuff = FR_SBUFF_IN(out_p, arg->vb_length);
156 
157  p = arg->vb_strvalue;
158  end = p + arg->vb_length;
159  while (*p) {
160  if (*p != '=') {
161  next:
162 
163  (void) fr_sbuff_in_char(&sbuff, *p++);
164  continue;
165  }
166 
167  /* Is a = char */
168 
169  if (((end - p) < 2) ||
170  !(c1 = memchr(hextab, tolower((uint8_t) *(p + 1)), 16)) ||
171  !(c2 = memchr(hextab, tolower((uint8_t) *(p + 2)), 16))) goto next;
172  c3 = ((c1 - hextab) << 4) + (c2 - hextab);
173 
174  (void) fr_sbuff_in_char(&sbuff, c3);
175  p += 3;
176  }
177 
178  fr_value_box_strtrim(ctx, vb);
179  fr_dcursor_append(out, vb);
180 
181  return XLAT_ACTION_DONE;
182 }
183 
184 /*
185  * Do any per-module initialization that is separate to each
186  * configured instance of the module. e.g. set up connections
187  * to external databases, read configuration files, set up
188  * dictionary entries, etc.
189  *
190  * If configuration information is given in the config section
191  * that must be referenced in later calls, store a handle to it
192  * in *instance otherwise put a null pointer there.
193  */
194 static int mod_bootstrap(module_inst_ctx_t const *mctx)
195 {
196  xlat_t *xlat;
197 
198  xlat = xlat_func_register_module(NULL, mctx, "escape", escape_xlat, FR_TYPE_STRING);
201 
202  xlat = xlat_func_register_module(NULL, mctx, "unescape", unescape_xlat, FR_TYPE_STRING);
205 
206  return 0;
207 }
208 
209 /*
210  * The module name should be the only globally exported symbol.
211  * That is, everything else should be 'static'.
212  *
213  * If the module needs to temporarily modify it's instantiation
214  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
215  * The server will then take care of ensuring that the module
216  * is single-threaded.
217  */
218 extern module_rlm_t rlm_escape;
220  .common = {
221  .magic = MODULE_MAGIC_INIT,
222  .name = "escape",
223  .inst_size = sizeof(rlm_escape_t),
225  .bootstrap = mod_bootstrap
226  }
227 };
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
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
static fr_slen_t in
Definition: dict.h:645
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
static xlat_action_t escape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support.
Definition: rlm_escape.c:64
static xlat_action_t unescape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Equivalent to the old safe_characters functionality in rlm_sql.
Definition: rlm_escape.c:138
#define RPEDEBUG(fmt,...)
Definition: log.h:376
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
unsigned char uint8_t
Definition: merged_model.c:30
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
char const * fr_utf8_strchr(int *out_chr_len, char const *str, ssize_t inlen, char const *chr)
Return a pointer to the first UTF8 char in a string.
Definition: print.c:176
static const conf_parser_t config[]
Definition: base.c:188
static xlat_arg_parser_t const escape_xlat_arg[]
Definition: rlm_escape.c:50
char const * allowed_chars
Definition: rlm_escape.c:40
static xlat_arg_parser_t const unescape_xlat_arg[]
Definition: rlm_escape.c:124
module_rlm_t rlm_escape
Definition: rlm_escape.c:219
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_escape.c:194
static char const hextab[]
Definition: rlm_escape.c:48
static const conf_parser_t module_config[]
Definition: rlm_escape.c:43
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
Trim a talloced sbuff to the minimum length required to represent the contained string.
Definition: sbuff.c:399
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
Definition: sbuff.c:1445
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition: sbuff.c:1554
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_buff(_sbuff_or_marker)
#define fr_sbuff_in_char(_sbuff,...)
Talloc sbuff extension structure.
Definition: sbuff.h:114
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
xlat expansion parsing and evaluation API.
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
#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
int fr_value_box_strtrim(TALLOC_CTX *ctx, fr_value_box_t *vb)
Trim the length of the string buffer to match the length of the C string.
Definition: value.c:3902
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition: value.c:3985
int fr_value_box_bstr_alloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Alloc and assign an empty \0 terminated string to a fr_value_box_t.
Definition: value.c:4020
#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
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:45
An xlat calling ctx.
Definition: xlat_ctx.h:42
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition: xlat_func.c:415
int xlat_func_mono_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the argument of an xlat.
Definition: xlat_func.c:392
xlat_t * xlat_func_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function for a module.
Definition: xlat_func.c:274
@ XLAT_FUNC_FLAG_PURE
Definition: xlat_func.h:38