The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
encode.c
Go to the documentation of this file.
1 /*
2  * This library is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  * This library 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 GNU
10  * Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public
13  * License along with this library; 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: bb86a3967066bfb6b689cddd3a3e97e5eec5fc10 $
19  *
20  * Because what we need is yet *ANOTHER* serialisation scheme.
21  *
22  * @file protocols/internal/encode.c
23  * @brief Functions to encode data in our internal structure.
24  *
25  * @copyright 2020 The FreeRADIUS server project
26  * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
27  */
28 #include <freeradius-devel/internal/internal.h>
29 #include <freeradius-devel/io/pair.h>
30 #include <freeradius-devel/io/test_point.h>
31 #include <freeradius-devel/util/net.h>
32 #include <freeradius-devel/util/proto.h>
33 
34 
36 
37 /** We use the same header for all types
38  *
39  */
40 
41 /** Encode the value of the value pair the cursor currently points at.
42  *
43  * @param dbuff data buffer to place the encoded data in
44  * @param da_stack da stack corresponding to the value pair
45  * @param depth in da_stack
46  * @param cursor cursor whose current value is the one to be encoded
47  * @param encode_ctx encoder context
48  *
49  * @return either a negative number, indicating an error
50  * or the number of bytes used to encode the value
51  */
53  fr_da_stack_t *da_stack, unsigned int depth,
54  fr_dcursor_t *cursor, void *encode_ctx)
55 {
56  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
57  fr_dbuff_marker_t enc_field, len_field, value_field;
58  fr_dbuff_t value_dbuff;
59  fr_dict_attr_t const *da = da_stack->da[depth];
60  fr_pair_t *vp = fr_dcursor_current(cursor);
61  bool unknown = false, internal = false;
62 
63  ssize_t slen;
64  size_t flen, vlen, mlen;
65 
66  uint8_t buff[sizeof(uint64_t)];
67  uint8_t enc_byte = 0;
68  fr_internal_encode_ctx_t *our_encode_ctx = encode_ctx;
69 
70  if (!our_encode_ctx) our_encode_ctx = &default_encode_ctx;
71 
72  /*
73  * Silently skip name only attributes if we're writing
74  * to a database or cache.
75  */
76  if (!our_encode_ctx->allow_name_only && vp->da->flags.name_only) return 0;
77 
78  FR_PROTO_STACK_PRINT(da_stack, depth);
79 
80  fr_dbuff_marker(&enc_field, &work_dbuff);
81 
82  /*
83  * Advance past first encoding byte
84  */
85  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, 0x00);
86 
87  switch (vp->vp_type) {
88  /*
89  * Only leaf attributes can be tainted
90  */
91  case FR_TYPE_LEAF:
92  if (vp->vp_tainted) enc_byte |= FR_INTERNAL_FLAG_TAINTED;
93  break;
94 
95  default:
96  break;
97  }
98 
99  /*
100  * Need to use the second encoding byte
101  *
102  * 0 1
103  * 0 1 2 3 4 5 6 7 8 9 0
104  * +-+-+-+-+-+-+-+-+-+-+
105  * |u|i|-|-|-|-|-|e|
106  * +-+-+-+-+-+-+-+-+-+-+
107  */
108  if ((unknown = da->flags.is_unknown) ||
109  (internal = (da->parent == fr_dict_root(fr_dict_internal())))) {
110  enc_byte |= FR_INTERNAL_FLAG_EXTENDED;
111  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff,
112  (unknown * FR_INTERNAL_FLAG_UNKNOWN) |
113  (internal * FR_INTERNAL_FLAG_INTERNAL));
114  }
115 
116  /*
117  * Encode the type and write the width of the
118  * integer to the encoding byte.
119  */
120  flen = fr_dbuff_in_uint64v(&work_dbuff, da->attr);
121  if (flen <= 0) return flen;
122  enc_byte |= ((flen - 1) << 5);
123 
124  /*
125  * Leave one byte in hopes that the length will fit
126  * so we needn't move the encoded data.
127  */
128  fr_dbuff_marker(&len_field, &work_dbuff);
129  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, 1);
130 
131  /*
132  * Create dbuff to hold encoded data--the fr_dbuff_move() done
133  * if the length field needs more than one byte will guard
134  * against insufficient space.
135  */
136  value_dbuff = FR_DBUFF_BIND_CURRENT(&work_dbuff);
137  fr_dbuff_marker(&value_field, &value_dbuff);
138 
139  switch (vp->vp_type) {
140  case FR_TYPE_LEAF:
141  slen = fr_value_box_to_network(&value_dbuff, &vp->data);
142  if (slen < 0) return PAIR_ENCODE_FATAL_ERROR;
143  fr_dcursor_next(cursor);
144  break;
145 
146  /*
147  * This is the vendor container.
148  * For RADIUS it'd be something like attr 26.
149  *
150  * Inside the VSA you then have the vendor
151  * which is just encoded as another TLVish
152  * type attribute.
153  *
154  * For small vendor PENs <= 255 this
155  * encoding is 6 bytes, the same as RADIUS.
156  *
157  * For larger vendor PENs it's more bytes
158  * but we really don't care.
159  */
160  case FR_TYPE_VSA:
161  case FR_TYPE_VENDOR:
162  slen = internal_encode(&value_dbuff, da_stack, depth + 1, cursor, encode_ctx);
163  if (slen < 0) return slen;
164  break;
165 
166  /*
167  * Children of TLVs are encoded in the context
168  * of the TLV.
169  *
170  * STRUCTs are encoded as TLVs, because the struct
171  * packing only applies to the original protocol, and not
172  * to our internal encoding.
173  */
174  case FR_TYPE_TLV:
175  case FR_TYPE_STRUCT:
176  /*
177  * We've done the complete stack.
178  * Hopefully this TLV has some
179  * children to encode...
180  */
181  if (da == vp->da) {
182  fr_dcursor_t children;
183  fr_pair_t *child;
184 
185  for (child = fr_pair_dcursor_init(&children, &vp->vp_group);
186  child;
187  child = fr_dcursor_current(&children)) {
188 
189  FR_PROTO_TRACE("encode ctx changed %s -> %s", da->name, child->da->name);
190 
191  fr_proto_da_stack_build_partial(da_stack, da_stack->da[depth], child->da);
192  FR_PROTO_STACK_PRINT(da_stack, depth);
193 
194  slen = internal_encode(&value_dbuff, da_stack, depth + 1, &children, encode_ctx);
195  if (slen < 0) return slen;
196  }
197  fr_dcursor_next(cursor);
198  break;
199  }
200 
201  /*
202  * Still encoding intermediary TLVs...
203  */
204  slen = internal_encode(&value_dbuff, da_stack, depth + 1, cursor, encode_ctx);
205  if (slen < 0) return slen;
206  break;
207 
208  /*
209  * Each child of a group encodes from the
210  * dictionary root to the leaf da.
211  *
212  * Re-enter the encoder at the start.
213  * We do this, because the child may
214  * have a completely different da_stack.
215  */
216  case FR_TYPE_GROUP:
217  {
218  fr_dcursor_t children;
219  fr_pair_t *child;
220 
221  for (child = fr_pair_dcursor_init(&children, &vp->vp_group);
222  child;
223  child = fr_dcursor_current(&children)) {
224  FR_PROTO_TRACE("encode ctx changed %s -> %s", da->name, child->da->name);
225 
226  slen = fr_internal_encode_pair(&value_dbuff, &children, encode_ctx);
227  if (slen < 0) return slen;
228  }
229  fr_dcursor_next(cursor);
230  }
231  break;
232 
233  default:
234  fr_strerror_printf("%s: Unexpected attribute type \"%s\"",
235  __FUNCTION__, fr_type_to_str(da->type));
237  }
238 
239  /*
240  * Encode the total length, and write the width
241  * of the integer to the encoding byte.
242  *
243  * Already did length checks at the start of
244  * the function.
245  */
246  vlen = fr_dbuff_used(&value_dbuff);
247  flen = (ssize_t) fr_nbo_from_uint64v(buff, vlen);
248 
249  /*
250  * Ugh, it's a long one, need to move the data.
251  */
252  if (flen > 1) {
253  fr_dbuff_advance(&value_field, flen - 1);
254  fr_dbuff_set_to_start(&value_dbuff);
255  mlen = fr_dbuff_move(&value_field, &value_dbuff, vlen);
256  if (mlen < vlen) return -(vlen - mlen);
257  }
258 
259  FR_DBUFF_IN_MEMCPY_RETURN(&len_field, buff, flen);
260  enc_byte |= ((flen - 1) << 2);
261  FR_DBUFF_IN_RETURN(&enc_field, enc_byte);
262 
263  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff) - vlen, "header");
264 
265  FR_PROTO_HEX_DUMP(fr_dbuff_start(&value_dbuff), vlen, "value %s",
266  fr_type_to_str(vp->vp_type));
267 
268  return fr_dbuff_set(dbuff, &work_dbuff);
269 }
270 
271 /** Encode a data structure into an internal attribute
272  *
273  * @param[in,out] dbuff Where to write encoded data and how much one can write.
274  * @param[in] cursor Specifying attribute to encode.
275  * @param[in] encode_ctx Additional data such as the shared secret to use.
276  * @return
277  * - >0 The number of bytes written to out.
278  * - 0 Nothing to encode (or attribute skipped).
279  * - <0 an error occurred.
280  */
282 {
283  fr_pair_t *vp;
284  fr_da_stack_t da_stack;
285 
286  vp = fr_dcursor_current(cursor);
287  if (!vp) return 0;
288 
289  fr_proto_da_stack_build(&da_stack, vp->da);
290 
291  return internal_encode(dbuff, &da_stack, 0, cursor, encode_ctx);
292 }
293 
294 /** Encode a list of pairs using the internal encoder
295  *
296  * @param[out] dbuff Where to write encoded data.
297  * @param[in] list List of attributes to encode.
298  * @param[in] encode_ctx Additional data to be used by the encoder.
299  * @return
300  * - length of encoded data on success
301  * - < 0 on failure
302  */
304 {
305  fr_pair_t *vp;
306  fr_dcursor_t dcursor;
307  ssize_t ret = 0, len = 0;
308  fr_da_stack_t da_stack;
309 
310  for (vp = fr_pair_dcursor_init(&dcursor, list);
311  vp;
312  vp = fr_dcursor_current(&dcursor)) {
313  fr_proto_da_stack_build(&da_stack, vp->da);
314  ret = internal_encode(dbuff, &da_stack, 0, &dcursor, encode_ctx);
315  if (ret < 0) return ret;
316  len += ret;
317  }
318 
319  return len;
320 }
321 
322 /*
323  * Test points
324  */
327  .test_ctx = NULL,
329 };
#define fr_dbuff_advance(_dbuff_or_marker, _len)
Advance 'current' position in dbuff or marker by _len bytes.
Definition: dbuff.h:1067
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition: dbuff.h:762
#define FR_DBUFF_ADVANCE_RETURN(_dbuff_or_marker, _len)
Advance the 'current' position in dbuff or marker by _len bytes returning if _len is out of range.
Definition: dbuff.h:1083
struct fr_dbuff_marker_s fr_dbuff_marker_t
A position marker associated with a dbuff.
Definition: dbuff.h:81
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition: dbuff.h:893
#define fr_dbuff_set_to_start(_dbuff_or_marker)
Reset the 'current' position of the dbuff or marker to the 'start' of the buffer.
Definition: dbuff.h:1150
#define FR_DBUFF_BIND_CURRENT(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:240
#define fr_dbuff_in_uint64v(_dbuff_or_marker, _num)
Copy an integer value into a dbuff or marker using our internal variable length encoding.
Definition: dbuff.h:1604
#define FR_DBUFF_IN_MEMCPY_RETURN(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1377
#define FR_DBUFF_IN_RETURN(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff returning if there is insufficient space.
Definition: dbuff.h:1580
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:222
static uint8_t * fr_dbuff_marker(fr_dbuff_marker_t *m, fr_dbuff_t *dbuff)
Initialises a new marker pointing to the 'current' position of the dbuff.
Definition: dbuff.h:1187
#define fr_dbuff_move(_out, _in, _len)
Copy in as many bytes as possible from one dbuff or marker to another.
Definition: dbuff.h:1648
#define FR_DBUFF_IN_BYTES_RETURN(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1467
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
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
fr_dict_t const * fr_dict_internal(void)
Definition: dict_util.c:4204
#define FR_INTERNAL_FLAG_INTERNAL
Definition: internal.h:33
bool allow_name_only
Allow name only pairs.
Definition: internal.h:36
#define FR_INTERNAL_FLAG_EXTENDED
Definition: internal.h:26
#define FR_INTERNAL_FLAG_UNKNOWN
Definition: internal.h:32
#define FR_INTERNAL_FLAG_TAINTED
Definition: internal.h:27
#define PAIR_ENCODE_FATAL_ERROR
Fatal encoding error.
Definition: pair.h:37
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
Definition: merged_model.c:119
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
Definition: merged_model.c:122
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
Definition: merged_model.c:121
@ FR_TYPE_GROUP
A grouping of other attributes.
Definition: merged_model.c:124
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
static size_t fr_nbo_from_uint64v(uint8_t out[static sizeof(uint64_t)], uint64_t num)
Write out an unsigned 64bit integer in wire format using the fewest bytes possible.
Definition: nbo.h:118
void fr_proto_da_stack_build(fr_da_stack_t *stack, fr_dict_attr_t const *da)
Build a complete DA stack from the da back to the root.
Definition: proto.c:118
void fr_proto_da_stack_build_partial(fr_da_stack_t *stack, fr_dict_attr_t const *parent, fr_dict_attr_t const *da)
Complete the DA stack for a child attribute.
Definition: proto.c:159
static fr_internal_encode_ctx_t encode_ctx
ssize_t fr_internal_encode_pair(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx)
Encode a data structure into an internal attribute.
Definition: encode.c:281
ssize_t fr_internal_encode_list(fr_dbuff_t *dbuff, fr_pair_list_t const *list, void *encode_ctx)
Encode a list of pairs using the internal encoder.
Definition: encode.c:303
fr_test_point_pair_encode_t internal_tp_encode_pair
Definition: encode.c:326
static ssize_t internal_encode(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
We use the same header for all types.
Definition: encode.c:52
static fr_internal_encode_ctx_t default_encode_ctx
Definition: encode.c:35
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:92
Entry point for pair encoders.
Definition: test_point.h:91
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:590
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition: proto.h:41
#define FR_PROTO_TRACE(_fmt,...)
Definition: proto.h:40
#define FR_PROTO_STACK_PRINT(_stack, _depth)
Definition: proto.h:43
fr_dict_attr_t const * da[FR_DICT_MAX_TLV_STACK+1]
The stack.
Definition: proto.h:56
Structure for holding the stack of dictionary attributes being encoded.
Definition: proto.h:54
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
#define FR_TYPE_LEAF
Definition: types.h:297
return fr_dbuff_set(dbuff, &our_dbuff)
ssize_t fr_value_box_to_network(fr_dbuff_t *dbuff, fr_value_box_t const *value)
Encode a single value box, serializing its contents in generic network format.
Definition: value.c:1359