/src/freeradius-server/src/protocols/internal/encode.c
Line | Count | Source |
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: 2a61464a5cd138dabce07297168318e2f3bdfaa0 $ |
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 | | |
35 | | static fr_internal_encode_ctx_t default_encode_ctx = { }; |
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 | | */ |
52 | | static ssize_t internal_encode(fr_dbuff_t *dbuff, |
53 | | fr_da_stack_t *da_stack, unsigned int depth, |
54 | | fr_dcursor_t *cursor, void *encode_ctx) |
55 | 0 | { |
56 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
57 | 0 | fr_dbuff_marker_t enc_field, len_field, value_field; |
58 | 0 | fr_dbuff_t value_dbuff; |
59 | 0 | fr_dict_attr_t const *da = da_stack->da[depth]; |
60 | 0 | fr_pair_t *vp = fr_dcursor_current(cursor); |
61 | 0 | bool unknown = false, internal = false; |
62 | |
|
63 | 0 | ssize_t slen; |
64 | 0 | size_t flen, vlen, mlen; |
65 | |
|
66 | 0 | uint8_t buff[sizeof(uint64_t)]; |
67 | 0 | uint8_t enc_byte = 0; |
68 | 0 | fr_internal_encode_ctx_t *our_encode_ctx = encode_ctx; |
69 | |
|
70 | 0 | 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 | 0 | if (!our_encode_ctx->allow_name_only && vp->da->flags.name_only) { |
77 | 0 | fr_dcursor_next(cursor); |
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
82 | |
|
83 | 0 | fr_dbuff_marker(&enc_field, &work_dbuff); |
84 | | |
85 | | /* |
86 | | * Advance past first encoding byte |
87 | | */ |
88 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, 0x00); |
89 | | |
90 | 0 | switch (vp->vp_type) { |
91 | | /* |
92 | | * Only leaf attributes can be tainted |
93 | | */ |
94 | 0 | case FR_TYPE_LEAF: |
95 | 0 | if (vp->vp_tainted) enc_byte |= FR_INTERNAL_FLAG_TAINTED; |
96 | 0 | break; |
97 | | |
98 | 0 | default: |
99 | 0 | break; |
100 | 0 | } |
101 | | |
102 | | /* |
103 | | * Need to use the second encoding byte |
104 | | * |
105 | | * 0 1 |
106 | | * 0 1 2 3 4 5 6 7 8 9 0 |
107 | | * +-+-+-+-+-+-+-+-+-+-+ |
108 | | * |u|i|-|-|-|-|-|e| |
109 | | * +-+-+-+-+-+-+-+-+-+-+ |
110 | | */ |
111 | 0 | if ((unknown = da->flags.is_unknown) || |
112 | 0 | (internal = (da->parent == fr_dict_root(fr_dict_internal())))) { |
113 | 0 | enc_byte |= FR_INTERNAL_FLAG_EXTENDED; |
114 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, |
115 | 0 | (unknown * FR_INTERNAL_FLAG_UNKNOWN) | |
116 | 0 | (internal * FR_INTERNAL_FLAG_INTERNAL)); |
117 | 0 | } |
118 | | |
119 | | /* |
120 | | * Encode the type and write the width of the |
121 | | * integer to the encoding byte. |
122 | | */ |
123 | 0 | flen = fr_dbuff_in_uint64v(&work_dbuff, da->attr); |
124 | 0 | if (flen <= 0) return flen; |
125 | 0 | enc_byte |= ((flen - 1) << 5); |
126 | | |
127 | | /* |
128 | | * Leave one byte in hopes that the length will fit |
129 | | * so we needn't move the encoded data. |
130 | | */ |
131 | 0 | fr_dbuff_marker(&len_field, &work_dbuff); |
132 | 0 | FR_DBUFF_ADVANCE_RETURN(&work_dbuff, 1); |
133 | | |
134 | | /* |
135 | | * Create dbuff to hold encoded data--the fr_dbuff_move() done |
136 | | * if the length field needs more than one byte will guard |
137 | | * against insufficient space. |
138 | | */ |
139 | 0 | value_dbuff = FR_DBUFF_BIND_CURRENT(&work_dbuff); |
140 | 0 | fr_dbuff_marker(&value_field, &value_dbuff); |
141 | |
|
142 | 0 | switch (da->type) { |
143 | 0 | case FR_TYPE_LEAF: |
144 | 0 | slen = fr_value_box_to_network(&value_dbuff, &vp->data); |
145 | 0 | if (slen < 0) return PAIR_ENCODE_FATAL_ERROR; |
146 | 0 | fr_dcursor_next(cursor); |
147 | 0 | break; |
148 | | |
149 | | /* |
150 | | * This is the vendor container. |
151 | | * For RADIUS it'd be something like attr 26. |
152 | | * |
153 | | * Inside the VSA you then have the vendor |
154 | | * which is just encoded as another TLVish |
155 | | * type attribute. |
156 | | * |
157 | | * For small vendor PENs <= 255 this |
158 | | * encoding is 6 bytes, the same as RADIUS. |
159 | | * |
160 | | * For larger vendor PENs it's more bytes |
161 | | * but we really don't care. |
162 | | */ |
163 | 0 | case FR_TYPE_VSA: |
164 | 0 | case FR_TYPE_VENDOR: |
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 | 0 | case FR_TYPE_TLV: |
175 | 0 | case FR_TYPE_STRUCT: |
176 | | /* |
177 | | * We've done the complete stack. |
178 | | * Hopefully this TLV has some |
179 | | * children to encode... |
180 | | */ |
181 | 0 | if (da == vp->da) { |
182 | 0 | fr_dcursor_t children; |
183 | 0 | fr_pair_t *child; |
184 | |
|
185 | 0 | for (child = fr_pair_dcursor_init(&children, &vp->vp_group); |
186 | 0 | child; |
187 | 0 | child = fr_dcursor_current(&children)) { |
188 | |
|
189 | 0 | FR_PROTO_TRACE("encode ctx changed %s -> %s", da->name, child->da->name); |
190 | |
|
191 | 0 | fr_proto_da_stack_build_partial(da_stack, da_stack->da[depth], child->da); |
192 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
193 | |
|
194 | 0 | slen = internal_encode(&value_dbuff, da_stack, depth + 1, &children, encode_ctx); |
195 | 0 | if (slen < 0) return slen; |
196 | 0 | } |
197 | 0 | fr_dcursor_next(cursor); |
198 | 0 | break; |
199 | 0 | } |
200 | | |
201 | | /* |
202 | | * Still encoding intermediary TLVs... |
203 | | */ |
204 | 0 | slen = internal_encode(&value_dbuff, da_stack, depth + 1, cursor, encode_ctx); |
205 | 0 | if (slen < 0) return slen; |
206 | 0 | 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 | 0 | case FR_TYPE_GROUP: |
217 | 0 | { |
218 | 0 | fr_dcursor_t children; |
219 | 0 | fr_pair_t *child; |
220 | |
|
221 | 0 | for (child = fr_pair_dcursor_init(&children, &vp->vp_group); |
222 | 0 | child; |
223 | 0 | child = fr_dcursor_current(&children)) { |
224 | 0 | FR_PROTO_TRACE("encode ctx changed %s -> %s", da->name, child->da->name); |
225 | |
|
226 | 0 | slen = fr_internal_encode_pair(&value_dbuff, &children, encode_ctx); |
227 | 0 | if (slen < 0) return slen; |
228 | 0 | } |
229 | 0 | fr_dcursor_next(cursor); |
230 | 0 | } |
231 | 0 | break; |
232 | | |
233 | 0 | default: |
234 | 0 | fr_strerror_printf("%s: Unexpected attribute type \"%s\"", |
235 | 0 | __FUNCTION__, fr_type_to_str(da->type)); |
236 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
237 | 0 | } |
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 | 0 | vlen = fr_dbuff_used(&value_dbuff); |
247 | 0 | 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 | 0 | if (flen > 1) { |
253 | 0 | fr_dbuff_advance(&value_field, flen - 1); |
254 | 0 | fr_dbuff_set_to_start(&value_dbuff); |
255 | 0 | mlen = fr_dbuff_move(&value_field, &value_dbuff, vlen); |
256 | 0 | if (mlen < vlen) return -(vlen - mlen); |
257 | 0 | } |
258 | | |
259 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&len_field, buff, flen); |
260 | 0 | enc_byte |= ((flen - 1) << 2); |
261 | 0 | FR_DBUFF_IN_RETURN(&enc_field, enc_byte); |
262 | | |
263 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff) - vlen, "header"); |
264 | |
|
265 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&value_dbuff), vlen, "value %s", |
266 | 0 | fr_type_to_str(vp->vp_type)); |
267 | |
|
268 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
269 | 0 | } |
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 | | */ |
281 | | ssize_t fr_internal_encode_pair(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx) |
282 | 0 | { |
283 | 0 | fr_pair_t *vp; |
284 | 0 | fr_da_stack_t da_stack; |
285 | |
|
286 | 0 | vp = fr_dcursor_current(cursor); |
287 | 0 | if (!vp) return 0; |
288 | | |
289 | 0 | fr_proto_da_stack_build(&da_stack, vp->da); |
290 | |
|
291 | 0 | return internal_encode(dbuff, &da_stack, 0, cursor, encode_ctx); |
292 | 0 | } |
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 | | */ |
303 | | ssize_t fr_internal_encode_list(fr_dbuff_t *dbuff, fr_pair_list_t const *list, void *encode_ctx) |
304 | 0 | { |
305 | 0 | fr_pair_t *vp; |
306 | 0 | fr_dcursor_t dcursor; |
307 | 0 | ssize_t ret = 0, len = 0; |
308 | 0 | fr_da_stack_t da_stack; |
309 | |
|
310 | 0 | for (vp = fr_pair_dcursor_init(&dcursor, list); |
311 | 0 | vp; |
312 | 0 | vp = fr_dcursor_current(&dcursor)) { |
313 | 0 | fr_proto_da_stack_build(&da_stack, vp->da); |
314 | 0 | ret = internal_encode(dbuff, &da_stack, 0, &dcursor, encode_ctx); |
315 | 0 | if (ret < 0) return ret; |
316 | 0 | len += ret; |
317 | 0 | } |
318 | | |
319 | 0 | return len; |
320 | 0 | } |
321 | | |
322 | | /* |
323 | | * Test points |
324 | | */ |
325 | | extern fr_test_point_pair_encode_t internal_tp_encode_pair; |
326 | | fr_test_point_pair_encode_t internal_tp_encode_pair = { |
327 | | .test_ctx = NULL, |
328 | | .func = fr_internal_encode_pair |
329 | | }; |