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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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