The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 23a8e9734a3cae9418e16f02985468f4f7798612 $
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: 23a8e9734a3cae9418e16f02985468f4f7798612 $")
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 error:
85 return -1;
86 }
87
88 p = data;
89 end = (data + data_len);
90
91 /* Opcode */
92 opcode = fr_nbo_to_uint16(p);
94 if (!vp) goto error;
95
96 vp->vp_uint16 = opcode;
98 p += 2;
99
100 switch (opcode) {
101 case FR_OPCODE_VALUE_READ_REQUEST:
102 case FR_OPCODE_VALUE_WRITE_REQUEST:
103 /*
104 * 2 bytes string 1 byte string 1 byte string 1 byte string 1 byte
105 * +------------------------------------------------------------------------------------+
106 * | Opcode | Filename | 0 | Mode | 0 | blksize | 0 | #blksize | 0 |
107 * +------------------------------------------------------------------------------------+
108 * Figure 5-1: RRQ/WRQ packet
109 */
110
111 /* first of all, here we should have always a '\0' */
112 if (*(end - 1) != '\0') goto error_malformed;
113
114 /* first character should be alpha numeric */
115 if (!isalnum(p[0])) {
116 fr_strerror_printf("Invalid Filename");
117 goto error;
118 }
119
120 /* <filename> */
121 q = memchr(p, '\0', (end - p));
122 if (!(q && q[0] == '\0')) {
123 error_malformed:
124 fr_strerror_printf("Packet contains malformed attribute");
125 return -1;
126 }
127
129 if (!vp) goto error;
130
131 fr_pair_value_bstrndup(vp, (char const *)p, (q - p), true);
133 p += (q - p) + 1 /* \0 */;
134
135 /* <mode> */
136 q = memchr(p, '\0', (end - p));
137 if (!(q && q[0] == '\0')) goto error_malformed;
138
140 if (!vp) goto error;
141
142 /* (netascii || ascii || octet) + \0 */
143 if ((q - p) == 5 && !memcmp(p, "octet", 5)) {
144 p += 5;
145 vp->vp_uint8 = FR_MODE_VALUE_OCTET;
146 } else if ((q - p) == 5 && !memcmp(p, "ascii", 5)) {
147 p += 5;
148 vp->vp_uint8 = FR_MODE_VALUE_ASCII;
149 } else if ((q - p) == 8 && !memcmp(p, "netascii", 8)) {
150 p += 8;
151 vp->vp_uint8 = FR_MODE_VALUE_ASCII;
152 } else {
153 fr_strerror_printf("Invalid Mode");
154 goto error;
155 }
156
158 p += 1 /* \0 */;
159
160 if (p >= end) goto done;
161
162 /*
163 * Once here, the next 'blksize' is optional.
164 * At least: | blksize | \0 | #blksize | \0 |
165 */
166 if ((end - p) < 10) goto error_malformed;
167
168 if (!memcmp(p, "blksize\0", 8)) {
169 char *p_end = NULL;
170 long blksize;
171
172 p += 8;
173
174 if (p >= end || (end - p) < 1 || (end - p) > 6) goto error_malformed;
175
177 if (!vp) goto error;
178
179 blksize = strtol((const char *)p, &p_end, 10);
180
181 if (p == (const uint8_t *)p_end || blksize > FR_TFTP_BLOCK_MAX_SIZE) {
182 fr_strerror_printf("Invalid Block-Size %ld value", blksize);
183 goto error;
184 }
185
186 vp->vp_uint16 = (uint16_t)blksize;
188 }
189
190 break;
191
192 case FR_OPCODE_VALUE_ACKNOWLEDGEMENT:
193 case FR_OPCODE_VALUE_DATA:
194 /**
195 * 2 bytes 2 bytes
196 * ---------------------
197 * | Opcode | Block # |
198 * ---------------------
199 * Figure 5-3: ACK packet
200 */
201
203 if (!vp) goto error;
204
205 vp->vp_uint16 = fr_nbo_to_uint16(p);
206
208
209 /*
210 * From that point...
211 *
212 * 2 bytes 2 bytes n bytes
213 * ----------------------------------
214 * | Opcode | Block # | Data |
215 * ----------------------------------
216 * Figure 5-2: DATA packet
217 */
218 if (opcode != FR_OPCODE_VALUE_DATA) goto done;
219
220 if ((p + 2) >= end) goto error_malformed;
221
222 p += 2;
223
225 if (!vp) goto error;
226
227 fr_pair_value_memdup(vp, p, (end - p), true);
229
230 break;
231
232 case FR_OPCODE_VALUE_ERROR:
233 /**
234 * 2 bytes 2 bytes string 1 byte
235 * -----------------------------------------
236 * | Opcode | ErrorCode | ErrMsg | 0 |
237 * -----------------------------------------
238 *
239 * Figure 5-4: ERROR packet
240 */
241
242 if ((p + 2) >= end) goto error_malformed;
243
245 if (!vp) goto error;
246
247 vp->vp_uint16 = fr_nbo_to_uint16(p);
248
250
251 p += 2; /* <ErrorCode> */
252 q = memchr(p, '\0', (end - p));
253 if (!q || q[0] != '\0') goto error_malformed;
254
256 if (!vp) goto error;
257
258 fr_pair_value_bstrndup(vp, (char const *)p, (q - p), true);
260
261 break;
262
263 default:
264 fr_strerror_printf("Invalid TFTP opcode %#04x", opcode);
265 goto error;
266 }
267
268done:
269 return data_len;
270}
271
272/**
273 * Used as the decoder ctx.
274 */
275typedef struct {
278
279/*
280 * Test points for protocol decode
281 */
283 uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
284{
285 return fr_tftp_decode(ctx, out, data, data_len);
286}
287
289{
291
292 return 0;
293}
294
295static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
296{
297 fr_tftp_ctx_t *test_ctx;
298
299 if (fr_tftp_global_init() < 0) return -1;
300
301 test_ctx = talloc_zero(ctx, fr_tftp_ctx_t);
302 if (!test_ctx) return -1;
303
304 talloc_set_destructor(test_ctx, _decode_test_ctx);
305
306 *out = test_ctx;
307
308 return 0;
309}
310
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
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:2981
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:1345
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:283
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:2784
static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
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:312
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:282
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:288
Used as the decoder ctx.
Definition decode.c:275
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:67
Entry point for protocol decoders.
Definition test_point.h:66
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_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
static fr_slen_t data
Definition value.h:1265
static size_t char ** out
Definition value.h:997