The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
decode.c
Go to the documentation of this file.
1/*
2 * This program 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
5 * (at 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: 1c64b0e37255898d0b9eedc5fdc577fba9bfb9af $
19 * @file src/protocols/tftp/decode.c
20 * @brief Functions to decode TFTP packets.
21 * @author Jorge Pereira <jpereira@freeradius.org>
22 *
23 * @copyright 2021 The FreeRADIUS server project.
24 * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
25 */
26RCSID("$Id: 1c64b0e37255898d0b9eedc5fdc577fba9bfb9af $")
27
28#include <freeradius-devel/util/udp.h>
29
30#include <freeradius-devel/io/test_point.h>
31
32#include "tftp.h"
33#include "attrs.h"
34
35/*
36 * https://tools.ietf.org/html/rfc1350
37 *
38 * Order of Headers
39 *
40 * 2 bytes
41 * ----------------------------------------------------------
42 * | Local Medium | Internet | Datagram | TFTP Opcode |
43 * ----------------------------------------------------------
44 *
45 * TFTP Formats
46 *
47 * Type Op # Format without header
48 *
49 * 2 bytes string 1 byte string 1 byte
50 * -----------------------------------------------
51 * RRQ/ | 01/02 | Filename | 0 | Mode | 0 |
52 * WRQ -----------------------------------------------
53 * 2 bytes 2 bytes n bytes
54 * ---------------------------------
55 * DATA | 03 | Block # | Data |
56 * ---------------------------------
57 * 2 bytes 2 bytes
58 * -------------------
59 * ACK | 04 | Block # |
60 * --------------------
61 * 2 bytes 2 bytes string 1 byte
62 * ----------------------------------------
63 * ERROR | 05 | ErrorCode | ErrMsg | 0 |
64 * ----------------------------------------
65 *
66 * Initial Connection Protocol for reading a file
67 *
68 * 1. Host A sends a "RRQ" to host B with source= A's TID,
69 * destination= 69.
70 *
71 * 2. Host B sends a "DATA" (with block number= 1) to host A with
72 * source= B's TID, destination= A's TID.
73 */
74int fr_tftp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len)
75{
76 uint8_t const *q, *p, *end;
77 uint16_t opcode;
78 fr_pair_t *vp = NULL;
79
80 if (data_len == 0) return -1;
81
82 if (data_len < FR_TFTP_HDR_LEN) {
83 fr_strerror_printf("TFTP packet is too small. (%zu < %d)", data_len, FR_TFTP_HDR_LEN);
84 return -1;
85 }
86
87 p = data;
88 end = (data + data_len);
89
90 /*
91 * Two bytes of Opcode
92 */
93 opcode = fr_nbo_to_uint16(p);
94 if (!opcode || (opcode > 5)) {
95 fr_strerror_printf("Invalid TFTP opcode %#04x", opcode);
96 return -1;
97 }
98
100 if (!vp) return -1;
101
102 vp->vp_uint16 = opcode;
104 p += 2;
105
106 /*
107 * Parse the data.
108 */
109 switch (opcode) {
110 case FR_OPCODE_VALUE_READ_REQUEST:
111 case FR_OPCODE_VALUE_WRITE_REQUEST:
112 /*
113 * Read / write requests end with a NUL byte.
114 */
115 if (*(end - 1) != '\0') {
116 fr_strerror_const("trailing NUL character is missing");
117 return -1;
118 }
119
120 /*
121 * 2 bytes string 1 byte string 1 byte string 1 byte string 1 byte
122 * +------------------------------------------------------------------------------------+
123 * | Opcode | Filename | 0 | Mode | 0 | blksize | 0 | #blksize | 0 |
124 * +------------------------------------------------------------------------------------+
125 * Figure 5-1: RRQ/WRQ packet
126 */
127
128 /*
129 * Find the end of the filename.
130 */
131 q = memchr(p, '\0', (end - p));
132 if (!q || (q == (end - 1))) {
133 missing_mode:
134 fr_strerror_const("'mode' field is missing");
135 return -1;
136 }
137
138 /*
139 * Sanity check the filename
140 */
141 for (q = p; *q != '\0'; q++) {
142 if (*q < ' ') {
143 fr_strerror_const("Invalid control character in filename");
144 return -1;
145 }
146 }
147
149 if (!vp) return -1;
150
151 fr_pair_value_bstrndup(vp, (const char *) p, (q - p), true);
153 p = q + 1;
154
155 if (p == end) goto missing_mode;
156
157 /*
158 * ascii + NUL
159 * octet + NUL
160 * netascii + NUL
161 */
162 q = memchr(p, '\0', (end - p));
163 if (!q) goto missing_mode;
164
166 if (!vp) return -1;
167
168 /* (netascii || ascii || octet) + \0 */
169 if ((q - p) == 5 && !memcmp(p, "octet", 5)) {
170 p += 6;
171 vp->vp_uint8 = FR_MODE_VALUE_OCTET;
172
173 } else if ((q - p) == 5 && !memcmp(p, "ascii", 5)) {
174 p += 6;
175 vp->vp_uint8 = FR_MODE_VALUE_ASCII;
176
177 } else if ((q - p) == 8 && !memcmp(p, "netascii", 8)) {
178 p += 9;
179 vp->vp_uint8 = FR_MODE_VALUE_ASCII;
180
181 } else {
182 fr_strerror_printf("Invalid mode '%.*s'", (int) (q - p), p);
184 return -1;
185 }
186
188 if (p >= end) goto done;
189
190 /*
191 * "blksize" is optional.
192 *
193 * If it exists, then it's at least
194 *
195 * blksize | \0 | #blksize | \0 |
196 */
197 if ((end - p) < 10) {
198 invalid_option:
199 fr_strerror_const("Invalid TFTP option");
200 return -1;
201 }
202
203 if (!memcmp(p, "blksize\0", 8)) {
204 char *p_end = NULL;
205 long blksize;
206
207 p += 8;
208
209 if ((p >= end) || ((end - p) < 1) || ((end - p) > 6)) goto invalid_option;
210
212 if (!vp) return -1;
213
214 blksize = strtol((const char *)p, &p_end, 10);
215
216 if (((p == (const uint8_t *)p_end)) ||
217 (blksize < FR_TFTP_BLOCK_MIN_SIZE) ||
218 (blksize > FR_TFTP_BLOCK_MAX_SIZE)) {
219 fr_strerror_printf("Invalid Block-Size %ld value", blksize);
221 return -1;
222 }
223
224 vp->vp_uint16 = (uint16_t)blksize;
226 }
227
228 break;
229
230 case FR_OPCODE_VALUE_ACKNOWLEDGEMENT:
231 case FR_OPCODE_VALUE_DATA:
232 /**
233 * 2 bytes 2 bytes
234 * ---------------------
235 * | Opcode | Block # |
236 * ---------------------
237 * Figure 5-3: ACK packet
238 */
239
241 if (!vp) return -1;
242
243 vp->vp_uint16 = fr_nbo_to_uint16(p);
244
246
247 /*
248 * From that point...
249 *
250 * 2 bytes 2 bytes n bytes
251 * ----------------------------------
252 * | Opcode | Block # | Data |
253 * ----------------------------------
254 * Figure 5-2: DATA packet
255 */
256 if (opcode != FR_OPCODE_VALUE_DATA) goto done;
257
258 if ((p + 2) > end) {
259 fr_strerror_const("Malformed Acknowledgment packet");
260 return -1;
261 }
262
263 p += 2;
264
266 if (!vp) return -1;
267
268 fr_pair_value_memdup(vp, p, (end - p), true);
270 break;
271
272 case FR_OPCODE_VALUE_ERROR:
273 /**
274 * 2 bytes 2 bytes string 1 byte
275 * -----------------------------------------
276 * | Opcode | ErrorCode | ErrMsg | 0 |
277 * -----------------------------------------
278 *
279 * Figure 5-4: ERROR packet
280 */
281
282 /*
283 * Error packets end with a NUL byte.
284 */
285 if (*(end - 1) != '\0') {
286 fr_strerror_const("trailing NUL character is missing");
287 return -1;
288 }
289
290 if ((p + 2) >= end) {
291 fr_strerror_const("Malformed Error packet");
292 return -1;
293 }
294
296 if (!vp) return -1;
297
298 vp->vp_uint16 = fr_nbo_to_uint16(p);
300
301 p += 2; /* <ErrorCode> */
302 q = memchr(p, '\0', (end - p));
303 if (!q || q[0] != '\0') {
304 fr_strerror_const("Missing Error-Code");
305 return -1;
306 }
307
309 if (!vp) return -1;
310
311 fr_pair_value_bstrndup(vp, (char const *)p, (q - p), true);
313 break;
314
315 default:
316 fr_strerror_printf("Invalid TFTP opcode %#04x", opcode);
317 return -1;
318 }
319
320done:
321 return data_len;
322}
323
324/**
325 * Used as the decoder ctx.
326 */
327typedef struct {
330
331/*
332 * Test points for protocol decode
333 */
335 uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
336{
337 return fr_tftp_decode(ctx, out, data, data_len);
338}
339
341{
343
344 return 0;
345}
346
347static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
349{
350 fr_tftp_ctx_t *test_ctx;
351
352 if (fr_tftp_global_init() < 0) return -1;
353
354 test_ctx = talloc_zero(ctx, fr_tftp_ctx_t);
355 if (!test_ctx) return -1;
356
357 talloc_set_destructor(test_ctx, _decode_test_ctx);
358
359 *out = test_ctx;
360
361 return 0;
362}
363
#define RCSID(id)
Definition build.h:512
#define UNUSED
Definition build.h:336
fr_dict_attr_t const * root_da
Definition common.c:32
talloc_free(hp)
unsigned short uint16_t
long int ssize_t
unsigned char uint8_t
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
Definition nbo.h:146
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition pair.c:2962
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1352
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:290
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2812
static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict, UNUSED fr_dict_attr_t const *root_da)
Definition decode.c:102
HIDDEN fr_dict_attr_t const * attr_tftp_error_code
Definition base.c:47
HIDDEN fr_dict_attr_t const * attr_tftp_filename
Definition base.c:49
HIDDEN fr_dict_attr_t const * attr_tftp_block_size
Definition base.c:45
HIDDEN fr_dict_attr_t const * attr_tftp_opcode
Definition base.c:50
HIDDEN fr_dict_attr_t const * attr_tftp_mode
Definition base.c:51
HIDDEN fr_dict_attr_t const * attr_tftp_data
Definition base.c:46
HIDDEN fr_dict_attr_t const * attr_tftp_block
Definition base.c:44
HIDDEN fr_dict_attr_t const * attr_tftp_error_message
Definition base.c:48
int fr_tftp_global_init(void)
Definition base.c:90
void fr_tftp_global_free(void)
Definition base.c:114
fr_test_point_proto_decode_t tftp_tp_decode_proto
Definition decode.c:365
static ssize_t fr_tftp_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
Definition decode.c:334
int fr_tftp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len)
Definition decode.c:74
static int _decode_test_ctx(UNUSED fr_tftp_ctx_t *proto_ctx)
Definition decode.c:340
Used as the decoder ctx.
Definition decode.c:327
VQP attributes.
static bool done
Definition radclient.c:80
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:68
Entry point for protocol decoders.
Definition test_point.h:67
Functions to encode/decode TFTP packets.
#define FR_TFTP_BLOCK_MAX_SIZE
Definition tftp.h:73
#define FR_TFTP_HDR_LEN
Definition tftp.h:41
#define FR_TFTP_BLOCK_MIN_SIZE
Definition tftp.h:72
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1340
static size_t char ** out
Definition value.h:1030