radius.c revision 1.1
1/*	$OpenBSD: radius.c,v 1.1 2024/03/24 00:05:01 yasuoka Exp $	*/
2
3/*
4 * Copyright (c) 2024 Internet Initiative Japan Inc.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/queue.h>
21#include <sys/socket.h>
22#include <sys/time.h>
23#include <arpa/inet.h>
24#include <netinet/ip_ipsp.h>
25
26#include <endian.h>
27#include <event.h>
28#include <errno.h>
29#include <imsg.h>
30#include <limits.h>
31#include <netinet/in.h>
32#include <radius.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <strings.h>
38#include <time.h>
39
40#include "iked.h"
41#include "eap.h"
42#include "ikev2.h"
43#include "types.h"
44
45void	 iked_radius_request_send(struct iked *, void *);
46void	 iked_radius_config(struct iked_radserver_req *, const RADIUS_PACKET *,
47	    int, uint32_t, uint8_t);
48void	 iked_radius_acct_request(struct iked *, struct iked_sa *, uint8_t);
49
50const struct iked_radcfgmap radius_cfgmaps[] = {
51    { IKEV2_CFG_INTERNAL_IP4_ADDRESS, 0, RADIUS_TYPE_FRAMED_IP_ADDRESS },
52    { IKEV2_CFG_INTERNAL_IP4_NETMASK, 0, RADIUS_TYPE_FRAMED_IP_NETMASK },
53    { IKEV2_CFG_INTERNAL_IP4_DNS, RADIUS_VENDOR_MICROSOFT,
54	RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER },
55    { IKEV2_CFG_INTERNAL_IP4_DNS, RADIUS_VENDOR_MICROSOFT,
56	RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER },
57    { IKEV2_CFG_INTERNAL_IP4_NBNS, RADIUS_VENDOR_MICROSOFT,
58	RADIUS_VTYPE_MS_PRIMARY_NBNS_SERVER },
59    { IKEV2_CFG_INTERNAL_IP4_NBNS, RADIUS_VENDOR_MICROSOFT,
60	RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER },
61    { 0 }
62};
63
64int
65iked_radius_request(struct iked *env, struct iked_sa *sa,
66    struct iked_message *msg)
67{
68	struct eap_message		*eap;
69	RADIUS_PACKET			*pkt;
70	size_t				 len;
71
72	eap = ibuf_data(msg->msg_eapmsg);
73	len = betoh16(eap->eap_length);
74	if (eap->eap_code != EAP_CODE_RESPONSE) {
75		log_debug("%s: eap_code is not response %u", __func__,
76		    (unsigned)eap->eap_code);
77		return -1;
78	}
79
80	if (eap->eap_type == EAP_TYPE_IDENTITY) {
81		if ((sa->sa_radreq = calloc(1,
82		    sizeof(struct iked_radserver_req))) == NULL) {
83			log_debug(
84			    "%s: calloc failed for iked_radserver_req: %s",
85			    __func__, strerror(errno));
86			return (-1);
87		}
88		timer_set(env, &sa->sa_radreq->rr_timer,
89		    iked_radius_request_send, sa->sa_radreq);
90		sa->sa_radreq->rr_user = strdup(msg->msg_eap.eam_identity);
91	}
92
93	if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST))
94	    == NULL) {
95		log_debug("%s: radius_new_request_packet failed %s", __func__,
96		    strerror(errno));
97		return -1;
98	}
99
100	radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME,
101	    sa->sa_radreq->rr_user);
102	if (sa->sa_radreq->rr_state != NULL)
103		radius_put_raw_attr(pkt, RADIUS_TYPE_STATE,
104		    ibuf_data(sa->sa_radreq->rr_state),
105		    ibuf_size(sa->sa_radreq->rr_state));
106
107	if (radius_put_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE,
108	    (uint8_t *)eap, len) == -1) {
109		log_debug("%s: radius_put_raw_attr_cat failed %s", __func__,
110		    strerror(errno));
111		return -1;
112	}
113
114	/* save the request, it'll be needed for message authentication */
115	if (sa->sa_radreq->rr_reqpkt != NULL)
116		radius_delete_packet(sa->sa_radreq->rr_reqpkt);
117	sa->sa_radreq->rr_reqpkt = pkt;
118	sa->sa_radreq->rr_sa = sa;
119	sa->sa_radreq->rr_ntry = 0;
120
121	iked_radius_request_send(env, sa->sa_radreq);
122
123	return 0;
124}
125
126void
127iked_radius_request_free(struct iked *env, struct iked_radserver_req *req)
128{
129	if (req == NULL)
130		return;
131	timer_del(env, &req->rr_timer);
132	free(req->rr_user);
133	ibuf_free(req->rr_state);
134	if (req->rr_reqpkt)
135		radius_delete_packet(req->rr_reqpkt);
136	if (req->rr_sa)
137		req->rr_sa->sa_radreq = NULL;
138	if (req->rr_server)
139		TAILQ_REMOVE(&req->rr_server->rs_reqs, req, rr_entry);
140	free(req);
141}
142
143void
144iked_radius_on_event(int fd, short ev, void *ctx)
145{
146	struct iked			*env;
147	struct iked_radserver		*server = ctx;
148	struct iked_radserver_req	*req;
149	const struct iked_radcfgmap	*cfgmap;
150	RADIUS_PACKET			*pkt;
151	int				 i, resid;
152	struct ibuf			*e;
153	const void			*attrval;
154	size_t				 attrlen;
155	uint8_t				 code;
156	u_char				 eapmsk[128];
157	/* RFC 3748 defines the MSK minimum size is 64 bytes */
158	size_t				 eapmsksiz = sizeof(eapmsk);
159
160	env = server->rs_env;
161	pkt = radius_recv(server->rs_sock, 0);
162	if (pkt == NULL) {
163		log_info("%s: receiving a RADIUS message failed: %s", __func__,
164		    strerror(errno));
165		return;
166	}
167	resid = radius_get_id(pkt);
168
169	TAILQ_FOREACH(req, &server->rs_reqs, rr_entry) {
170		if (req->rr_reqid == resid)
171			break;
172	}
173	if (req == NULL) {
174		log_debug("%s: received an unknown RADIUS message: id=%u",
175		    __func__, (unsigned)resid);
176		return;
177	}
178
179	radius_set_request_packet(pkt, req->rr_reqpkt);
180	if (radius_check_response_authenticator(pkt, server->rs_secret) != 0) {
181		log_info("%s: received an invalid RADIUS message: bad "
182		    "response authenticator", __func__);
183		return;
184	}
185	if (req->rr_accounting) {
186		/* accounting */
187		code = radius_get_code(pkt);
188		switch (code) {
189		case RADIUS_CODE_ACCOUNTING_RESPONSE: /* Expected */
190			break;
191		default:
192			log_info("%s: received an invalid RADIUS message: "
193			    "code %u", __func__, (unsigned)code);
194		}
195		timer_del(env, &req->rr_timer);
196		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
197		req->rr_server = NULL;
198		free(req);
199		return;
200	}
201
202	/* authentication */
203	if (radius_check_message_authenticator(pkt, server->rs_secret) != 0) {
204		log_info("%s: received an invalid RADIUS message: bad "
205		    "message authenticator", __func__);
206		return;
207	}
208
209	timer_del(env, &req->rr_timer);
210	req->rr_ntry = 0;
211
212	if (req->rr_sa == NULL)
213		goto fail;
214
215	code = radius_get_code(pkt);
216	switch (code) {
217	case RADIUS_CODE_ACCESS_CHALLENGE:
218		if (radius_get_raw_attr_ptr(pkt, RADIUS_TYPE_STATE, &attrval,
219		    &attrlen) != 0) {
220			log_info("%s: received an invalid RADIUS message: no "
221			    "state attribute", __func__);
222			goto fail;
223		}
224		if ((req->rr_state != NULL &&
225		    ibuf_set(req->rr_state, 0, attrval, attrlen) != 0) ||
226		    (req->rr_state = ibuf_new(attrval, attrlen)) == NULL) {
227			log_info("%s: ibuf_new() failed: %s", __func__,
228			    strerror(errno));
229			goto fail;
230		}
231		break;
232	case RADIUS_CODE_ACCESS_ACCEPT:
233		log_info("%s: received Access-Accept for %s",
234		    SPI_SA(req->rr_sa, __func__), req->rr_user);
235		/* Try to retrieve the EAP MSK from the RADIUS response */
236		if (radius_get_eap_msk(pkt, eapmsk, &eapmsksiz,
237		    server->rs_secret) == 0) {
238			ibuf_free(req->rr_sa->sa_eapmsk);
239			if ((req->rr_sa->sa_eapmsk = ibuf_new(eapmsk,
240			    eapmsksiz)) == NULL) {
241				log_info("%s: ibuf_new() failed: %s", __func__,
242				    strerror(errno));
243				goto fail;
244			}
245		} else
246			log_debug("Could not retrieve the EAP MSK from the "
247			    "RADIUS message");
248		free(req->rr_sa->sa_eapid);
249		req->rr_sa->sa_eapid = req->rr_user;
250		req->rr_user = NULL;
251		sa_state(env, req->rr_sa, IKEV2_STATE_AUTH_SUCCESS);
252
253		/* Map RADIUS attributes to cp */
254		if (TAILQ_EMPTY(&env->sc_radcfgmaps)) {
255			for (i = 0; radius_cfgmaps[i].cfg_type != 0; i++) {
256				cfgmap = &radius_cfgmaps[i];
257				iked_radius_config(req, pkt, cfgmap->cfg_type,
258				    cfgmap->vendor_id, cfgmap->attr_type);
259			}
260		} else {
261			TAILQ_FOREACH(cfgmap, &env->sc_radcfgmaps, entry)
262				iked_radius_config(req, pkt, cfgmap->cfg_type,
263				    cfgmap->vendor_id, cfgmap->attr_type);
264		}
265
266		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
267		req->rr_server = NULL;
268		break;
269	case RADIUS_CODE_ACCESS_REJECT:
270		log_info("%s: received Access-Reject for %s",
271		    SPI_SA(req->rr_sa, __func__), req->rr_user);
272		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
273		req->rr_server = NULL;
274		break;
275	default:
276		log_debug("%s: received an invalid RADIUS message: code %u",
277		    __func__, (unsigned)code);
278		break;
279	}
280
281	/* get the length first */
282	if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, NULL,
283	    &attrlen) != 0) {
284		log_info("%s: failed to retrieve the EAP message", __func__);
285		goto fail;
286	}
287	/* allocate a buffer */
288	if ((e = ibuf_new(NULL, attrlen)) == NULL) {
289		log_info("%s: ibuf_new() failed: %s", __func__,
290		    strerror(errno));
291		goto fail;
292	}
293	/* copy the message to the buffer */
294	if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE,
295	    ibuf_data(e), &attrlen) != 0) {
296		ibuf_free(e);
297		log_info("%s: failed to retrieve the EAP message", __func__);
298		goto fail;
299	}
300	ikev2_send_ike_e(env, req->rr_sa, e, IKEV2_PAYLOAD_EAP,
301	    IKEV2_EXCHANGE_IKE_AUTH, 1);
302	return;
303 fail:
304	if (req->rr_server != NULL)
305		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
306	req->rr_server = NULL;
307	if (req->rr_sa != NULL) {
308		ikev2_ike_sa_setreason(req->rr_sa, "RADIUS request failed");
309		sa_free(env, req->rr_sa);
310	}
311}
312
313void
314iked_radius_request_send(struct iked *env, void *ctx)
315{
316	struct iked_radserver_req	*req = ctx, *req0;
317	struct iked_radserver		*server = req->rr_server;
318	const int			 timeouts[] = { 2, 4, 8 };
319	uint8_t				 seq;
320	int				 i, max_tries, max_failovers;
321	struct sockaddr_storage		 ss;
322	socklen_t			 sslen;
323	struct iked_radservers		*radservers;
324	struct timespec			 now;
325
326	if (!req->rr_accounting) {
327		max_tries = env->sc_radauth.max_tries;
328		max_failovers = env->sc_radauth.max_failovers;
329		radservers = &env->sc_radauthservers;
330	} else {
331		max_tries = env->sc_radacct.max_tries;
332		max_failovers = env->sc_radacct.max_failovers;
333		radservers = &env->sc_radacctservers;
334	}
335
336	if (req->rr_ntry > max_tries) {
337		req->rr_ntry = 0;
338		log_info("%s: RADIUS server %s failed", __func__,
339		    print_addr(&server->rs_sockaddr));
340 next_server:
341		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
342		req->rr_server = NULL;
343		if (req->rr_nfailover >= max_failovers ||
344		    TAILQ_NEXT(server, rs_entry) == NULL) {
345			log_info("%s: No more RADIUS server", __func__);
346			goto fail;
347		} else if (req->rr_state != NULL) {
348			log_info("%s: Can't change RADIUS server: "
349			    "client has a state already", __func__);
350			goto fail;
351		} else {
352			TAILQ_REMOVE(radservers, server, rs_entry);
353			TAILQ_INSERT_TAIL(radservers, server, rs_entry);
354			server = TAILQ_FIRST(radservers);
355			log_info("%s: RADIUS server %s is active",
356			    __func__, print_addr(&server->rs_sockaddr));
357		}
358		req->rr_nfailover++;
359	}
360
361	if (req->rr_server != NULL &&
362	    req->rr_server != TAILQ_FIRST(radservers)) {
363		/* Current server is marked fail */
364		if (req->rr_state != NULL || req->rr_nfailover >= max_failovers)
365			goto fail; /* can't fail over */
366		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
367		req->rr_server = NULL;
368		req->rr_nfailover++;
369	}
370
371	if (req->rr_server == NULL) {
372		/* Select a new server */
373		server = TAILQ_FIRST(radservers);
374		if (server == NULL) {
375			log_info("%s: No RADIUS server is configured",
376			    __func__);
377			goto fail;
378		}
379		TAILQ_INSERT_TAIL(&server->rs_reqs, req, rr_entry);
380		req->rr_server = server;
381
382		/* Prepare NAS-IP-Address */
383		if (server->rs_nas_ipv4.s_addr == INADDR_ANY &&
384		    IN6_IS_ADDR_UNSPECIFIED(&server->rs_nas_ipv6)) {
385			sslen = sizeof(ss);
386			if (getsockname(server->rs_sock, (struct sockaddr *)&ss,
387			    &sslen) == 0) {
388				if (ss.ss_family == AF_INET)
389					server->rs_nas_ipv4 =
390					    ((struct sockaddr_in *)&ss)
391					    ->sin_addr;
392				else
393					server->rs_nas_ipv6 =
394					    ((struct sockaddr_in6 *)&ss)
395					    ->sin6_addr;
396			}
397		}
398	}
399	if (req->rr_ntry == 0) {
400		/* decide the ID */
401		seq = ++server->rs_reqseq;
402		for (i = 0; i < UCHAR_MAX; i++) {
403			TAILQ_FOREACH(req0, &server->rs_reqs, rr_entry) {
404				if (req0->rr_reqid == seq)
405					break;
406			}
407			if (req0 == NULL)
408				break;
409			seq++;
410		}
411		if (i >= UCHAR_MAX) {
412			log_info("%s: RADIUS server %s failed.  Too many "
413			    "pending requests", __func__,
414			    print_addr(&server->rs_sockaddr));
415			if (TAILQ_NEXT(server, rs_entry) != NULL)
416				goto next_server;
417			goto fail;
418		}
419		req->rr_reqid = seq;
420		radius_set_id(req->rr_reqpkt, req->rr_reqid);
421	}
422
423	if (server->rs_nas_ipv4.s_addr != INADDR_ANY)
424		radius_put_ipv4_attr(req->rr_reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS,
425		    server->rs_nas_ipv4);
426	else if (!IN6_IS_ADDR_UNSPECIFIED(&server->rs_nas_ipv6))
427		radius_put_ipv6_attr(req->rr_reqpkt,
428		    RADIUS_TYPE_NAS_IPV6_ADDRESS, &server->rs_nas_ipv6);
429	/* Identifier */
430	radius_put_string_attr(req->rr_reqpkt, RADIUS_TYPE_NAS_IDENTIFIER,
431	    "OpenIKED");
432
433	/* NAS Port Type = Virtual */
434	radius_put_uint32_attr(req->rr_reqpkt,
435	    RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL);
436	/* Service Type =  Framed */
437	radius_put_uint32_attr(req->rr_reqpkt,
438	    RADIUS_TYPE_SERVICE_TYPE, RADIUS_SERVICE_TYPE_FRAMED);
439
440	if (req->rr_accounting) {
441		if (req->rr_ntry == 0 && req->rr_nfailover == 0)
442			radius_put_uint32_attr(req->rr_reqpkt,
443			    RADIUS_TYPE_ACCT_DELAY_TIME, 0);
444		else {
445			clock_gettime(CLOCK_MONOTONIC, &now);
446			timespecsub(&now, &req->rr_accttime, &now);
447			radius_put_uint32_attr(req->rr_reqpkt,
448			    RADIUS_TYPE_ACCT_DELAY_TIME, now.tv_sec);
449		}
450		radius_set_accounting_request_authenticator(req->rr_reqpkt,
451		    server->rs_secret);
452	} else {
453		radius_put_message_authenticator(req->rr_reqpkt,
454		    server->rs_secret);
455	}
456
457	if (radius_send(server->rs_sock, req->rr_reqpkt, 0) < 0)
458		log_info("%s: sending a RADIUS message failed: %s", __func__,
459		    strerror(errno));
460
461	if (req->rr_ntry >= (int)nitems(timeouts))
462		timer_add(env, &req->rr_timer, timeouts[nitems(timeouts) - 1]);
463	else
464		timer_add(env, &req->rr_timer, timeouts[req->rr_ntry]);
465	req->rr_ntry++;
466	return;
467 fail:
468	if (req->rr_server != NULL)
469		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
470	req->rr_server = NULL;
471	if (req->rr_sa != NULL) {
472		ikev2_ike_sa_setreason(req->rr_sa, "RADIUS request failed");
473		sa_free(env, req->rr_sa);
474	}
475}
476
477void
478iked_radius_config(struct iked_radserver_req *req, const RADIUS_PACKET *pkt,
479    int cfg_type, uint32_t vendor_id, uint8_t attr_type)
480{
481	unsigned int		 i;
482	struct iked_sa		*sa = req->rr_sa;
483	struct in_addr		 ia4;
484	struct in6_addr		 ia6;
485	struct sockaddr_in	*sin4;
486	struct sockaddr_in6	*sin6;
487	struct iked_addr	*addr;
488	struct iked_cfg		*ikecfg;
489
490	for (i = 0; i < sa->sa_policy->pol_ncfg; i++) {
491		ikecfg = &sa->sa_policy->pol_cfg[i];
492		if (ikecfg->cfg_type == cfg_type &&
493		    ikecfg->cfg_type != IKEV2_CFG_INTERNAL_IP4_ADDRESS)
494			return;	/* use config rather than radius */
495	}
496	switch (cfg_type) {
497	case IKEV2_CFG_INTERNAL_IP4_ADDRESS:
498	case IKEV2_CFG_INTERNAL_IP4_NETMASK:
499	case IKEV2_CFG_INTERNAL_IP4_DNS:
500	case IKEV2_CFG_INTERNAL_IP4_NBNS:
501	case IKEV2_CFG_INTERNAL_IP4_DHCP:
502	case IKEV2_CFG_INTERNAL_IP4_SERVER:
503		if (vendor_id == 0 && radius_has_attr(pkt, attr_type))
504			radius_get_ipv4_attr(pkt, attr_type, &ia4);
505		else if (vendor_id != 0 && radius_has_vs_attr(pkt, vendor_id,
506		    attr_type))
507			radius_get_vs_ipv4_attr(pkt, vendor_id, attr_type,
508			    &ia4);
509		else
510			break; /* no attribute contained */
511
512		if (cfg_type == IKEV2_CFG_INTERNAL_IP4_NETMASK) {
513			/*
514			 * This assumes IKEV2_CFG_INTERNAL_IP4_ADDRESS is
515			 * called before IKEV2_CFG_INTERNAL_IP4_NETMASK
516			 */
517			if (sa->sa_rad_addr == NULL) {
518				/*
519				 * RFC 7296, IKEV2_CFG_INTERNAL_IP4_NETMASK
520				 * must be used with
521				 * IKEV2_CFG_INTERNAL_IP4_ADDRESS
522				 */
523				break;
524			}
525			if (ia4.s_addr == 0) {
526				log_debug("%s: netmask is wrong", __func__);
527				break;
528			}
529			if (ia4.s_addr == htonl(0))
530				sa->sa_rad_addr->addr_mask = 0;
531			else
532				sa->sa_rad_addr->addr_mask =
533				    33 - ffs(ntohl(ia4.s_addr));
534			if (sa->sa_rad_addr->addr_mask < 32)
535				sa->sa_rad_addr->addr_net = 1;
536		}
537		if (cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) {
538			if ((addr = calloc(1, sizeof(*addr))) == NULL) {
539				log_warn("%s: calloc", __func__);
540				return;
541			}
542			sa->sa_rad_addr = addr;
543		} else {
544			req->rr_cfg[req->rr_ncfg].cfg_action = IKEV2_CP_REPLY;
545			req->rr_cfg[req->rr_ncfg].cfg_type = cfg_type;
546			addr = &req->rr_cfg[req->rr_ncfg].cfg.address;
547			req->rr_ncfg++;
548		}
549		addr->addr_af = AF_INET;
550		sin4 = (struct sockaddr_in *)&addr->addr;
551		sin4->sin_family = AF_INET;
552		sin4->sin_len = sizeof(struct sockaddr_in);
553		sin4->sin_addr = ia4;
554		break;
555	case IKEV2_CFG_INTERNAL_IP6_ADDRESS:
556	case IKEV2_CFG_INTERNAL_IP6_DNS:
557	case IKEV2_CFG_INTERNAL_IP6_NBNS:
558	case IKEV2_CFG_INTERNAL_IP6_DHCP:
559	case IKEV2_CFG_INTERNAL_IP6_SERVER:
560		if (vendor_id == 0 && radius_has_attr(pkt, attr_type))
561			radius_get_ipv6_attr(pkt, attr_type, &ia6);
562		else if (vendor_id != 0 && radius_has_vs_attr(pkt, vendor_id,
563		    attr_type))
564			radius_get_vs_ipv6_attr(pkt, vendor_id, attr_type,
565			    &ia6);
566		else
567			break; /* no attribute contained */
568
569		if (cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS) {
570			if ((addr = calloc(1, sizeof(*addr))) == NULL) {
571				log_warn("%s: calloc", __func__);
572				return;
573			}
574			sa->sa_rad_addr = addr;
575		} else {
576			req->rr_cfg[req->rr_ncfg].cfg_action = IKEV2_CP_REPLY;
577			req->rr_cfg[req->rr_ncfg].cfg_type = cfg_type;
578			addr = &req->rr_cfg[req->rr_ncfg].cfg.address;
579			req->rr_ncfg++;
580		}
581		addr->addr_af = AF_INET;
582		sin6 = (struct sockaddr_in6 *)&addr->addr;
583		sin6->sin6_family = AF_INET6;
584		sin6->sin6_len = sizeof(struct sockaddr_in6);
585		sin6->sin6_addr = ia6;
586		break;
587	}
588	return;
589}
590
591void
592iked_radius_acct_on(struct iked *env)
593{
594	if (TAILQ_EMPTY(&env->sc_radacctservers))
595		return;
596	if (env->sc_radaccton == 0) {	/* trigger once */
597		iked_radius_acct_request(env, NULL, RADIUS_ACCT_STATUS_TYPE_ACCT_ON);
598		env->sc_radaccton = 1;
599	}
600}
601
602void
603iked_radius_acct_off(struct iked *env)
604{
605	iked_radius_acct_request(env, NULL, RADIUS_ACCT_STATUS_TYPE_ACCT_OFF);
606}
607
608void
609iked_radius_acct_start(struct iked *env, struct iked_sa *sa)
610{
611	iked_radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_START);
612}
613
614void
615iked_radius_acct_stop(struct iked *env, struct iked_sa *sa)
616{
617	iked_radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_STOP);
618}
619
620void
621iked_radius_acct_request(struct iked *env, struct iked_sa *sa, uint8_t stype)
622{
623	struct iked_radserver_req	*req;
624	RADIUS_PACKET			*pkt;
625	struct iked_addr		*addr4 = NULL;
626	struct iked_addr		*addr6 = NULL;
627	struct in_addr			 mask4;
628	char				 sa_id[IKED_ID_SIZE];
629	char				 sid[16 + 1];
630	struct timespec			 now;
631	int				 cause;
632
633	if (TAILQ_EMPTY(&env->sc_radacctservers))
634		return;
635	/*
636	 * In RFC2866 5.6, "Users who are delivered service without
637	 * being authenticated SHOULD NOT generate Accounting records
638	 */
639	if (sa != NULL && sa->sa_eapid == NULL) {
640		/* fallback to IKEID for accounting */
641		if (ikev2_print_id(IKESA_DSTID(sa), sa_id, sizeof(sa_id)) != -1)
642			sa->sa_eapid = strdup(sa_id);
643		if (sa->sa_eapid == NULL)
644			return;
645	}
646
647	if ((req = calloc(1, sizeof(struct iked_radserver_req))) == NULL) {
648		log_debug("%s: calloc faile for iked_radserver_req: %s",
649		    __func__, strerror(errno));
650		return;
651	}
652	req->rr_accounting = 1;
653	clock_gettime(CLOCK_MONOTONIC, &now);
654	req->rr_accttime = now;
655	timer_set(env, &req->rr_timer, iked_radius_request_send, req);
656
657	if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCOUNTING_REQUEST))
658	    == NULL) {
659		log_debug("%s: radius_new_request_packet failed %s", __func__,
660		    strerror(errno));
661		return;
662	}
663
664	/* RFC 2866  5.1. Acct-Status-Type */
665	radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_STATUS_TYPE, stype);
666
667	if (sa == NULL) {
668		/* ASSERT(stype == RADIUS_ACCT_STATUS_TYPE_ACCT_ON ||
669		    stype == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) */
670		req->rr_reqpkt = pkt;
671		req->rr_ntry = 0;
672		iked_radius_request_send(env, req);
673		return;
674	}
675
676	radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, sa->sa_eapid);
677
678	/* RFC 2866  5.5. Acct-Session-Id */
679	snprintf(sid, sizeof(sid), "%016llx",
680	    (unsigned long long)sa->sa_hdr.sh_ispi);
681	radius_put_string_attr(pkt, RADIUS_TYPE_ACCT_SESSION_ID, sid);
682
683	/* Accounting Request must have Framed-IP-Address */
684	addr4 = sa->sa_addrpool;
685	if (addr4 != NULL) {
686		radius_put_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_ADDRESS,
687		    ((struct sockaddr_in *)&addr4->addr)->sin_addr);
688		if (addr4->addr_mask != 0) {
689			mask4.s_addr = htonl(
690			    0xFFFFFFFFUL << (32 - addr4->addr_mask));
691			radius_put_ipv4_attr(pkt,
692			    RADIUS_TYPE_FRAMED_IP_NETMASK, mask4);
693		}
694	}
695	addr6 = sa->sa_addrpool6;
696	if (addr6 != NULL)
697		radius_put_ipv6_attr(pkt, RADIUS_TYPE_FRAMED_IPV6_ADDRESS,
698		    &((struct sockaddr_in6 *)&addr6->addr)->sin6_addr);
699
700	/* RFC2866 5.6 Acct-Authentic */
701	radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_AUTHENTIC,
702	    (sa->sa_radreq != NULL)? RADIUS_ACCT_AUTHENTIC_RADIUS :
703	    RADIUS_ACCT_AUTHENTIC_LOCAL);
704
705	switch (stype) {
706	case RADIUS_ACCT_STATUS_TYPE_START:
707		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_STATUS_TYPE,
708		    RADIUS_ACCT_STATUS_TYPE_START);
709		break;
710	case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
711	case RADIUS_ACCT_STATUS_TYPE_STOP:
712		/* RFC 2866 5.7.  Acct-Session-Time */
713		timespecsub(&now, &sa->sa_starttime, &now);
714		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_SESSION_TIME,
715		    now.tv_sec);
716		/* RFC 2866 5.10 Acct-Terminate-Cause */
717		cause = RADIUS_TERMNATE_CAUSE_SERVICE_UNAVAIL;
718		if (sa->sa_reason) {
719			if (strcmp(sa->sa_reason, "received delete") == 0) {
720				cause = RADIUS_TERMNATE_CAUSE_USER_REQUEST;
721			} else if (strcmp(sa->sa_reason, "SA rekeyed") == 0) {
722				cause = RADIUS_TERMNATE_CAUSE_SESSION_TIMEOUT;
723			} else if (strncmp(sa->sa_reason, "retransmit",
724			    strlen("retransmit")) == 0) {
725				cause = RADIUS_TERMNATE_CAUSE_LOST_SERVICE;
726			}
727		}
728		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_TERMINATE_CAUSE,
729		    cause);
730		/* I/O statistics {Input,Output}-{Packets,Octets,Gigawords} */
731		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_PACKETS,
732		    sa->sa_stats.sas_ipackets);
733		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_PACKETS,
734		    sa->sa_stats.sas_opackets);
735		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_OCTETS,
736		    sa->sa_stats.sas_ibytes & 0xffffffffUL);
737		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_OCTETS,
738		    sa->sa_stats.sas_obytes & 0xffffffffUL);
739		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_GIGAWORDS,
740		    sa->sa_stats.sas_ibytes >> 32);
741		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS,
742		    sa->sa_stats.sas_obytes >> 32);
743		radius_put_string_attr(pkt, RADIUS_TYPE_CALLED_STATION_ID,
744		    print_addr(&sa->sa_local.addr));
745		radius_put_string_attr(pkt, RADIUS_TYPE_CALLING_STATION_ID,
746		    print_addr(&sa->sa_peer.addr));
747		break;
748	}
749	req->rr_reqpkt = pkt;
750	req->rr_ntry = 0;
751	iked_radius_request_send(env, req);
752}
753