Coverage Report

Created: 2024-08-28 06:17

/src/freeradius-server/src/protocols/bfd/encode.c
Line
Count
Source (jump to first uncovered line)
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: e5b6e1e5f740ffd535f251573c9396fe719036f5 $
19
 *
20
 * @file protocols/bfd/encode.c
21
 * @brief Functions to encode BFD packets
22
 *
23
 * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
24
 */
25
RCSID("$Id: e5b6e1e5f740ffd535f251573c9396fe719036f5 $")
26
27
#include <freeradius-devel/util/dbuff.h>
28
#include <freeradius-devel/util/struct.h>
29
#include <freeradius-devel/io/test_point.h>
30
#include <freeradius-devel/internal/internal.h>
31
32
#include "attrs.h"
33
34
/** Encodes the data portion of an attribute
35
 *
36
 * @return
37
 *  > 0, Length of the data portion.
38
 *      = 0, we could not encode anything, skip this attribute (and don't encode the header)
39
 *    unless it's one of a list of exceptions.
40
 *  < 0, How many additional bytes we'd need as a negative integer.
41
 *  PAIR_ENCODE_FATAL_ERROR - Abort encoding the packet.
42
 */
43
static ssize_t encode_value(fr_dbuff_t *dbuff,
44
          fr_da_stack_t *da_stack, unsigned int depth,
45
          fr_dcursor_t *cursor, void *encode_ctx)
46
0
{
47
0
  ssize_t     slen;
48
0
  fr_pair_t const   *vp = fr_dcursor_current(cursor);
49
0
  fr_dict_attr_t const  *da = da_stack->da[depth];
50
//  fr_bfd_ctx_t    *packet_ctx = encode_ctx;
51
0
  fr_dbuff_t    work_dbuff = FR_DBUFF(dbuff);
52
53
0
  PAIR_VERIFY(vp);
54
0
  FR_PROTO_STACK_PRINT(da_stack, depth);
55
56
  /*
57
   *  This has special requirements.
58
   */
59
0
  if ((vp->vp_type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_STRUCT)) {
60
0
    slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, NULL);
61
0
    goto done;
62
0
  }
63
64
  /*
65
   *  If it's not a TLV, it should be a value type RFC
66
   *  attribute make sure that it is.
67
   */
68
0
  if (da_stack->da[depth + 1] != NULL) {
69
0
    fr_strerror_printf("%s: Encoding value but not at top of stack", __FUNCTION__);
70
0
    return PAIR_ENCODE_FATAL_ERROR;
71
0
  }
72
73
0
  if (vp->da != da) {
74
0
    fr_strerror_printf("%s: Top of stack does not match vp->da", __FUNCTION__);
75
0
    return PAIR_ENCODE_FATAL_ERROR;
76
0
  }
77
78
0
  if (fr_type_is_structural(da->type)) {
79
0
    fr_strerror_printf("%s: Called with structural type %s", __FUNCTION__,
80
0
           fr_type_to_str(da_stack->da[depth]->type));
81
0
    return PAIR_ENCODE_FATAL_ERROR;
82
0
  }
83
84
0
  slen = fr_value_box_to_network(&work_dbuff, &vp->data);
85
86
0
done:
87
0
  if (slen < 0) return slen;
88
89
0
  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "%pP", vp);
90
91
0
  vp = fr_dcursor_next(cursor);
92
0
  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
93
94
0
  return fr_dbuff_set(dbuff, &work_dbuff);
95
0
}
96
97
/** Encode VPS into a BFD packet.
98
 *
99
 */
100
ssize_t fr_bfd_encode(uint8_t *out, size_t outlen, UNUSED uint8_t const *original,
101
          char const *secret, size_t secret_len, fr_pair_list_t *vps)
