1 /* $OpenBSD: radius.c,v 1.3 2015/08/20 21:51:42 deraadt 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 <sys/types.h> 30 #include <sys/socket.h> 31 #include <netinet/in.h> 32 33 #include <sys/uio.h> 34 #include <stdbool.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <openssl/md5.h> 40 41 #include "radius.h" 42 43 #include "radius_local.h" 44 45 static uint8_t radius_id_counter = 0; 46 47 static int 48 radius_check_packet_data(const RADIUS_PACKET_DATA * pdata, size_t length) 49 { 50 const RADIUS_ATTRIBUTE *attr; 51 const RADIUS_ATTRIBUTE *end; 52 53 if (length < sizeof(RADIUS_PACKET_DATA)) 54 return (-1); 55 if (length > 0xffff) 56 return (-1); 57 if (length != (size_t) ntohs(pdata->length)) 58 return (-1); 59 60 attr = ATTRS_BEGIN(pdata); 61 end = ATTRS_END(pdata); 62 for (; attr < end; ATTRS_ADVANCE(attr)) { 63 if (attr->length < 2) 64 return (-1); 65 if (attr->type == RADIUS_TYPE_VENDOR_SPECIFIC) { 66 if (attr->length < 8) 67 return (-1); 68 if ((attr->vendor & htonl(0xff000000U)) != 0) 69 return (-1); 70 if (attr->length != attr->vlength + 6) 71 return (-1); 72 } 73 } 74 75 if (attr != end) 76 return (-1); 77 78 return (0); 79 } 80 81 int 82 radius_ensure_add_capacity(RADIUS_PACKET * packet, size_t capacity) 83 { 84 size_t newsize; 85 void *newptr; 86 87 /* 88 * The maximum size is 64KB. 89 * We use little bit smaller value for our safety(?). 90 */ 91 if (ntohs(packet->pdata->length) + capacity > 0xfe00) 92 return (-1); 93 94 if (ntohs(packet->pdata->length) + capacity > packet->capacity) { 95 newsize = ntohs(packet->pdata->length) + capacity + 96 RADIUS_PACKET_CAPACITY_INCREMENT; 97 newptr = realloc(packet->pdata, newsize); 98 if (newptr == NULL) 99 return (-1); 100 packet->capacity = newsize; 101 packet->pdata = (RADIUS_PACKET_DATA *)newptr; 102 } 103 return (0); 104 } 105 106 RADIUS_PACKET * 107 radius_new_request_packet(uint8_t code) 108 { 109 RADIUS_PACKET *packet; 110 111 packet = malloc(sizeof(RADIUS_PACKET)); 112 if (packet == NULL) 113 return (NULL); 114 packet->pdata = malloc(RADIUS_PACKET_CAPACITY_INITIAL); 115 if (packet->pdata == NULL) { 116 free(packet); 117 return (NULL); 118 } 119 packet->capacity = RADIUS_PACKET_CAPACITY_INITIAL; 120 packet->request = NULL; 121 packet->pdata->code = code; 122 packet->pdata->id = radius_id_counter++; 123 packet->pdata->length = htons(sizeof(RADIUS_PACKET_DATA)); 124 arc4random_buf(packet->pdata->authenticator, 125 sizeof(packet->pdata->authenticator)); 126 127 return (packet); 128 } 129 130 RADIUS_PACKET * 131 radius_new_response_packet(uint8_t code, const RADIUS_PACKET * request) 132 { 133 RADIUS_PACKET *packet; 134 135 packet = radius_new_request_packet(code); 136 if (packet == NULL) 137 return (NULL); 138 packet->request = request; 139 packet->pdata->id = request->pdata->id; 140 141 return (packet); 142 } 143 144 RADIUS_PACKET * 145 radius_convert_packet(const void *pdata, size_t length) 146 { 147 RADIUS_PACKET *packet; 148 149 if (radius_check_packet_data((const RADIUS_PACKET_DATA *)pdata, 150 length) != 0) 151 return (NULL); 152 packet = malloc(sizeof(RADIUS_PACKET)); 153 if (packet == NULL) 154 return (NULL); 155 packet->pdata = malloc(length); 156 packet->capacity = length; 157 packet->request = NULL; 158 if (packet->pdata == NULL) { 159 free(packet); 160 return (NULL); 161 } 162 memcpy(packet->pdata, pdata, length); 163 164 return (packet); 165 } 166 167 int 168 radius_delete_packet(RADIUS_PACKET * packet) 169 { 170 free(packet->pdata); 171 free(packet); 172 return (0); 173 } 174 175 uint8_t 176 radius_get_code(const RADIUS_PACKET * packet) 177 { 178 return (packet->pdata->code); 179 } 180 181 uint8_t 182 radius_get_id(const RADIUS_PACKET * packet) 183 { 184 return (packet->pdata->id); 185 } 186 187 void 188 radius_update_id(RADIUS_PACKET * packet) 189 { 190 packet->pdata->id = radius_id_counter++; 191 } 192 193 void 194 radius_set_id(RADIUS_PACKET * packet, uint8_t id) 195 { 196 packet->pdata->id = id; 197 } 198 199 void 200 radius_get_authenticator(const RADIUS_PACKET * packet, void *authenticator) 201 { 202 memcpy(authenticator, packet->pdata->authenticator, 16); 203 } 204 205 uint8_t * 206 radius_get_authenticator_retval(const RADIUS_PACKET * packet) 207 { 208 return (packet->pdata->authenticator); 209 } 210 211 uint8_t * 212 radius_get_request_authenticator_retval(const RADIUS_PACKET * packet) 213 { 214 if (packet->request == NULL) 215 return (packet->pdata->authenticator); 216 else 217 return (packet->request->pdata->authenticator); 218 } 219 220 void 221 radius_set_request_packet(RADIUS_PACKET * packet, 222 const RADIUS_PACKET * request) 223 { 224 packet->request = request; 225 } 226 227 const RADIUS_PACKET * 228 radius_get_request_packet(const RADIUS_PACKET * packet) 229 { 230 return (packet->request); 231 } 232 233 static void 234 radius_calc_authenticator(uint8_t * authenticator_dst, 235 const RADIUS_PACKET * packet, const uint8_t * authenticator_src, 236 const char *secret) 237 { 238 MD5_CTX ctx; 239 240 MD5_Init(&ctx); 241 MD5_Update(&ctx, (unsigned char *)packet->pdata, 4); 242 MD5_Update(&ctx, (unsigned char *)authenticator_src, 16); 243 MD5_Update(&ctx, 244 (unsigned char *)packet->pdata->attributes, 245 radius_get_length(packet) - 20); 246 MD5_Update(&ctx, (unsigned char *)secret, strlen(secret)); 247 MD5_Final((unsigned char *)authenticator_dst, &ctx); 248 } 249 250 static void 251 radius_calc_response_authenticator(uint8_t * authenticator_dst, 252 const RADIUS_PACKET * packet, const char *secret) 253 { 254 radius_calc_authenticator(authenticator_dst, 255 packet, packet->request->pdata->authenticator, secret); 256 } 257 258 int 259 radius_check_response_authenticator(const RADIUS_PACKET * packet, 260 const char *secret) 261 { 262 uint8_t authenticator[16]; 263 264 radius_calc_response_authenticator(authenticator, packet, secret); 265 return (memcmp(authenticator, packet->pdata->authenticator, 16)); 266 } 267 268 void 269 radius_set_response_authenticator(RADIUS_PACKET * packet, 270 const char *secret) 271 { 272 radius_calc_response_authenticator(packet->pdata->authenticator, 273 packet, secret); 274 } 275 276 static void 277 radius_calc_accounting_request_authenticator(uint8_t * authenticator_dst, 278 const RADIUS_PACKET * packet, const char *secret) 279 { 280 uint8_t zero[16]; 281 282 memset(zero, 0, sizeof(zero)); 283 radius_calc_authenticator(authenticator_dst, 284 packet, zero, secret); 285 } 286 287 void 288 radius_set_accounting_request_authenticator(RADIUS_PACKET * packet, 289 const char *secret) 290 { 291 radius_calc_accounting_request_authenticator( 292 packet->pdata->authenticator, packet, secret); 293 } 294 295 int 296 radius_check_accounting_request_authenticator(const RADIUS_PACKET * packet, 297 const char *secret) 298 { 299 uint8_t authenticator[16]; 300 301 radius_calc_accounting_request_authenticator(authenticator, packet, 302 secret); 303 return (memcmp(authenticator, packet->pdata->authenticator, 16)); 304 } 305 306 307 uint16_t 308 radius_get_length(const RADIUS_PACKET * packet) 309 { 310 return (ntohs(packet->pdata->length)); 311 } 312 313 314 const void * 315 radius_get_data(const RADIUS_PACKET * packet) 316 { 317 return (packet->pdata); 318 } 319 320 RADIUS_PACKET * 321 radius_recvfrom(int s, int flags, struct sockaddr * sa, socklen_t * slen) 322 { 323 char buf[0x10000]; 324 ssize_t n; 325 326 n = recvfrom(s, buf, sizeof(buf), flags, sa, slen); 327 if (n <= 0) 328 return (NULL); 329 330 return (radius_convert_packet(buf, (size_t) n)); 331 } 332 333 int 334 radius_sendto(int s, const RADIUS_PACKET * packet, 335 int flags, const struct sockaddr * sa, socklen_t slen) 336 { 337 ssize_t n; 338 339 n = sendto(s, packet->pdata, radius_get_length(packet), flags, sa, 340 slen); 341 if (n != radius_get_length(packet)) 342 return (-1); 343 344 return (0); 345 } 346 347 RADIUS_PACKET * 348 radius_recv(int s, int flags) 349 { 350 char buf[0x10000]; 351 ssize_t n; 352 353 n = recv(s, buf, sizeof(buf), flags); 354 if (n <= 0) 355 return (NULL); 356 357 return (radius_convert_packet(buf, (size_t) n)); 358 } 359 360 int 361 radius_send(int s, const RADIUS_PACKET * packet, int flags) 362 { 363 ssize_t n; 364 365 n = send(s, packet->pdata, radius_get_length(packet), flags); 366 if (n != radius_get_length(packet)) 367 return (-1); 368 369 return (0); 370 } 371 372 RADIUS_PACKET * 373 radius_recvmsg(int s, struct msghdr * msg, int flags) 374 { 375 struct iovec iov; 376 char buf[0x10000]; 377 ssize_t n; 378 379 if (msg->msg_iov != NULL || msg->msg_iovlen != 0) 380 return (NULL); 381 382 iov.iov_base = buf; 383 iov.iov_len = sizeof(buf); 384 msg->msg_iov = &iov; 385 msg->msg_iovlen = 1; 386 n = recvmsg(s, msg, flags); 387 msg->msg_iov = NULL; 388 msg->msg_iovlen = 0; 389 if (n <= 0) 390 return (NULL); 391 392 return (radius_convert_packet(buf, (size_t) n)); 393 } 394 395 int 396 radius_sendmsg(int s, const RADIUS_PACKET * packet, 397 const struct msghdr * msg, int flags) 398 { 399 struct msghdr msg0; 400 struct iovec iov; 401 ssize_t n; 402 403 if (msg->msg_iov != NULL || msg->msg_iovlen != 0) 404 return (-1); 405 406 iov.iov_base = packet->pdata; 407 iov.iov_len = radius_get_length(packet); 408 msg0 = *msg; 409 msg0.msg_iov = &iov; 410 msg0.msg_iovlen = 1; 411 n = sendmsg(s, &msg0, flags); 412 if (n != radius_get_length(packet)) 413 return (-1); 414 415 return (0); 416 } 417