Coverage Report

Created: 2024-08-28 06:17

/src/freeradius-server/src/protocols/tftp/encode.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: 3ade5916865e76eedd14e69087b1fd0885d1d486 $
19
 * @file src/protocols/tftp/encode.c
20
 * @brief Functions to encode 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: 3ade5916865e76eedd14e69087b1fd0885d1d486 $")
27
28
#include <freeradius-devel/util/dbuff.h>
29
#include <freeradius-devel/util/udp.h>
30
31
#include <freeradius-devel/io/test_point.h>
32
33
#include "tftp.h"
34
#include "attrs.h"
35
36
/*
37
 *  https://tools.ietf.org/html/rfc1350
38
 *
39
 *  Order of Headers
40
 *
41
 *                                                 2 bytes
42
 *   ----------------------------------------------------------
43
 *  |  Local Medium  |  Internet  |  Datagram  |  TFTP Opcode  |
44
 *   ----------------------------------------------------------
45
 *
46
 *  TFTP Formats
47
 *
48
 *  Type   Op #     Format without header
49
 *
50
 *         2 bytes    string   1 byte     string   1 byte
51
 *         -----------------------------------------------
52
 *  RRQ/  | 01/02 |  Filename  |   0  |    Mode    |   0  |
53
 *  WRQ    -----------------------------------------------
54
 *          2 bytes    2 bytes       n bytes
55
 *         ---------------------------------
56
 *  DATA  | 03    |   Block #  |    Data    |
57
 *         ---------------------------------
58
 *          2 bytes    2 bytes
59
 *         -------------------
60
 *  ACK   | 04    |   Block #  |
61
 *         --------------------
62
 *         2 bytes  2 bytes        string    1 byte
63
 *         ----------------------------------------
64
 *  ERROR | 05    |  ErrorCode |   ErrMsg   |   0  |
65
 *         ----------------------------------------
66
 *
67
 *  Initial Connection Protocol for reading a file
68
 *
69
 *  1. Host  A  sends  a  "RRQ"  to  host  B  with  source= A's TID,
70
 *     destination= 69.
71
 *
72
 *  2. Host B sends a "DATA" (with block number= 1) to host  A  with
73
 *     source= B's TID, destination= A's TID.
74
 */
75
ssize_t fr_tftp_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps)
76
0
{
77
0
  fr_dbuff_t  work_dbuff = FR_DBUFF_MAX(dbuff, FR_TFTP_BLOCK_MAX_SIZE);
78
0
  fr_pair_t   *vp;
79
0
  uint16_t  opcode;
80
0
  char const  *buf;
81
82
0
  vp = fr_pair_find_by_da(vps, NULL, attr_tftp_opcode);
83
0
  if (!vp) {
84
0
    fr_strerror_printf("Cannot send TFTP packet without %s", attr_tftp_opcode->name);
85
0
    return -1;
86
0
  }
87
88
0
  opcode = vp->vp_uint16;
89
0
  fr_dbuff_in(&work_dbuff, opcode);
90
91
0
  switch (opcode) {
92
0
  case FR_OPCODE_VALUE_READ_REQUEST:
93
0
  case FR_OPCODE_VALUE_WRITE_REQUEST:
94
    /*
95
     *  2 bytes     string    1 byte     string   1 byte   string    1 byte   string   1 byte
96
     *  +------------------------------------------------------------------------------------+
97
     *  | Opcode |  Filename  |   0  |    Mode    |   0  |  blksize  |  0  |  #blksize |  0  |
98
     *  +------------------------------------------------------------------------------------+
99
     *  Figure 5-1: RRQ/WRQ packet
100
     */
101
102
    /* <Filename> */
103
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_filename);
104
0
    if (!vp) {
105
0
      fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_filename->name);
106
0
      return -1;
107
0
    }
108
109
0
    FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
110
0
    fr_dbuff_in_bytes(&work_dbuff, '\0');
111
112
    /* <mode> */
113
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_mode);
114
0
    if (!vp) {
115
0
      fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_mode->name);
116
0
      return -1;
117
0
    }
118
119
0
    switch(vp->vp_uint16) {
120
0
    case FR_MODE_VALUE_ASCII: buf = "ascii"; break;
121
0
    case FR_MODE_VALUE_OCTET: buf = "octet"; break;
122
0
    default:
123
0
      fr_strerror_printf("Invalid %s value", attr_tftp_mode->name);
124
0
      return -1;
125
0
    }
126
127
0
    FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, buf, 5);
128
0
    fr_dbuff_in_bytes(&work_dbuff, '\0');
129
130
    /* <blksize> is optional */
131
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_block_size);
132
0
    if (vp) {
133
0
      char tmp[5+1];                                   /* max: 65535 */
134
135
0
      FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, "blksize", 7);
136
0
      fr_dbuff_in_bytes(&work_dbuff, '\0');