102
0
{
103
0
  ssize_t     slen;
104
0
  fr_bfd_ctx_t    packet_ctx;
105
0
  bfd_packet_t    *packet;
106
0
  fr_dcursor_t    cursor;
107
0
  fr_dbuff_t    work_dbuff = FR_DBUFF_TMP(out, outlen);
108
0
  fr_da_stack_t   da_stack;
109
110
0
  if (!fr_pair_dcursor_by_ancestor_init(&cursor, vps, attr_bfd_packet)) {
111
0
    fr_strerror_const("No BFD attributes found in the list");
112
0
    return -1;
113
0
  }
114
115
0
  packet_ctx.secret = secret;
116
117
0
  fr_proto_da_stack_build(&da_stack, attr_bfd_packet);
118
0
  FR_PROTO_STACK_PRINT(&da_stack, 0);
119
120
0
  slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, &packet_ctx, encode_value, NULL);
121
0
  if (slen < 0) return slen;
122
123
  /*
124
   *  The length is only 8 bits.  :(
125
   */
126
0
  if (slen > UINT8_MAX) {
127
0
    fr_strerror_const("Packet is larger than 255 octets");
128
0
    return -1;
129
0
  }
130
131
  /*
132
   *  For various reasons the base BFD struct has "auth-type" as the last MEMBER, even if it's not
133
   *  always used.  The struct encoder will fill it in with zeros, so we have to check for
134
   *  "auth_present" and then remove the last byte if there's no authentication stuff present.
135
   */
136
0
  packet = (bfd_packet_t *) out;
137
138
0
  if (!packet->auth_present) {
139
0
    if (slen > FR_BFD_HEADER_LENGTH) slen = FR_BFD_HEADER_LENGTH;
140
141
0
  } else if (!secret || secret_len == 0) {
142
0
    fr_strerror_const("Cannot sign packets without a secret");
143
0
    return -1;
144
145
0
  } else {
146
147
#if 0
148
    /*
149
     *  @todo - sign the packet with the chosen auth type
150
     */
151
    if (fr_bfd_sign(data, NULL, (uint8_t const *) secret, secret_len - 1) < 0) {
152
      return -1;
153
    }
154
#endif
155
0
  }
156
157
0
  packet->length = slen;
158
159
0
  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), slen, "BFD Packet");
160
161
0
  return slen;
162
0
}
163
164
165
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
166
0
{
167
0
  fr_bfd_ctx_t  *test_ctx;
168
169
0
  test_ctx = talloc_zero(ctx, fr_bfd_ctx_t);
170
0
  if (!test_ctx) return -1;
171
172
0
  test_ctx->secret = talloc_strdup(test_ctx, "testing123");
173
174
0
  *out = test_ctx;
175
176
0
  return 0;
177
0
}
178
179
static ssize_t fr_bfd_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, void *proto_ctx)
180
0
{
181
0
  fr_bfd_ctx_t  *test_ctx = talloc_get_type_abort(proto_ctx, fr_bfd_ctx_t);
182
0
  ssize_t   slen, alen;
183
0
  fr_pair_t *vp;
184
0
  fr_dbuff_t  dbuff;
185
186
  /*
187
   *  @todo - pass in test_ctx to this function, so that we
188
   *  can leverage a consistent random number generator.
189
   */
190
0
  slen = fr_bfd_encode(data, data_len, NULL, test_ctx->secret, talloc_array_length(test_ctx->secret) - 1, vps);
191
0
  if (slen <= 0) return slen;
192
193
0
  vp = fr_pair_find_by_da(vps, NULL, attr_bfd_additional_data);
194
0
  if (!vp) return slen;
195
196
0
  fr_dbuff_init(&dbuff, data + slen, data_len - slen);
197
0
  alen =  fr_internal_encode_list(&dbuff, &vp->vp_group, NULL);
198
0
  if (alen <= 0) return slen;
199
200
0
  return slen + alen;
201
0
}
202
203
/*
204
 *  No one else should be using this.
205
 */
206
extern void *fr_bfd_next_encodable(fr_dlist_head_t *list, void *to_eval, void *uctx);
207
208
/*
209
 *  Test points
210
 */
211
extern fr_test_point_proto_encode_t bfd_tp_encode_proto;
212
fr_test_point_proto_encode_t bfd_tp_encode_proto = {
213
  .test_ctx = encode_test_ctx,
214
  .func   = fr_bfd_encode_proto
215
};