1 /* $OpenBSD: radius_attr.c,v 1.3 2024/07/24 08:19:16 yasuoka Exp $ */
2
3 /*-
4 * Copyright (c) 2009 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE"AUTHOR" AND CONTRIBUTORS AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31
32 #include <endian.h>
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "radius.h"
39
40 #include "radius_local.h"
41
42 #define FIND_ATTRIBUTE_BEGIN(constness, redolabel) \
43 constness RADIUS_ATTRIBUTE* attr; \
44 constness RADIUS_ATTRIBUTE* end; \
45 \
46 attr = ATTRS_BEGIN(packet->pdata); \
47 end = ATTRS_END(packet->pdata); \
48 \
49 for (;; ATTRS_ADVANCE(attr)) \
50 { \
51 redolabel \
52 if (attr >= end) \
53 break; \
54 if (attr->type != type) \
55 continue; \
56 {
57
58 #define FIND_ATTRIBUTE_END \
59 } }
60
61 #define FIND_VS_ATTRIBUTE_BEGIN(constness, redolabel) \
62 constness RADIUS_ATTRIBUTE* attr; \
63 constness RADIUS_ATTRIBUTE* end; \
64 \
65 attr = ATTRS_BEGIN(packet->pdata); \
66 end = ATTRS_END(packet->pdata); \
67 \
68 for (;; ATTRS_ADVANCE(attr)) \
69 { \
70 redolabel \
71 if (attr >= end) \
72 break; \
73 if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) \
74 continue; \
75 if (attr->vendor != htonl(vendor)) \
76 continue; \
77 if (attr->vtype != vtype) \
78 continue; \
79 {
80
81 #define FIND_VS_ATTRIBUTE_END \
82 } }
83
84
85 int
radius_get_raw_attr_ptr(const RADIUS_PACKET * packet,uint8_t type,const void ** ptr,size_t * length)86 radius_get_raw_attr_ptr(const RADIUS_PACKET * packet, uint8_t type,
87 const void **ptr, size_t * length)
88 {
89 FIND_ATTRIBUTE_BEGIN(const,) {
90 *length = attr->length - 2;
91 *ptr = attr->data;
92 return (0);
93 } FIND_ATTRIBUTE_END;
94
95 return (-1);
96 }
97
98 int
radius_get_vs_raw_attr_ptr(const RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,const void ** ptr,size_t * length)99 radius_get_vs_raw_attr_ptr(const RADIUS_PACKET * packet, uint32_t vendor,
100 uint8_t vtype, const void **ptr, size_t * length)
101 {
102 FIND_VS_ATTRIBUTE_BEGIN(const,) {
103 *length = attr->vlength - 2;
104 *ptr = attr->vdata;
105 return (0);
106 } FIND_VS_ATTRIBUTE_END;
107
108 return (-1);
109 }
110
111 int
radius_get_raw_attr(const RADIUS_PACKET * packet,uint8_t type,void * buf,size_t * length)112 radius_get_raw_attr(const RADIUS_PACKET * packet, uint8_t type, void *buf,
113 size_t * length)
114 {
115 FIND_ATTRIBUTE_BEGIN(const,) {
116 *length = MINIMUM(attr->length - 2, *length);
117 memcpy(buf, attr->data, *length);
118 return (0);
119 } FIND_ATTRIBUTE_END;
120
121 return (-1);
122 }
123
124 int
radius_get_vs_raw_attr(const RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,void * buf,size_t * length)125 radius_get_vs_raw_attr(const RADIUS_PACKET * packet, uint32_t vendor,
126 uint8_t vtype, void *buf, size_t * length)
127 {
128 FIND_VS_ATTRIBUTE_BEGIN(const,) {
129 *length = MINIMUM(attr->vlength - 2, *length);
130 memcpy(buf, attr->vdata, *length);
131 return (0);
132 } FIND_VS_ATTRIBUTE_END;
133
134 return (-1);
135 }
136
137 int
radius_get_raw_attr_cat(const RADIUS_PACKET * packet,uint8_t type,void * buf,size_t * length)138 radius_get_raw_attr_cat(const RADIUS_PACKET * packet, uint8_t type, void *buf,
139 size_t * length)
140 {
141 size_t off = 0;
142
143 FIND_ATTRIBUTE_BEGIN(const,) {
144 if (buf != NULL) {
145 if (off + attr->length - 2 <= *length)
146 memcpy((char *)buf + off, attr->data,
147 attr->length - 2);
148 else
149 return (-1);
150 }
151 off += attr->length - 2;
152 } FIND_ATTRIBUTE_END;
153
154 *length = off;
155
156 return (0);
157 }
158
159 int
radius_get_vs_raw_attr_cat(const RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,void * buf,size_t * length)160 radius_get_vs_raw_attr_cat(const RADIUS_PACKET * packet, uint32_t vendor,
161 uint8_t vtype, void *buf, size_t * length)
162 {
163 size_t off = 0;
164
165 FIND_VS_ATTRIBUTE_BEGIN(const,) {
166 if (buf != NULL) {
167 if (off + attr->vlength - 2 <= *length)
168 memcpy((char *)buf + off, attr->vdata,
169 attr->vlength - 2);
170 else
171 return (-1);
172 }
173 off += attr->vlength - 2;
174 } FIND_VS_ATTRIBUTE_END;
175
176 *length = off;
177
178 return (0);
179 }
180
181 int
radius_put_raw_attr(RADIUS_PACKET * packet,uint8_t type,const void * buf,size_t length)182 radius_put_raw_attr(RADIUS_PACKET * packet, uint8_t type, const void *buf,
183 size_t length)
184 {
185 RADIUS_ATTRIBUTE *newattr;
186
187 if (length > 255 - 2)
188 return (-1);
189
190 if (radius_ensure_add_capacity(packet, length + 2) != 0)
191 return (-1);
192
193 newattr = ATTRS_END(packet->pdata);
194 newattr->type = type;
195 newattr->length = length + 2;
196 memcpy(newattr->data, buf, length);
197 packet->pdata->length = htons(radius_get_length(packet) + length + 2);
198
199 return (0);
200 }
201
202 int
radius_unshift_raw_attr(RADIUS_PACKET * packet,uint8_t type,const void * buf,size_t length)203 radius_unshift_raw_attr(RADIUS_PACKET * packet, uint8_t type, const void *buf,
204 size_t length)
205 {
206 RADIUS_ATTRIBUTE *newattr;
207
208 if (length > 255 - 2)
209 return (-1);
210
211 if (radius_ensure_add_capacity(packet, length + 2) != 0)
212 return (-1);
213
214 memmove(packet->pdata->attributes + length + 2,
215 packet->pdata->attributes,
216 radius_get_length(packet) - sizeof(RADIUS_PACKET_DATA));
217
218 newattr = ATTRS_BEGIN(packet->pdata);
219 newattr->type = type;
220 newattr->length = length + 2;
221 memcpy(newattr->data, buf, length);
222 packet->pdata->length = htons(radius_get_length(packet) + length + 2);
223
224 return (0);
225 }
226
227 int
radius_put_vs_raw_attr(RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,const void * buf,size_t length)228 radius_put_vs_raw_attr(RADIUS_PACKET * packet, uint32_t vendor, uint8_t vtype,
229 const void *buf, size_t length)
230 {
231 RADIUS_ATTRIBUTE *newattr;
232
233 if (length > 255 - 8)
234 return (-1);
235 if ((vendor & 0xff000000U) != 0)
236 return (-1);
237
238 if (radius_ensure_add_capacity(packet, length + 8) != 0)
239 return (-1);
240
241 newattr = ATTRS_END(packet->pdata);
242 newattr->type = RADIUS_TYPE_VENDOR_SPECIFIC;
243 newattr->length = length + 8;
244 newattr->vendor = htonl(vendor);
245 newattr->vtype = vtype;
246 newattr->vlength = length + 2;
247 memcpy(newattr->vdata, buf, length);
248 packet->pdata->length = htons(radius_get_length(packet) + length + 8);
249
250 return (0);
251 }
252
253 int
radius_put_raw_attr_cat(RADIUS_PACKET * packet,uint8_t type,const void * buf,size_t length)254 radius_put_raw_attr_cat(RADIUS_PACKET * packet, uint8_t type, const void *buf,
255 size_t length)
256 {
257 int off, len0;
258
259 off = 0;
260 while (off < length) {
261 len0 = MINIMUM(length - off, 255 - 2);
262
263 if (radius_put_raw_attr(packet, type, (const char *)buf + off,
264 len0) != 0)
265 return (-1);
266
267 off += len0;
268 }
269
270 return (0);
271 }
272
273 int
radius_put_vs_raw_attr_cat(RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,const void * buf,size_t length)274 radius_put_vs_raw_attr_cat(RADIUS_PACKET * packet, uint32_t vendor,
275 uint8_t vtype, const void *buf, size_t length)
276 {
277 int off, len0;
278
279 off = 0;
280 while (off < length) {
281 len0 = MINIMUM(length - off, 255 - 8);
282
283 if (radius_put_vs_raw_attr(packet, vendor, vtype,
284 (const char *)buf + off, len0) != 0)
285 return (-1);
286
287 off += len0;
288 }
289
290 return (0);
291 }
292
293 int
radius_set_raw_attr(RADIUS_PACKET * packet,uint8_t type,const void * buf,size_t length)294 radius_set_raw_attr(RADIUS_PACKET * packet,
295 uint8_t type, const void *buf, size_t length)
296 {
297 FIND_ATTRIBUTE_BEGIN(,) {
298 if (length != attr->length - 2)
299 return (-1);
300 memcpy(attr->data, buf, length);
301 return (0);
302 } FIND_ATTRIBUTE_END;
303
304 return (-1);
305 }
306
307 int
radius_set_vs_raw_attr(RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,const void * buf,size_t length)308 radius_set_vs_raw_attr(RADIUS_PACKET * packet,
309 uint32_t vendor, uint8_t vtype, const void *buf, size_t length)
310 {
311 FIND_VS_ATTRIBUTE_BEGIN(,) {
312 if (length != attr->vlength - 2)
313 return (-1);
314 memcpy(attr->vdata, buf, length);
315 return (0);
316 } FIND_VS_ATTRIBUTE_END;
317
318 return (-1);
319 }
320
321 int
radius_del_attr_all(RADIUS_PACKET * packet,uint8_t type)322 radius_del_attr_all(RADIUS_PACKET * packet, uint8_t type)
323 {
324 FIND_ATTRIBUTE_BEGIN(, redo:) {
325 RADIUS_ATTRIBUTE *next = ATTRS_NEXT(attr);
326 packet->pdata->length =
327 htons(ntohs(packet->pdata->length) - attr->length);
328 memmove(attr, next, ((char *)end) - ((char *)next));
329 end = ATTRS_END(packet->pdata);
330 goto redo;
331 } FIND_ATTRIBUTE_END;
332
333 return (0);
334 }
335
336 int
radius_del_vs_attr_all(RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype)337 radius_del_vs_attr_all(RADIUS_PACKET * packet, uint32_t vendor, uint8_t vtype)
338 {
339 FIND_VS_ATTRIBUTE_BEGIN(, redo:) {
340 RADIUS_ATTRIBUTE *next = ATTRS_NEXT(attr);
341 packet->pdata->length =
342 htons(ntohs(packet->pdata->length) - attr->length);
343 memmove(attr, next, ((char *)end) - ((char *)next));
344 end = ATTRS_END(packet->pdata);
345 goto redo;
346 } FIND_VS_ATTRIBUTE_END;
347
348 return (0);
349 }
350
351 bool
radius_has_attr(const RADIUS_PACKET * packet,uint8_t type)352 radius_has_attr(const RADIUS_PACKET * packet, uint8_t type)
353 {
354 FIND_ATTRIBUTE_BEGIN(const,) {
355 return (true);
356 } FIND_VS_ATTRIBUTE_END;
357
358 return (false);
359 }
360
361 bool
radius_has_vs_attr(const RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype)362 radius_has_vs_attr(const RADIUS_PACKET * packet, uint32_t vendor, uint8_t vtype)
363 {
364 FIND_VS_ATTRIBUTE_BEGIN(const,) {
365 return (true);
366 } FIND_VS_ATTRIBUTE_END;
367
368 return (false);
369 }
370
371 int
radius_get_string_attr(const RADIUS_PACKET * packet,uint8_t type,char * str,size_t len)372 radius_get_string_attr(const RADIUS_PACKET * packet, uint8_t type, char *str,
373 size_t len)
374 {
375 const void *p;
376 size_t origlen;
377
378 if (radius_get_raw_attr_ptr(packet, type, &p, &origlen) != 0)
379 return (-1);
380 if (memchr(p, 0, origlen) != NULL)
381 return (-1);
382 if (len >= 1) {
383 len = MINIMUM(origlen, len - 1);
384 memcpy(str, (const char *)p, len);
385 str[len] = '\0';
386 }
387 return (0);
388 }
389
390 int
radius_get_vs_string_attr(const RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,char * str,size_t len)391 radius_get_vs_string_attr(const RADIUS_PACKET * packet,
392 uint32_t vendor, uint8_t vtype, char *str, size_t len)
393 {
394 const void *p;
395 size_t origlen;
396
397 if (radius_get_vs_raw_attr_ptr(packet,
398 vendor, vtype, &p, &origlen) != 0)
399 return (-1);
400 if (memchr(p, 0, origlen) != NULL)
401 return (-1);
402 if (len >= 1) {
403 len = MINIMUM(origlen, len - 1);
404 memcpy(str, (const char *)p, len);
405 str[len] = '\0';
406 }
407
408 return (0);
409 }
410
411 int
radius_put_string_attr(RADIUS_PACKET * packet,uint8_t type,const char * str)412 radius_put_string_attr(RADIUS_PACKET * packet, uint8_t type, const char *str)
413 {
414 return radius_put_raw_attr(packet, type, str, strlen(str));
415 }
416
417 int
radius_put_vs_string_attr(RADIUS_PACKET * packet,uint32_t vendor,uint8_t vtype,const char * str)418 radius_put_vs_string_attr(RADIUS_PACKET * packet,
419 uint32_t vendor, uint8_t vtype, const char *str)
420 {
421 return radius_put_vs_raw_attr(packet, vendor, vtype, str, strlen(str));
422 }
423
424
425 #define DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYVAL(ftname, tname, hton, ntoh) \
426 int radius_get_ ## ftname ## _attr(const RADIUS_PACKET *packet, \
427 uint8_t type, tname *val) \
428 { \
429 const void *p; \
430 tname nval; \
431 size_t len; \
432 \
433 if (radius_get_raw_attr_ptr(packet, type, &p, &len) != 0) \
434 return (-1); \
435 if (len != sizeof(tname)) \
436 return (-1); \
437 memcpy(&nval, p, sizeof(tname)); \
438 *val = ntoh(nval); \
439 return (0); \
440 } \
441 \
442 int radius_get_vs_ ## ftname ## _attr(const RADIUS_PACKET *packet, \
443 uint32_t vendor, uint8_t vtype, tname *val) \
444 { \
445 const void *p; \
446 tname nval; \
447 size_t len; \
448 \
449 if (radius_get_vs_raw_attr_ptr(packet, \
450 vendor, vtype, &p, &len) != 0) \
451 return (-1); \
452 if (len != sizeof(tname)) \
453 return (-1); \
454 memcpy(&nval, p, sizeof(tname)); \
455 *val = ntoh(nval); \
456 return (0); \
457 } \
458 \
459 int radius_put_ ## ftname ## _attr(RADIUS_PACKET *packet, \
460 uint8_t type, tname val) \
461 { \
462 tname nval; \
463 \
464 nval = hton(val); \
465 return radius_put_raw_attr(packet, type, &nval, sizeof(tname)); \
466 } \
467 \
468 int radius_put_vs_ ## ftname ## _attr(RADIUS_PACKET *packet, \
469 uint32_t vendor, uint8_t vtype, tname val) \
470 { \
471 tname nval; \
472 \
473 nval = hton(val); \
474 return radius_put_vs_raw_attr(packet, vendor, vtype, \
475 &nval, sizeof(tname)); \
476 } \
477 \
478 int radius_set_ ## ftname ## _attr(RADIUS_PACKET *packet, \
479 uint8_t type, tname val) \
480 { \
481 tname nval; \
482 \
483 nval = hton(val); \
484 return radius_set_raw_attr(packet, type, &nval, sizeof(tname)); \
485 } \
486 \
487 int radius_set_vs_ ## ftname ## _attr(RADIUS_PACKET *packet, \
488 uint32_t vendor, uint8_t vtype, tname val) \
489 { \
490 tname nval; \
491 \
492 nval = hton(val); \
493 return radius_set_vs_raw_attr(packet, vendor, vtype, \
494 &nval, sizeof(tname)); \
495 }
496
497 #define DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYPTR(ftname, tname) \
498 int radius_get_ ## ftname ## _attr(const RADIUS_PACKET *packet, \
499 uint8_t type, tname *val) \
500 { \
501 const void *p; \
502 size_t len; \
503 \
504 if (radius_get_raw_attr_ptr(packet, type, &p, &len) != 0) \
505 return (-1); \
506 if (len != sizeof(tname)) \
507 return (-1); \
508 memcpy(val, p, sizeof(tname)); \
509 return (0); \
510 } \
511 \
512 int radius_get_vs_ ## ftname ## _attr(const RADIUS_PACKET *packet, \
513 uint32_t vendor, uint8_t vtype, tname *val) \
514 { \
515 const void *p; \
516 size_t len; \
517 \
518 if (radius_get_vs_raw_attr_ptr(packet, \
519 vendor, vtype, &p, &len) != 0) \
520 return (-1); \
521 if (len != sizeof(tname)) \
522 return (-1); \
523 memcpy(val, p, sizeof(tname)); \
524 return (0); \
525 } \
526 \
527 int radius_put_ ## ftname ## _attr(RADIUS_PACKET *packet, \
528 uint8_t type, const tname *val) \
529 { \
530 return radius_put_raw_attr(packet, type, val, sizeof(tname)); \
531 } \
532 \
533 int radius_put_vs_ ## ftname ## _attr(RADIUS_PACKET *packet, \
534 uint32_t vendor, uint8_t vtype, const tname *val) \
535 { \
536 return radius_put_vs_raw_attr(packet, vendor, vtype, \
537 val, sizeof(tname)); \
538 } \
539 \
540 int radius_set_ ## ftname ## _attr(RADIUS_PACKET *packet, \
541 uint8_t type, const tname *val) \
542 { \
543 return radius_set_raw_attr(packet, type, val, sizeof(tname)); \
544 } \
545 \
546 int radius_set_vs_ ## ftname ## _attr(RADIUS_PACKET *packet, \
547 uint32_t vendor, uint8_t vtype, const tname *val) \
548 { \
549 return radius_set_vs_raw_attr(packet, vendor, vtype, \
550 val, sizeof(tname)); \
551 }
552
553 DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYVAL(uint16, uint16_t, htons, ntohs)
554 DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYVAL(uint32, uint32_t, htonl, ntohl)
555 DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYVAL(uint64, uint64_t, htobe64, betoh64)
556 DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYVAL(ipv4, struct in_addr,,)
557 DEFINE_TYPED_ATTRIBUTE_ACCESSOR_BYPTR(ipv6, struct in6_addr)
558