137
138
0
      snprintf(tmp, sizeof(tmp), "%d", vp->vp_uint16); /* #blksize */
139
0
      FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, tmp, strlen(tmp));
140
0
      fr_dbuff_in_bytes(&work_dbuff, '\0');
141
0
    }
142
143
0
    break;
144
145
0
  case FR_OPCODE_VALUE_ACKNOWLEDGEMENT:
146
0
  case FR_OPCODE_VALUE_DATA:
147
    /**
148
     * 2 bytes     2 bytes
149
     * ---------------------
150
     * | Opcode |   Block #  |
151
     * ---------------------
152
     * Figure 5-3: ACK packet
153
     */
154
155
    /* <Block> */
156
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_block);
157
0
    if (!vp) {
158
0
      fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_block->name);
159
0
      return -1;
160
0
    }
161
162
0
    fr_dbuff_in(&work_dbuff, vp->vp_uint16);
163
164
    /*
165
     *  From that point...
166
     *
167
     *  2 bytes     2 bytes      n bytes
168
     *  ----------------------------------
169
     *  | Opcode |   Block #  |   Data     |
170
     *  ----------------------------------
171
     *  Figure 5-2: DATA packet
172
     */
173
0
    if (opcode != FR_OPCODE_VALUE_DATA) goto done;
174
175
    /* <Data> */
176
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_data);
177
0
    if (!vp) {
178
0
      fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_data->name);
179
0
      return -1;
180
0
    }
181
182
0
    FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_octets, vp->vp_length);
183
184
0
    break;
185
186
0
  case FR_OPCODE_VALUE_ERROR:
187
0
  {
188
    /**
189
     * 2 bytes     2 bytes      string    1 byte
190
     * -----------------------------------------
191
     * | Opcode |  ErrorCode |   ErrMsg   |   0  |
192
     * -----------------------------------------
193
     *
194
     * Figure 5-4: ERROR packet
195
     */
196
0
    uint16_t  error_code;
197
0
    char const  *error_msg;
198
0
    size_t    error_msg_len;
199
200
    /* <ErroCode> */
201
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_error_code);
202
0
    if (!vp) {
203
0
      fr_strerror_printf("Invalid TFTP packet without %s", attr_tftp_error_code->name);
204
0
      return -1;
205
0
    }
206
207
0
    error_code = vp->vp_uint16;
208
0
    fr_dbuff_in(&work_dbuff, error_code);
209
210
    /* <ErrMsg> */
211
0
    vp = fr_pair_find_by_da(vps, NULL, attr_tftp_error_message);
212
0
    if (vp) {
213
0
      error_msg = vp->vp_strvalue;
214
0
      error_msg_len = vp->vp_length;
215
0
    } else {
216
0
      error_msg = fr_tftp_error_codes[error_code] ? fr_tftp_error_codes[error_code] : "Invalid ErrorCode";
217
0
      error_msg_len = strlen(error_msg);
218
0
    }
219
220
0
    FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, error_msg, error_msg_len);
221
0
    fr_dbuff_in_bytes(&work_dbuff, '\0');
222
0
    break;
223
0
  }
224
225
0
  default:
226
0
    fr_strerror_printf("Invalid TFTP opcode %#04x", opcode);
227
0
    return -1;
228
0
  }
229
230
0
done:
231
0
  fr_dbuff_set(dbuff, &work_dbuff);
232
233
0
  return fr_dbuff_used(dbuff);
234
0
}
235
236
/**
237
 *  Used as the encoder ctx.
238
 */
239
typedef struct {
240
  int   nothing;
241
} fr_tftp_ctx_t;
242
/*
243
 *  Test points for protocol encode
244
 */
245
static ssize_t fr_tftp_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
246
0
{
247
0
  return fr_tftp_encode(&FR_DBUFF_TMP(data, data_len), vps);
248
0
}
249
250
static int _encode_test_ctx(UNUSED fr_tftp_ctx_t *proto_ctx)
251
0
{
252
0
  fr_tftp_global_free();
253
254
0
  return 0;
255
0
}
256
257
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
258
0
{
259
0
  fr_tftp_ctx_t *test_ctx;
260
261
0
  if (fr_tftp_global_init() < 0) return -1;
262
263
0
  test_ctx = talloc_zero(ctx, fr_tftp_ctx_t);
264
0
  if (!test_ctx) return -1;
265
266
0
  talloc_set_destructor(test_ctx, _encode_test_ctx);
267
268
0
  *out = test_ctx;
269
270
0
  return 0;
271
0
}
272
273
/*
274
 *  Test points
275
 */
276
extern fr_test_point_proto_encode_t tftp_tp_encode_proto;
277
fr_test_point_proto_encode_t tftp_tp_encode_proto = {
278
  .test_ctx = encode_test_ctx,
279
  .func   = fr_tftp_encode_proto
280
};