xref: /openbsd/lib/libradius/radius_attr.c (revision 834c20a7)
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