All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_unpack.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: 528c275eb46dbaa37217fad5ef400d2e7eeae531 $
19  * @file rlm_unpack.c
20  * @brief Unpack binary data
21  *
22  * @copyright 2014 The FreeRADIUS server project
23  * @copyright 2014 Alan DeKok <aland@freeradius.org>
24  */
25 RCSID("$Id: 528c275eb46dbaa37217fad5ef400d2e7eeae531 $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <ctype.h>
30 
31 #define PW_CAST_BASE (1850)
32 
33 #define GOTO_ERROR do { REDEBUG("Unexpected text at '%s'", p); goto error;} while (0)
34 
35 /** Unpack data
36  *
37  * Example: %{unpack:&Class 0 integer}
38  *
39  * Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer".
40  */
41 static ssize_t unpack_xlat(char **out, size_t outlen,
42  UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
43  REQUEST *request, char const *fmt)
44 {
45  char *data_name, *data_size, *data_type;
46  char *p;
47  size_t len, input_len;
48  int offset;
49  PW_TYPE type;
50  fr_dict_attr_t const *da;
51  VALUE_PAIR *vp, *cast;
52  uint8_t const *input;
53  char buffer[256];
54  uint8_t blob[256];
55 
56  /*
57  * FIXME: copy only the fields here, as we parse them.
58  */
59  strlcpy(buffer, fmt, sizeof(buffer));
60 
61  p = buffer;
62  while (isspace((int) *p)) p++; /* skip leading spaces */
63 
64  data_name = p;
65 
66  while (*p && !isspace((int) *p)) p++;
67 
68  if (!*p) {
69  error:
70  REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'");
71  nothing:
72  return -1;
73  }
74 
75  while (isspace((int) *p)) *(p++) = '\0';
76  if (!*p) GOTO_ERROR;
77 
78  data_size = p;
79 
80  while (*p && !isspace((int) *p)) p++;
81  if (!*p) GOTO_ERROR;
82 
83  while (isspace((int) *p)) *(p++) = '\0';
84  if (!*p) GOTO_ERROR;
85 
86  data_type = p;
87 
88  while (*p && !isspace((int) *p)) p++;
89  if (*p) GOTO_ERROR; /* anything after the type is an error */
90 
91  /*
92  * Attribute reference
93  */
94  if (*data_name == '&') {
95  if (radius_get_vp(&vp, request, data_name) < 0) goto nothing;
96 
97  if ((vp->da->type != PW_TYPE_OCTETS) &&
98  (vp->da->type != PW_TYPE_STRING)) {
99  REDEBUG("unpack requires the input attribute to be 'string' or 'octets'");
100  goto nothing;
101  }
102  input = vp->vp_octets;
103  input_len = vp->vp_length;
104 
105  } else if ((data_name[0] == '0') && (data_name[1] == 'x')) {
106  /*
107  * Hex data.
108  */
109  len = strlen(data_name + 2);
110  if ((len & 0x01) != 0) {
111  RDEBUG("Invalid hex string in '%s'", data_name);
112  goto nothing;
113  }
114  input = blob;
115  input_len = fr_hex2bin(blob, sizeof(blob), data_name + 2, len);
116 
117  } else {
118  GOTO_ERROR;
119  }
120 
121  offset = (int) strtoul(data_size, &p, 10);
122  if (*p) {
123  REDEBUG("unpack requires a decimal number, not '%s'", data_size);
124  goto nothing;
125  }
126 
127  type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID);
128  if (type == PW_TYPE_INVALID) {
129  REDEBUG("Invalid data type '%s'", data_type);
130  goto nothing;
131  }
132 
133  /*
134  * Output must be a non-zero limited size.
135  */
136  if ((dict_attr_sizes[type][0] == 0) ||
137  (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) {
138  REDEBUG("unpack requires fixed-size output type, not '%s'", data_type);
139  goto nothing;
140  }
141 
142  if (input_len < (offset + dict_attr_sizes[type][0])) {
143  REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name);
144  goto nothing;
145  }
146 
147  da = fr_dict_attr_by_num(NULL, 0, PW_CAST_BASE + type);
148  if (!da) {
149  REDEBUG("Cannot decode type '%s'", data_type);
150  goto nothing;
151  }
152 
153  cast = fr_pair_afrom_da(request, da);
154  if (!cast) goto nothing;
155 
156  memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]);
157  cast->vp_length = dict_attr_sizes[type][0];
158 
159  /*
160  * Hacks
161  */
162  switch (type) {
163  case PW_TYPE_SIGNED:
164  case PW_TYPE_INTEGER:
165  case PW_TYPE_DATE:
166  cast->vp_integer = ntohl(cast->vp_integer);
167  break;
168 
169  case PW_TYPE_SHORT:
170  cast->vp_short = ((input[offset] << 8) | input[offset + 1]);
171  break;
172 
173  case PW_TYPE_INTEGER64:
174  cast->vp_integer64 = ntohll(cast->vp_integer64);
175  break;
176 
177  default:
178  break;
179  }
180 
181  len = fr_pair_value_snprint(*out, outlen, cast, 0);
182  talloc_free(cast);
183  if (is_truncated(len, outlen)) {
184  REDEBUG("Insufficient buffer space to unpack data");
185  goto nothing;
186  }
187 
188  return len;
189 }
190 
191 
192 /*
193  * Register the xlats
194  */
195 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
196 {
197  if (cf_section_name2(conf)) return 0;
198 
199  xlat_register(instance, "unpack", unpack_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN);
200 
201  return 0;
202 }
203 
204 /*
205  * The module name should be the only globally exported symbol.
206  * That is, everything else should be 'static'.
207  *
208  * If the module needs to temporarily modify it's instantiation
209  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
210  * The server will then take care of ensuring that the module
211  * is single-threaded.
212  */
213 extern module_t rlm_unpack;
214 module_t rlm_unpack = {
216  .name = "unpack",
217  .type = RLM_TYPE_THREAD_SAFE,
218  .bootstrap = mod_bootstrap
219 };
static ssize_t unpack_xlat(char **out, size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt)
Unpack data.
Definition: rlm_unpack.c:41
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
Metadata exported by the module.
Definition: modules.h:134
Dictionary attribute.
Definition: dict.h:77
#define PW_CAST_BASE
Definition: rlm_unpack.c:31
32 Bit signed integer.
Definition: radius.h:45
const size_t dict_attr_sizes[PW_TYPE_MAX][2]
Map data types to min / max data sizes.
Definition: dict.c:119
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
void size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
Print the value of an attribute to a string.
Definition: pair.c:2107
#define XLAT_DEFAULT_BUF_LEN
Definition: xlat.h:89
#define is_truncated(_ret, _max)
Definition: libradius.h:204
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
const FR_NAME_NUMBER dict_attr_types[]
Map data types to names representing those types.
Definition: dict.c:85
size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
Convert hex strings to binary data.
Definition: misc.c:220
void void int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
Return a VP from the specified request.
Definition: pair.c:815
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
Invalid (uninitialised) attribute type.
Definition: radius.h:32
#define GOTO_ERROR
Definition: rlm_unpack.c:33
32 Bit unsigned integer.
Definition: radius.h:34
static rs_t * conf
Definition: radsniff.c:46
64 Bit unsigned integer.
Definition: radius.h:51
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
32 Bit Unix timestamp.
Definition: radius.h:36
#define REDEBUG(fmt,...)
Definition: log.h:254
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
VALUE_PAIR * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute.
Definition: pair.c:58
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
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
PW_TYPE type
Value type.
Definition: dict.h:80
#define RCSID(id)
Definition: build.h:135
static int mod_bootstrap(CONF_SECTION *conf, void *instance)
Definition: rlm_unpack.c:195
#define RDEBUG(fmt,...)
Definition: log.h:243
16 Bit unsigned integer.
Definition: radius.h:43
Raw octets.
Definition: radius.h:38
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
PW_TYPE
Internal data types used within libfreeradius.
Definition: radius.h:31
module_t rlm_unpack
Definition: rlm_unpack.c:214
value_data_t data
Definition: pair.h:133