1*9c3f005cSyasuoka /* $OpenBSD: radius.c,v 1.6 2024/08/14 04:50:31 yasuoka Exp $ */ 20eaf192dSyasuoka 30eaf192dSyasuoka /*- 40eaf192dSyasuoka * Copyright (c) 2009 Internet Initiative Japan Inc. 50eaf192dSyasuoka * All rights reserved. 60eaf192dSyasuoka * 70eaf192dSyasuoka * Redistribution and use in source and binary forms, with or without 80eaf192dSyasuoka * modification, are permitted provided that the following conditions 90eaf192dSyasuoka * are met: 100eaf192dSyasuoka * 1. Redistributions of source code must retain the above copyright 110eaf192dSyasuoka * notice, this list of conditions and the following disclaimer. 120eaf192dSyasuoka * 2. Redistributions in binary form must reproduce the above copyright 130eaf192dSyasuoka * notice, this list of conditions and the following disclaimer in the 140eaf192dSyasuoka * documentation and/or other materials provided with the distribution. 150eaf192dSyasuoka * 160eaf192dSyasuoka * THIS SOFTWARE IS PROVIDED BY THE"AUTHOR" AND CONTRIBUTORS AS IS'' AND 170eaf192dSyasuoka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 180eaf192dSyasuoka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 190eaf192dSyasuoka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 200eaf192dSyasuoka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 210eaf192dSyasuoka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 220eaf192dSyasuoka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 230eaf192dSyasuoka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 240eaf192dSyasuoka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 250eaf192dSyasuoka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 260eaf192dSyasuoka * SUCH DAMAGE. 270eaf192dSyasuoka */ 280eaf192dSyasuoka 290eaf192dSyasuoka #include <sys/socket.h> 300eaf192dSyasuoka #include <sys/uio.h> 317426d5d9Syasuoka #include <arpa/inet.h> 327426d5d9Syasuoka 337426d5d9Syasuoka #include <stdint.h> 340eaf192dSyasuoka #include <stdio.h> 350eaf192dSyasuoka #include <stdlib.h> 360eaf192dSyasuoka #include <string.h> 370eaf192dSyasuoka 380eaf192dSyasuoka #include <openssl/md5.h> 390eaf192dSyasuoka 400eaf192dSyasuoka #include "radius.h" 410eaf192dSyasuoka 420eaf192dSyasuoka #include "radius_local.h" 430eaf192dSyasuoka 440eaf192dSyasuoka static uint8_t radius_id_counter = 0; 450eaf192dSyasuoka 460eaf192dSyasuoka static int 474551b161Syasuoka radius_check_packet_data(const RADIUS_PACKET_DATA * pdata, size_t length) 480eaf192dSyasuoka { 490eaf192dSyasuoka const RADIUS_ATTRIBUTE *attr; 500eaf192dSyasuoka const RADIUS_ATTRIBUTE *end; 510eaf192dSyasuoka 520eaf192dSyasuoka if (length < sizeof(RADIUS_PACKET_DATA)) 530eaf192dSyasuoka return (-1); 540eaf192dSyasuoka if (length > 0xffff) 550eaf192dSyasuoka return (-1); 560eaf192dSyasuoka if (length != (size_t) ntohs(pdata->length)) 570eaf192dSyasuoka return (-1); 580eaf192dSyasuoka 590eaf192dSyasuoka attr = ATTRS_BEGIN(pdata); 600eaf192dSyasuoka end = ATTRS_END(pdata); 610eaf192dSyasuoka for (; attr < end; ATTRS_ADVANCE(attr)) { 620eaf192dSyasuoka if (attr->length < 2) 630eaf192dSyasuoka return (-1); 640eaf192dSyasuoka if (attr->type == RADIUS_TYPE_VENDOR_SPECIFIC) { 650eaf192dSyasuoka if (attr->length < 8) 660eaf192dSyasuoka return (-1); 670eaf192dSyasuoka if ((attr->vendor & htonl(0xff000000U)) != 0) 680eaf192dSyasuoka return (-1); 690eaf192dSyasuoka if (attr->length != attr->vlength + 6) 700eaf192dSyasuoka return (-1); 710eaf192dSyasuoka } 720eaf192dSyasuoka } 730eaf192dSyasuoka 740eaf192dSyasuoka if (attr != end) 750eaf192dSyasuoka return (-1); 760eaf192dSyasuoka 770eaf192dSyasuoka return (0); 780eaf192dSyasuoka } 790eaf192dSyasuoka 800eaf192dSyasuoka int 810eaf192dSyasuoka radius_ensure_add_capacity(RADIUS_PACKET * packet, size_t capacity) 820eaf192dSyasuoka { 830eaf192dSyasuoka size_t newsize; 840eaf192dSyasuoka void *newptr; 850eaf192dSyasuoka 860eaf192dSyasuoka /* 870eaf192dSyasuoka * The maximum size is 64KB. 880eaf192dSyasuoka * We use little bit smaller value for our safety(?). 890eaf192dSyasuoka */ 900eaf192dSyasuoka if (ntohs(packet->pdata->length) + capacity > 0xfe00) 910eaf192dSyasuoka return (-1); 920eaf192dSyasuoka 930eaf192dSyasuoka if (ntohs(packet->pdata->length) + capacity > packet->capacity) { 940eaf192dSyasuoka newsize = ntohs(packet->pdata->length) + capacity + 950eaf192dSyasuoka RADIUS_PACKET_CAPACITY_INCREMENT; 960eaf192dSyasuoka newptr = realloc(packet->pdata, newsize); 970eaf192dSyasuoka if (newptr == NULL) 980eaf192dSyasuoka return (-1); 990eaf192dSyasuoka packet->capacity = newsize; 1000eaf192dSyasuoka packet->pdata = (RADIUS_PACKET_DATA *)newptr; 1010eaf192dSyasuoka } 1020eaf192dSyasuoka return (0); 1030eaf192dSyasuoka } 1040eaf192dSyasuoka 1050eaf192dSyasuoka RADIUS_PACKET * 1060eaf192dSyasuoka radius_new_request_packet(uint8_t code) 1070eaf192dSyasuoka { 1080eaf192dSyasuoka RADIUS_PACKET *packet; 1090eaf192dSyasuoka 110285f54cdSderaadt packet = malloc(sizeof(RADIUS_PACKET)); 1110eaf192dSyasuoka if (packet == NULL) 1120eaf192dSyasuoka return (NULL); 113285f54cdSderaadt packet->pdata = malloc(RADIUS_PACKET_CAPACITY_INITIAL); 1140eaf192dSyasuoka if (packet->pdata == NULL) { 1150eaf192dSyasuoka free(packet); 1160eaf192dSyasuoka return (NULL); 1170eaf192dSyasuoka } 1180eaf192dSyasuoka packet->capacity = RADIUS_PACKET_CAPACITY_INITIAL; 1190eaf192dSyasuoka packet->request = NULL; 1200eaf192dSyasuoka packet->pdata->code = code; 1210eaf192dSyasuoka packet->pdata->id = radius_id_counter++; 1220eaf192dSyasuoka packet->pdata->length = htons(sizeof(RADIUS_PACKET_DATA)); 1230eaf192dSyasuoka arc4random_buf(packet->pdata->authenticator, 1240eaf192dSyasuoka sizeof(packet->pdata->authenticator)); 1250eaf192dSyasuoka 1260eaf192dSyasuoka return (packet); 1270eaf192dSyasuoka } 1280eaf192dSyasuoka 1290eaf192dSyasuoka RADIUS_PACKET * 1300eaf192dSyasuoka radius_new_response_packet(uint8_t code, const RADIUS_PACKET * request) 1310eaf192dSyasuoka { 1320eaf192dSyasuoka RADIUS_PACKET *packet; 1330eaf192dSyasuoka 1340eaf192dSyasuoka packet = radius_new_request_packet(code); 1350eaf192dSyasuoka if (packet == NULL) 1360eaf192dSyasuoka return (NULL); 1370eaf192dSyasuoka packet->request = request; 1380eaf192dSyasuoka packet->pdata->id = request->pdata->id; 1390eaf192dSyasuoka 1400eaf192dSyasuoka return (packet); 1410eaf192dSyasuoka } 1420eaf192dSyasuoka 1430eaf192dSyasuoka RADIUS_PACKET * 1440eaf192dSyasuoka radius_convert_packet(const void *pdata, size_t length) 1450eaf192dSyasuoka { 1460eaf192dSyasuoka RADIUS_PACKET *packet; 1470eaf192dSyasuoka 1480eaf192dSyasuoka if (radius_check_packet_data((const RADIUS_PACKET_DATA *)pdata, 1490eaf192dSyasuoka length) != 0) 1500eaf192dSyasuoka return (NULL); 151285f54cdSderaadt packet = malloc(sizeof(RADIUS_PACKET)); 1520eaf192dSyasuoka if (packet == NULL) 1530eaf192dSyasuoka return (NULL); 154285f54cdSderaadt packet->pdata = malloc(length); 1550eaf192dSyasuoka packet->capacity = length; 1560eaf192dSyasuoka packet->request = NULL; 1570eaf192dSyasuoka if (packet->pdata == NULL) { 1580eaf192dSyasuoka free(packet); 1590eaf192dSyasuoka return (NULL); 1600eaf192dSyasuoka } 1610eaf192dSyasuoka memcpy(packet->pdata, pdata, length); 1620eaf192dSyasuoka 1630eaf192dSyasuoka return (packet); 1640eaf192dSyasuoka } 1650eaf192dSyasuoka 1660eaf192dSyasuoka int 1670eaf192dSyasuoka radius_delete_packet(RADIUS_PACKET * packet) 1680eaf192dSyasuoka { 1690eaf192dSyasuoka free(packet->pdata); 1700eaf192dSyasuoka free(packet); 1710eaf192dSyasuoka return (0); 1720eaf192dSyasuoka } 1730eaf192dSyasuoka 1740eaf192dSyasuoka uint8_t 1750eaf192dSyasuoka radius_get_code(const RADIUS_PACKET * packet) 1760eaf192dSyasuoka { 1770eaf192dSyasuoka return (packet->pdata->code); 1780eaf192dSyasuoka } 1790eaf192dSyasuoka 1800eaf192dSyasuoka uint8_t 1810eaf192dSyasuoka radius_get_id(const RADIUS_PACKET * packet) 1820eaf192dSyasuoka { 1830eaf192dSyasuoka return (packet->pdata->id); 1840eaf192dSyasuoka } 1850eaf192dSyasuoka 1860eaf192dSyasuoka void 1870eaf192dSyasuoka radius_update_id(RADIUS_PACKET * packet) 1880eaf192dSyasuoka { 1890eaf192dSyasuoka packet->pdata->id = radius_id_counter++; 1900eaf192dSyasuoka } 1910eaf192dSyasuoka 1920eaf192dSyasuoka void 1930eaf192dSyasuoka radius_set_id(RADIUS_PACKET * packet, uint8_t id) 1940eaf192dSyasuoka { 1950eaf192dSyasuoka packet->pdata->id = id; 1960eaf192dSyasuoka } 1970eaf192dSyasuoka 1980eaf192dSyasuoka void 1990eaf192dSyasuoka radius_get_authenticator(const RADIUS_PACKET * packet, void *authenticator) 2000eaf192dSyasuoka { 2010eaf192dSyasuoka memcpy(authenticator, packet->pdata->authenticator, 16); 2020eaf192dSyasuoka } 2030eaf192dSyasuoka 2040eaf192dSyasuoka uint8_t * 2050eaf192dSyasuoka radius_get_authenticator_retval(const RADIUS_PACKET * packet) 2060eaf192dSyasuoka { 2070eaf192dSyasuoka return (packet->pdata->authenticator); 2080eaf192dSyasuoka } 2090eaf192dSyasuoka 2100eaf192dSyasuoka uint8_t * 2110eaf192dSyasuoka radius_get_request_authenticator_retval(const RADIUS_PACKET * packet) 2120eaf192dSyasuoka { 2130eaf192dSyasuoka if (packet->request == NULL) 2140eaf192dSyasuoka return (packet->pdata->authenticator); 2150eaf192dSyasuoka else 2160eaf192dSyasuoka return (packet->request->pdata->authenticator); 2170eaf192dSyasuoka } 2180eaf192dSyasuoka 2190eaf192dSyasuoka void 2200eaf192dSyasuoka radius_set_request_packet(RADIUS_PACKET * packet, 2210eaf192dSyasuoka const RADIUS_PACKET * request) 2220eaf192dSyasuoka { 2230eaf192dSyasuoka packet->request = request; 2240eaf192dSyasuoka } 2250eaf192dSyasuoka 2260eaf192dSyasuoka const RADIUS_PACKET * 2270eaf192dSyasuoka radius_get_request_packet(const RADIUS_PACKET * packet) 2280eaf192dSyasuoka { 2290eaf192dSyasuoka return (packet->request); 2300eaf192dSyasuoka } 2310eaf192dSyasuoka 2320eaf192dSyasuoka static void 2330eaf192dSyasuoka radius_calc_authenticator(uint8_t * authenticator_dst, 2340eaf192dSyasuoka const RADIUS_PACKET * packet, const uint8_t * authenticator_src, 2350eaf192dSyasuoka const char *secret) 2360eaf192dSyasuoka { 2370eaf192dSyasuoka MD5_CTX ctx; 2380eaf192dSyasuoka 2390eaf192dSyasuoka MD5_Init(&ctx); 2400eaf192dSyasuoka MD5_Update(&ctx, (unsigned char *)packet->pdata, 4); 2410eaf192dSyasuoka MD5_Update(&ctx, (unsigned char *)authenticator_src, 16); 2420eaf192dSyasuoka MD5_Update(&ctx, 2430eaf192dSyasuoka (unsigned char *)packet->pdata->attributes, 2440eaf192dSyasuoka radius_get_length(packet) - 20); 2450eaf192dSyasuoka MD5_Update(&ctx, (unsigned char *)secret, strlen(secret)); 2460eaf192dSyasuoka MD5_Final((unsigned char *)authenticator_dst, &ctx); 2470eaf192dSyasuoka } 2480eaf192dSyasuoka 2490eaf192dSyasuoka static void 2500eaf192dSyasuoka radius_calc_response_authenticator(uint8_t * authenticator_dst, 2510eaf192dSyasuoka const RADIUS_PACKET * packet, const char *secret) 2520eaf192dSyasuoka { 2530eaf192dSyasuoka radius_calc_authenticator(authenticator_dst, 2540eaf192dSyasuoka packet, packet->request->pdata->authenticator, secret); 2550eaf192dSyasuoka } 2560eaf192dSyasuoka 2570eaf192dSyasuoka int 2580eaf192dSyasuoka radius_check_response_authenticator(const RADIUS_PACKET * packet, 2590eaf192dSyasuoka const char *secret) 2600eaf192dSyasuoka { 2610eaf192dSyasuoka uint8_t authenticator[16]; 2620eaf192dSyasuoka 2630eaf192dSyasuoka radius_calc_response_authenticator(authenticator, packet, secret); 264*9c3f005cSyasuoka return (timingsafe_bcmp(authenticator, packet->pdata->authenticator, 2659c0ae8ffSyasuoka 16)); 2660eaf192dSyasuoka } 2670eaf192dSyasuoka 2680eaf192dSyasuoka void 2690eaf192dSyasuoka radius_set_response_authenticator(RADIUS_PACKET * packet, 2700eaf192dSyasuoka const char *secret) 2710eaf192dSyasuoka { 2720eaf192dSyasuoka radius_calc_response_authenticator(packet->pdata->authenticator, 2730eaf192dSyasuoka packet, secret); 2740eaf192dSyasuoka } 2750eaf192dSyasuoka 2760eaf192dSyasuoka static void 2770eaf192dSyasuoka radius_calc_accounting_request_authenticator(uint8_t * authenticator_dst, 2780eaf192dSyasuoka const RADIUS_PACKET * packet, const char *secret) 2790eaf192dSyasuoka { 2800eaf192dSyasuoka uint8_t zero[16]; 2810eaf192dSyasuoka 2820eaf192dSyasuoka memset(zero, 0, sizeof(zero)); 2830eaf192dSyasuoka radius_calc_authenticator(authenticator_dst, 2840eaf192dSyasuoka packet, zero, secret); 2850eaf192dSyasuoka } 2860eaf192dSyasuoka 2870eaf192dSyasuoka void 2880eaf192dSyasuoka radius_set_accounting_request_authenticator(RADIUS_PACKET * packet, 2890eaf192dSyasuoka const char *secret) 2900eaf192dSyasuoka { 2910eaf192dSyasuoka radius_calc_accounting_request_authenticator( 2920eaf192dSyasuoka packet->pdata->authenticator, packet, secret); 2930eaf192dSyasuoka } 2940eaf192dSyasuoka 2950eaf192dSyasuoka int 2960eaf192dSyasuoka radius_check_accounting_request_authenticator(const RADIUS_PACKET * packet, 2970eaf192dSyasuoka const char *secret) 2980eaf192dSyasuoka { 2990eaf192dSyasuoka uint8_t authenticator[16]; 3000eaf192dSyasuoka 3010eaf192dSyasuoka radius_calc_accounting_request_authenticator(authenticator, packet, 3020eaf192dSyasuoka secret); 303*9c3f005cSyasuoka return (timingsafe_bcmp(authenticator, packet->pdata->authenticator, 3049c0ae8ffSyasuoka 16)); 3050eaf192dSyasuoka } 3060eaf192dSyasuoka 3070eaf192dSyasuoka 3080eaf192dSyasuoka uint16_t 3090eaf192dSyasuoka radius_get_length(const RADIUS_PACKET * packet) 3100eaf192dSyasuoka { 3110eaf192dSyasuoka return (ntohs(packet->pdata->length)); 3120eaf192dSyasuoka } 3130eaf192dSyasuoka 3140eaf192dSyasuoka 3150eaf192dSyasuoka const void * 3160eaf192dSyasuoka radius_get_data(const RADIUS_PACKET * packet) 3170eaf192dSyasuoka { 3180eaf192dSyasuoka return (packet->pdata); 3190eaf192dSyasuoka } 3200eaf192dSyasuoka 3210eaf192dSyasuoka RADIUS_PACKET * 3220eaf192dSyasuoka radius_recvfrom(int s, int flags, struct sockaddr * sa, socklen_t * slen) 3230eaf192dSyasuoka { 3240eaf192dSyasuoka char buf[0x10000]; 3250eaf192dSyasuoka ssize_t n; 3260eaf192dSyasuoka 3270eaf192dSyasuoka n = recvfrom(s, buf, sizeof(buf), flags, sa, slen); 3280eaf192dSyasuoka if (n <= 0) 3290eaf192dSyasuoka return (NULL); 3300eaf192dSyasuoka 3310eaf192dSyasuoka return (radius_convert_packet(buf, (size_t) n)); 3320eaf192dSyasuoka } 3330eaf192dSyasuoka 3340eaf192dSyasuoka int 3350eaf192dSyasuoka radius_sendto(int s, const RADIUS_PACKET * packet, 3360eaf192dSyasuoka int flags, const struct sockaddr * sa, socklen_t slen) 3370eaf192dSyasuoka { 3380eaf192dSyasuoka ssize_t n; 3390eaf192dSyasuoka 3400eaf192dSyasuoka n = sendto(s, packet->pdata, radius_get_length(packet), flags, sa, 3410eaf192dSyasuoka slen); 3420eaf192dSyasuoka if (n != radius_get_length(packet)) 3430eaf192dSyasuoka return (-1); 3440eaf192dSyasuoka 3450eaf192dSyasuoka return (0); 3460eaf192dSyasuoka } 3470eaf192dSyasuoka 3480eaf192dSyasuoka RADIUS_PACKET * 3490eaf192dSyasuoka radius_recv(int s, int flags) 3500eaf192dSyasuoka { 3510eaf192dSyasuoka char buf[0x10000]; 3520eaf192dSyasuoka ssize_t n; 3530eaf192dSyasuoka 3540eaf192dSyasuoka n = recv(s, buf, sizeof(buf), flags); 3550eaf192dSyasuoka if (n <= 0) 3560eaf192dSyasuoka return (NULL); 3570eaf192dSyasuoka 3580eaf192dSyasuoka return (radius_convert_packet(buf, (size_t) n)); 3590eaf192dSyasuoka } 3600eaf192dSyasuoka 3610eaf192dSyasuoka int 3620eaf192dSyasuoka radius_send(int s, const RADIUS_PACKET * packet, int flags) 3630eaf192dSyasuoka { 3640eaf192dSyasuoka ssize_t n; 3650eaf192dSyasuoka 3660eaf192dSyasuoka n = send(s, packet->pdata, radius_get_length(packet), flags); 3670eaf192dSyasuoka if (n != radius_get_length(packet)) 3680eaf192dSyasuoka return (-1); 3690eaf192dSyasuoka 3700eaf192dSyasuoka return (0); 3710eaf192dSyasuoka } 3720eaf192dSyasuoka 3730eaf192dSyasuoka RADIUS_PACKET * 3740eaf192dSyasuoka radius_recvmsg(int s, struct msghdr * msg, int flags) 3750eaf192dSyasuoka { 3760eaf192dSyasuoka struct iovec iov; 3770eaf192dSyasuoka char buf[0x10000]; 3780eaf192dSyasuoka ssize_t n; 3790eaf192dSyasuoka 3800eaf192dSyasuoka if (msg->msg_iov != NULL || msg->msg_iovlen != 0) 3810eaf192dSyasuoka return (NULL); 3820eaf192dSyasuoka 3830eaf192dSyasuoka iov.iov_base = buf; 3840eaf192dSyasuoka iov.iov_len = sizeof(buf); 3850eaf192dSyasuoka msg->msg_iov = &iov; 3860eaf192dSyasuoka msg->msg_iovlen = 1; 3870eaf192dSyasuoka n = recvmsg(s, msg, flags); 3880eaf192dSyasuoka msg->msg_iov = NULL; 3890eaf192dSyasuoka msg->msg_iovlen = 0; 3900eaf192dSyasuoka if (n <= 0) 3910eaf192dSyasuoka return (NULL); 3920eaf192dSyasuoka 3930eaf192dSyasuoka return (radius_convert_packet(buf, (size_t) n)); 3940eaf192dSyasuoka } 3950eaf192dSyasuoka 3960eaf192dSyasuoka int 3970eaf192dSyasuoka radius_sendmsg(int s, const RADIUS_PACKET * packet, 3980eaf192dSyasuoka const struct msghdr * msg, int flags) 3990eaf192dSyasuoka { 4000eaf192dSyasuoka struct msghdr msg0; 4010eaf192dSyasuoka struct iovec iov; 4020eaf192dSyasuoka ssize_t n; 4030eaf192dSyasuoka 4040eaf192dSyasuoka if (msg->msg_iov != NULL || msg->msg_iovlen != 0) 4050eaf192dSyasuoka return (-1); 4060eaf192dSyasuoka 4070eaf192dSyasuoka iov.iov_base = packet->pdata; 4080eaf192dSyasuoka iov.iov_len = radius_get_length(packet); 4090eaf192dSyasuoka msg0 = *msg; 4100eaf192dSyasuoka msg0.msg_iov = &iov; 4110eaf192dSyasuoka msg0.msg_iovlen = 1; 4120eaf192dSyasuoka n = sendmsg(s, &msg0, flags); 4130eaf192dSyasuoka if (n != radius_get_length(packet)) 4140eaf192dSyasuoka return (-1); 4150eaf192dSyasuoka 4160eaf192dSyasuoka return (0); 4170eaf192dSyasuoka } 418