Coverage Report

Created: 2023-12-08 06:56

/src/freeradius-server/src/protocols/tftp/decode.c
Line
Count
Source (jump to first uncovered line)
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: 5d6fd95f83a3379ca21c6f9cd57b7c8ce966f528 $
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
 */
26
RCSID("$Id: 5d6fd95f83a3379ca21c6f9cd57b7c8ce966f528 $")
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
 */
74
int fr_tftp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len)
75
321
{
76
321
  uint8_t const   *q, *p, *end;
77
321
  uint16_t  opcode;
78
321
  fr_pair_t *vp = NULL;
79
80
321
  if (data_len == 0) return -1;
81
82
321
  if (data_len < FR_TFTP_HDR_LEN) {
83
4
    fr_strerror_printf("TFTP packet is too small. (%zu < %d)", data_len, FR_TFTP_HDR_LEN);
84
117
  error:
85
117
    return -1;
86
4
  }
87
88
317
  p = data;
89
317
  end = (data + data_len);
90
91
  /* Opcode */
92
317
  opcode = fr_nbo_to_uint16(p);
93
317
  vp = fr_pair_afrom_da(ctx, attr_tftp_opcode);
94
317
  if (!vp) goto error;
95
96
317
  vp->vp_uint16 = opcode;
97
317
  fr_pair_append(out, vp);
98
317
  p += 2;
99
100
317
  switch (opcode) {
101
179
  case FR_OPCODE_VALUE_READ_REQUEST:
102
253
  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
253
    if (*(end - 1) != '\0') goto error_malformed;
113
114
    /* first character should be alpha numeric */
115
246
    if (!isalnum(p[0])) {
116
2
      fr_strerror_printf("Invalid Filename");
117
2
      goto error;
118
2
    }
119
120
    /* <filename> */
121
244
    q = memchr(p, '\0', (end - p));
122
244
    if (!(q && q[0] == '\0')) {
123
84
    error_malformed:
124
84
      fr_strerror_printf("Packet contains malformed attribute");
125
84
      return -1;
126
0
    }
127
128
244
    vp = fr_pair_afrom_da(ctx, attr_tftp_filename);
129
244
    if (!vp) goto error;
130
131
244
    fr_pair_value_bstrndup(vp, (char const *)p, (q - p), true);
132
244
    fr_pair_append(out, vp);
133
244
    p += (q - p) + 1 /* \0 */;
134
135
    /* <mode> */
136
244
    q = memchr(p, '\0', (end - p));
137
244
    if (!(q && q[0] == '\0')) goto error_malformed;
138
139
225
    vp = fr_pair_afrom_da(ctx, attr_tftp_mode);
140
225
    if (!vp) goto error;
141
142
    /* (netascii || ascii || octet) + \0 */
143
225
    if ((q - p) == 5 && !memcmp(p, "octet", 5)) {
144
117
      p += 5;
145
117
      vp->vp_uint8 = FR_MODE_VALUE_OCTET;
146
117
    } else if ((q - p) == 5 && !memcmp(p, "ascii", 5)) {
147
3
      p += 5;
148
3
      vp->vp_uint8 = FR_MODE_VALUE_ASCII;
149
105
    } else if ((q - p) == 8 && !memcmp(p, "netascii", 8)) {
150
1
      p += 8;
151
1
      vp->vp_uint8 = FR_MODE_VALUE_ASCII;
152
104
    } else {
153
104
      fr_strerror_printf("Invalid Mode");
154
104
      goto error;
155
104
    }
156
157
121
    fr_pair_append(out, vp);
158
121
    p += 1 /* \0 */;
159
160
121
    if (p >= end) goto done;
161
162
    /*
163
     *  Once here, the next 'blksize' is optional.
164
     *  At least: | blksize | \0 | #blksize | \0 |
165
     */
166
118
    if ((end - p) < 10) goto error_malformed;
167
168
112
    if (!memcmp(p, "blksize\0", 8)) {
169
93
      char *p_end = NULL;
170
93
      long blksize;
171
172
93
      p += 8;
173
174
93
      if (p >= end || (end - p) < 1 || (end - p) > 6) goto error_malformed;
175
176
46
      vp = fr_pair_afrom_da(ctx, attr_tftp_block_size);
177
46
      if (!vp) goto error;
178
179
46
      blksize = strtol((const char *)p, &p_end, 10);
180
181
46
      if (p == (const uint8_t *)p_end || blksize > FR_TFTP_BLOCK_MAX_SIZE) {
182
3
        fr_strerror_printf("Invalid Block-Size %ld value", blksize);
183
3
        goto error;
184
3
      }
185
186
43
      vp->vp_uint16 = (uint16_t)blksize;
187
43
      fr_pair_append(out, vp);
188
43
    }
189
190
62
    break;
191
192
62
  case FR_OPCODE_VALUE_ACKNOWLEDGEMENT:
193
37
  case FR_OPCODE_VALUE_DATA:
194
    /**
195
     *  2 bytes     2 bytes
196
     *  ---------------------
197
     *  | Opcode |   Block #  |
198
     *  ---------------------
199
     *  Figure 5-3: ACK packet
200
     */
201
202
37
    vp = fr_pair_afrom_da(ctx, attr_tftp_block);
203
37
    if (!vp) goto error;
204
205
37
    vp->vp_uint16 = fr_nbo_to_uint16(p);
206
207
37
    fr_pair_append(out, vp);
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
37
    if (opcode != FR_OPCODE_VALUE_DATA) goto done;
219
220
36
    if ((p + 2) >= end) goto error_malformed;
221
222
35
    p += 2;
223
224
35
    vp = fr_pair_afrom_da(ctx, attr_tftp_data);
225
35
    if (!vp) goto error;
226
227
35
    fr_pair_value_memdup(vp, p, (end - p), true);
228
35
    fr_pair_append(out, vp);
229
230
35
    break;
231
232
23
  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
23
    if ((p + 2) >= end) goto error_malformed;
243
244
22
    vp = fr_pair_afrom_da(ctx, attr_tftp_error_code);
245
22
    if (!vp) goto error;
246
247
22
    vp->vp_uint16 = fr_nbo_to_uint16(p);
248
249
22
    fr_pair_append(out, vp);
250
251
22
    p  += 2; /* <ErrorCode> */
252
22
    q   = memchr(p, '\0', (end - p));
253
22
    if (!q || q[0] != '\0') goto error_malformed;
254
255
19
    vp = fr_pair_afrom_da(ctx, attr_tftp_error_message);
256
19
    if (!vp) goto error;
257
258
19
    fr_pair_value_bstrndup(vp, (char const *)p, (q - p), true);
259
19
    fr_pair_append(out, vp);
260
261
19
    break;
262
263
4
  default:
264
4
    fr_strerror_printf("Invalid TFTP opcode %#04x", opcode);
265
4
    goto error;
266
317
  }
267
268
120
done:
269
120
  return data_len;
270
317
}
271
272
/**
273
 *  Used as the decoder ctx.
274
 */
