xref: /openbsd/lib/libradius/radius.c (revision 73471bf0)
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