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