275
typedef struct {
276
  int   nothing;
277
} fr_tftp_ctx_t;
278
279
/*
280
 *  Test points for protocol decode
281
 */
282
static ssize_t fr_tftp_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out,
283
            uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
284
321
{
285
321
  return fr_tftp_decode(ctx, out, data, data_len);
286
321
}
287
288
static int _decode_test_ctx(UNUSED fr_tftp_ctx_t *proto_ctx)
289
2.95k
{
290
2.95k
  fr_tftp_free();
291
292
2.95k
  return 0;
293
2.95k
}
294
295
static int decode_test_ctx(void **out, TALLOC_CTX *ctx)
296
7.59k
{
297
7.59k
  fr_tftp_ctx_t *test_ctx;
298
299
7.59k
  if (fr_tftp_init() < 0) return -1;
300
301
7.59k
  test_ctx = talloc_zero(ctx, fr_tftp_ctx_t);
302
7.59k
  if (!test_ctx) return -1;
303
304
7.59k
  talloc_set_destructor(test_ctx, _decode_test_ctx);
305
306
7.59k
  *out = test_ctx;
307
308
7.59k
  return 0;
309
7.59k
}
310
311
extern fr_test_point_proto_decode_t tftp_tp_decode_proto;
312
fr_test_point_proto_decode_t tftp_tp_decode_proto = {
313
  .test_ctx = decode_test_ctx,
314
  .func   = fr_tftp_decode_proto
315
};