xref: /openbsd-src/sbin/iked/radius.c (revision 9ca241fcbd1e3e57a03aa8097496cb954222290b)
1*9ca241fcSyasuoka /*	$OpenBSD: radius.c,v 1.13 2024/09/15 11:08:50 yasuoka Exp $	*/
2f36db9c4Syasuoka 
3f36db9c4Syasuoka /*
4f36db9c4Syasuoka  * Copyright (c) 2024 Internet Initiative Japan Inc.
5f36db9c4Syasuoka  *
6f36db9c4Syasuoka  * Permission to use, copy, modify, and distribute this software for any
7f36db9c4Syasuoka  * purpose with or without fee is hereby granted, provided that the above
8f36db9c4Syasuoka  * copyright notice and this permission notice appear in all copies.
9f36db9c4Syasuoka  *
10f36db9c4Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f36db9c4Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f36db9c4Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f36db9c4Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f36db9c4Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f36db9c4Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f36db9c4Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f36db9c4Syasuoka  */
18f36db9c4Syasuoka 
19f36db9c4Syasuoka #include <sys/types.h>
20f36db9c4Syasuoka #include <sys/queue.h>
21f36db9c4Syasuoka #include <sys/socket.h>
22f36db9c4Syasuoka #include <sys/time.h>
23f36db9c4Syasuoka #include <arpa/inet.h>
24f36db9c4Syasuoka #include <netinet/ip_ipsp.h>
25f36db9c4Syasuoka 
26f36db9c4Syasuoka #include <endian.h>
27f36db9c4Syasuoka #include <event.h>
28f36db9c4Syasuoka #include <errno.h>
29f36db9c4Syasuoka #include <imsg.h>
30f36db9c4Syasuoka #include <limits.h>
31f36db9c4Syasuoka #include <netinet/in.h>
32f36db9c4Syasuoka #include <radius.h>
33f36db9c4Syasuoka #include <stdint.h>
34f36db9c4Syasuoka #include <stdio.h>
35f36db9c4Syasuoka #include <stdlib.h>
36f36db9c4Syasuoka #include <string.h>
37f36db9c4Syasuoka #include <strings.h>
38f36db9c4Syasuoka #include <time.h>
39f36db9c4Syasuoka 
40f36db9c4Syasuoka #include "iked.h"
41f36db9c4Syasuoka #include "eap.h"
42f36db9c4Syasuoka #include "ikev2.h"
43f36db9c4Syasuoka #include "types.h"
44f36db9c4Syasuoka 
45f36db9c4Syasuoka void	 iked_radius_request_send(struct iked *, void *);
46f36db9c4Syasuoka void	 iked_radius_fill_attributes(struct iked_sa *, RADIUS_PACKET *);
47f36db9c4Syasuoka void	 iked_radius_config(struct iked_radserver_req *, const RADIUS_PACKET *,
48f36db9c4Syasuoka 	    int, uint32_t, uint8_t);
49f36db9c4Syasuoka void	 iked_radius_acct_request(struct iked *, struct iked_sa *, uint8_t);
50f36db9c4Syasuoka 
51f36db9c4Syasuoka const struct iked_radcfgmap radius_cfgmaps[] = {
52f36db9c4Syasuoka     { IKEV2_CFG_INTERNAL_IP4_ADDRESS, 0, RADIUS_TYPE_FRAMED_IP_ADDRESS },
53f36db9c4Syasuoka     { IKEV2_CFG_INTERNAL_IP4_NETMASK, 0, RADIUS_TYPE_FRAMED_IP_NETMASK },
54f36db9c4Syasuoka     { IKEV2_CFG_INTERNAL_IP4_DNS, RADIUS_VENDOR_MICROSOFT,
55f36db9c4Syasuoka 	RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER },
56f36db9c4Syasuoka     { IKEV2_CFG_INTERNAL_IP4_DNS, RADIUS_VENDOR_MICROSOFT,
57f36db9c4Syasuoka 	RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER },
58f36db9c4Syasuoka     { IKEV2_CFG_INTERNAL_IP4_NBNS, RADIUS_VENDOR_MICROSOFT,
59f36db9c4Syasuoka 	RADIUS_VTYPE_MS_PRIMARY_NBNS_SERVER },
60f36db9c4Syasuoka     { IKEV2_CFG_INTERNAL_IP4_NBNS, RADIUS_VENDOR_MICROSOFT,
61f36db9c4Syasuoka 	RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER },
62f36db9c4Syasuoka     { 0 }
63f36db9c4Syasuoka };
64f36db9c4Syasuoka 
65f36db9c4Syasuoka int
66f36db9c4Syasuoka iked_radius_request(struct iked *env, struct iked_sa *sa,
67f36db9c4Syasuoka     struct iked_message *msg)
68f36db9c4Syasuoka {
69f36db9c4Syasuoka 	struct eap_message		*eap;
70f36db9c4Syasuoka 	RADIUS_PACKET			*pkt;
71f36db9c4Syasuoka 	size_t				 len;
72f36db9c4Syasuoka 
73f36db9c4Syasuoka 	eap = ibuf_data(msg->msg_eapmsg);
74f36db9c4Syasuoka 	len = betoh16(eap->eap_length);
75f36db9c4Syasuoka 	if (eap->eap_code != EAP_CODE_RESPONSE) {
76f36db9c4Syasuoka 		log_debug("%s: eap_code is not response %u", __func__,
77f36db9c4Syasuoka 		    (unsigned)eap->eap_code);
78f36db9c4Syasuoka 		return -1;
79f36db9c4Syasuoka 	}
80f36db9c4Syasuoka 
81f36db9c4Syasuoka 	if (eap->eap_type == EAP_TYPE_IDENTITY) {
82f36db9c4Syasuoka 		if ((sa->sa_radreq = calloc(1,
83f36db9c4Syasuoka 		    sizeof(struct iked_radserver_req))) == NULL) {
84f36db9c4Syasuoka 			log_debug(
85f36db9c4Syasuoka 			    "%s: calloc failed for iked_radserver_req: %s",
86f36db9c4Syasuoka 			    __func__, strerror(errno));
87f36db9c4Syasuoka 			return (-1);
88f36db9c4Syasuoka 		}
89f36db9c4Syasuoka 		timer_set(env, &sa->sa_radreq->rr_timer,
90f36db9c4Syasuoka 		    iked_radius_request_send, sa->sa_radreq);
91f36db9c4Syasuoka 		sa->sa_radreq->rr_user = strdup(msg->msg_eap.eam_identity);
92f36db9c4Syasuoka 	}
93f36db9c4Syasuoka 
94f36db9c4Syasuoka 	if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST))
95f36db9c4Syasuoka 	    == NULL) {
96f36db9c4Syasuoka 		log_debug("%s: radius_new_request_packet failed %s", __func__,
97f36db9c4Syasuoka 		    strerror(errno));
98f36db9c4Syasuoka 		return -1;
99f36db9c4Syasuoka 	}
100f36db9c4Syasuoka 
101f36db9c4Syasuoka 	radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME,
102f36db9c4Syasuoka 	    sa->sa_radreq->rr_user);
103f36db9c4Syasuoka 	if (sa->sa_radreq->rr_state != NULL)
104f36db9c4Syasuoka 		radius_put_raw_attr(pkt, RADIUS_TYPE_STATE,
105f36db9c4Syasuoka 		    ibuf_data(sa->sa_radreq->rr_state),
106f36db9c4Syasuoka 		    ibuf_size(sa->sa_radreq->rr_state));
107f36db9c4Syasuoka 
108f36db9c4Syasuoka 	if (radius_put_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE,
109f36db9c4Syasuoka 	    (uint8_t *)eap, len) == -1) {
110f36db9c4Syasuoka 		log_debug("%s: radius_put_raw_attr_cat failed %s", __func__,
111f36db9c4Syasuoka 		    strerror(errno));
112f36db9c4Syasuoka 		return -1;
113f36db9c4Syasuoka 	}
114f36db9c4Syasuoka 
115f36db9c4Syasuoka 	iked_radius_fill_attributes(sa, pkt);
116f36db9c4Syasuoka 
117f36db9c4Syasuoka 	/* save the request, it'll be needed for message authentication */
118f36db9c4Syasuoka 	if (sa->sa_radreq->rr_reqpkt != NULL)
119f36db9c4Syasuoka 		radius_delete_packet(sa->sa_radreq->rr_reqpkt);
120f36db9c4Syasuoka 	sa->sa_radreq->rr_reqpkt = pkt;
121f36db9c4Syasuoka 	sa->sa_radreq->rr_sa = sa;
122f36db9c4Syasuoka 	sa->sa_radreq->rr_ntry = 0;
123f36db9c4Syasuoka 
124f36db9c4Syasuoka 	iked_radius_request_send(env, sa->sa_radreq);
125f36db9c4Syasuoka 
126f36db9c4Syasuoka 	return 0;
127f36db9c4Syasuoka }
128f36db9c4Syasuoka 
129f36db9c4Syasuoka void
130f36db9c4Syasuoka iked_radius_request_free(struct iked *env, struct iked_radserver_req *req)
131f36db9c4Syasuoka {
132f36db9c4Syasuoka 	if (req == NULL)
133f36db9c4Syasuoka 		return;
134f36db9c4Syasuoka 	timer_del(env, &req->rr_timer);
135f36db9c4Syasuoka 	free(req->rr_user);
136f36db9c4Syasuoka 	ibuf_free(req->rr_state);
137f36db9c4Syasuoka 	if (req->rr_reqpkt)
138f36db9c4Syasuoka 		radius_delete_packet(req->rr_reqpkt);
139f36db9c4Syasuoka 	if (req->rr_sa)
140f36db9c4Syasuoka 		req->rr_sa->sa_radreq = NULL;
141f36db9c4Syasuoka 	if (req->rr_server)
142f36db9c4Syasuoka 		TAILQ_REMOVE(&req->rr_server->rs_reqs, req, rr_entry);
143f36db9c4Syasuoka 	free(req);
144f36db9c4Syasuoka }
145f36db9c4Syasuoka 
146f36db9c4Syasuoka void
147f36db9c4Syasuoka iked_radius_on_event(int fd, short ev, void *ctx)
148f36db9c4Syasuoka {
149f36db9c4Syasuoka 	struct iked			*env;
150f36db9c4Syasuoka 	struct iked_radserver		*server = ctx;
151f36db9c4Syasuoka 	struct iked_radserver_req	*req;
152f36db9c4Syasuoka 	const struct iked_radcfgmap	*cfgmap;
153f36db9c4Syasuoka 	RADIUS_PACKET			*pkt;
154f36db9c4Syasuoka 	int				 i, resid;
155f36db9c4Syasuoka 	struct ibuf			*e;
156f36db9c4Syasuoka 	const void			*attrval;
157f36db9c4Syasuoka 	size_t				 attrlen;
158f36db9c4Syasuoka 	uint8_t				 code;
159f36db9c4Syasuoka 	char				 username[256];
160f36db9c4Syasuoka 	u_char				 eapmsk[128];
161f36db9c4Syasuoka 	/* RFC 3748 defines the MSK minimum size is 64 bytes */
162f36db9c4Syasuoka 	size_t				 eapmsksiz = sizeof(eapmsk);
163f36db9c4Syasuoka 
164f36db9c4Syasuoka 	env = server->rs_env;
165f36db9c4Syasuoka 	pkt = radius_recv(server->rs_sock, 0);
166f36db9c4Syasuoka 	if (pkt == NULL) {
167f36db9c4Syasuoka 		log_info("%s: receiving a RADIUS message failed: %s", __func__,
168f36db9c4Syasuoka 		    strerror(errno));
169f36db9c4Syasuoka 		return;
170f36db9c4Syasuoka 	}
171f36db9c4Syasuoka 	resid = radius_get_id(pkt);
172f36db9c4Syasuoka 
173f36db9c4Syasuoka 	TAILQ_FOREACH(req, &server->rs_reqs, rr_entry) {
174f36db9c4Syasuoka 		if (req->rr_reqid == resid)
175f36db9c4Syasuoka 			break;
176f36db9c4Syasuoka 	}
177f36db9c4Syasuoka 	if (req == NULL) {
178f36db9c4Syasuoka 		log_debug("%s: received an unknown RADIUS message: id=%u",
179f36db9c4Syasuoka 		    __func__, (unsigned)resid);
180ee519c7fSyasuoka 		radius_delete_packet(pkt);
181f36db9c4Syasuoka 		return;
182f36db9c4Syasuoka 	}
183f36db9c4Syasuoka 
184f36db9c4Syasuoka 	radius_set_request_packet(pkt, req->rr_reqpkt);
185f36db9c4Syasuoka 	if (radius_check_response_authenticator(pkt, server->rs_secret) != 0) {
186f36db9c4Syasuoka 		log_info("%s: received an invalid RADIUS message: bad "
187f36db9c4Syasuoka 		    "response authenticator", __func__);
188ee519c7fSyasuoka 		radius_delete_packet(pkt);
189f36db9c4Syasuoka 		return;
190f36db9c4Syasuoka 	}
191f36db9c4Syasuoka 	if (req->rr_accounting) {
192f36db9c4Syasuoka 		/* accounting */
193f36db9c4Syasuoka 		code = radius_get_code(pkt);
194f36db9c4Syasuoka 		switch (code) {
195f36db9c4Syasuoka 		case RADIUS_CODE_ACCOUNTING_RESPONSE: /* Expected */
196f36db9c4Syasuoka 			break;
197f36db9c4Syasuoka 		default:
198f36db9c4Syasuoka 			log_info("%s: received an invalid RADIUS message: "
199f36db9c4Syasuoka 			    "code %u", __func__, (unsigned)code);
200f36db9c4Syasuoka 		}
201ee519c7fSyasuoka 		radius_delete_packet(pkt);
2028172eb65Syasuoka 		iked_radius_request_free(env, req);
203f36db9c4Syasuoka 		return;
204f36db9c4Syasuoka 	}
205f36db9c4Syasuoka 
206f36db9c4Syasuoka 	/* authentication */
207f36db9c4Syasuoka 	if (radius_check_message_authenticator(pkt, server->rs_secret) != 0) {
208f36db9c4Syasuoka 		log_info("%s: received an invalid RADIUS message: bad "
209f36db9c4Syasuoka 		    "message authenticator", __func__);
210ee519c7fSyasuoka 		radius_delete_packet(pkt);
211f36db9c4Syasuoka 		return;
212f36db9c4Syasuoka 	}
213f36db9c4Syasuoka 
214f36db9c4Syasuoka 	timer_del(env, &req->rr_timer);
215f36db9c4Syasuoka 	req->rr_ntry = 0;
216f36db9c4Syasuoka 
217f36db9c4Syasuoka 	if (req->rr_sa == NULL)
218f36db9c4Syasuoka 		goto fail;
219f36db9c4Syasuoka 
220f36db9c4Syasuoka 	code = radius_get_code(pkt);
221f36db9c4Syasuoka 	switch (code) {
222f36db9c4Syasuoka 	case RADIUS_CODE_ACCESS_CHALLENGE:
223f36db9c4Syasuoka 		if (radius_get_raw_attr_ptr(pkt, RADIUS_TYPE_STATE, &attrval,
224f36db9c4Syasuoka 		    &attrlen) != 0) {
225f36db9c4Syasuoka 			log_info("%s: received an invalid RADIUS message: no "
226f36db9c4Syasuoka 			    "state attribute", __func__);
227f36db9c4Syasuoka 			goto fail;
228f36db9c4Syasuoka 		}
2298172eb65Syasuoka 		if (req->rr_state != NULL &&
2308172eb65Syasuoka 		    ibuf_set(req->rr_state, 0, attrval, attrlen) != 0) {
2318172eb65Syasuoka 			ibuf_free(req->rr_state);
2328172eb65Syasuoka 			req->rr_state = NULL;
2338172eb65Syasuoka 		}
2348172eb65Syasuoka 		if (req->rr_state == NULL &&
235f36db9c4Syasuoka 		    (req->rr_state = ibuf_new(attrval, attrlen)) == NULL) {
236f36db9c4Syasuoka 			log_info("%s: ibuf_new() failed: %s", __func__,
237f36db9c4Syasuoka 			    strerror(errno));
238f36db9c4Syasuoka 			goto fail;
239f36db9c4Syasuoka 		}
240f36db9c4Syasuoka 		break;
241f36db9c4Syasuoka 	case RADIUS_CODE_ACCESS_ACCEPT:
242f36db9c4Syasuoka 		log_info("%s: received Access-Accept for %s",
243f36db9c4Syasuoka 		    SPI_SA(req->rr_sa, __func__), req->rr_user);
244f36db9c4Syasuoka 		/* Try to retrieve the EAP MSK from the RADIUS response */
245f36db9c4Syasuoka 		if (radius_get_eap_msk(pkt, eapmsk, &eapmsksiz,
246f36db9c4Syasuoka 		    server->rs_secret) == 0) {
247f36db9c4Syasuoka 			ibuf_free(req->rr_sa->sa_eapmsk);
248f36db9c4Syasuoka 			if ((req->rr_sa->sa_eapmsk = ibuf_new(eapmsk,
249f36db9c4Syasuoka 			    eapmsksiz)) == NULL) {
250f36db9c4Syasuoka 				log_info("%s: ibuf_new() failed: %s", __func__,
251f36db9c4Syasuoka 				    strerror(errno));
252f36db9c4Syasuoka 				goto fail;
253f36db9c4Syasuoka 			}
254f36db9c4Syasuoka 		} else
255f36db9c4Syasuoka 			log_debug("Could not retrieve the EAP MSK from the "
256f36db9c4Syasuoka 			    "RADIUS message");
257f36db9c4Syasuoka 
258f36db9c4Syasuoka 		free(req->rr_sa->sa_eapid);
259f36db9c4Syasuoka 		/* The EAP identity might be protected (RFC 3748 7.3) */
260f36db9c4Syasuoka 		if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME,
261f36db9c4Syasuoka 		    username, sizeof(username)) == 0 &&
262f36db9c4Syasuoka 		    strcmp(username, req->rr_user) != 0) {
263f36db9c4Syasuoka 			/*
264f36db9c4Syasuoka 			 * The Access-Accept might have a User-Name.  It
2654c856965Syasuoka 			 * should be used for Accounting (RFC 2865 5.1).
266f36db9c4Syasuoka 			 */
267f36db9c4Syasuoka 			free(req->rr_user);
268f36db9c4Syasuoka 			req->rr_sa->sa_eapid = strdup(username);
269f36db9c4Syasuoka 		} else
270f36db9c4Syasuoka 			req->rr_sa->sa_eapid = req->rr_user;
271f36db9c4Syasuoka 		req->rr_user = NULL;
272f36db9c4Syasuoka 
273*9ca241fcSyasuoka 		if (radius_get_raw_attr_ptr(pkt, RADIUS_TYPE_CLASS, &attrval,
274*9ca241fcSyasuoka 		    &attrlen) == 0) {
275*9ca241fcSyasuoka 			ibuf_free(req->rr_sa->sa_eapclass);
276*9ca241fcSyasuoka 			if ((req->rr_sa->sa_eapclass = ibuf_new(attrval,
277*9ca241fcSyasuoka 			    attrlen)) == NULL) {
278*9ca241fcSyasuoka 				log_info("%s: ibuf_new() failed: %s", __func__,
279*9ca241fcSyasuoka 				    strerror(errno));
280*9ca241fcSyasuoka 			}
281*9ca241fcSyasuoka 		}
282*9ca241fcSyasuoka 
283f36db9c4Syasuoka 		sa_state(env, req->rr_sa, IKEV2_STATE_AUTH_SUCCESS);
284f36db9c4Syasuoka 
285f36db9c4Syasuoka 		/* Map RADIUS attributes to cp */
286f36db9c4Syasuoka 		if (TAILQ_EMPTY(&env->sc_radcfgmaps)) {
287f36db9c4Syasuoka 			for (i = 0; radius_cfgmaps[i].cfg_type != 0; i++) {
288f36db9c4Syasuoka 				cfgmap = &radius_cfgmaps[i];
289f36db9c4Syasuoka 				iked_radius_config(req, pkt, cfgmap->cfg_type,
290f36db9c4Syasuoka 				    cfgmap->vendor_id, cfgmap->attr_type);
291f36db9c4Syasuoka 			}
292f36db9c4Syasuoka 		} else {
293f36db9c4Syasuoka 			TAILQ_FOREACH(cfgmap, &env->sc_radcfgmaps, entry)
294f36db9c4Syasuoka 				iked_radius_config(req, pkt, cfgmap->cfg_type,
295f36db9c4Syasuoka 				    cfgmap->vendor_id, cfgmap->attr_type);
296f36db9c4Syasuoka 		}
297f36db9c4Syasuoka 
298f36db9c4Syasuoka 		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
299f36db9c4Syasuoka 		req->rr_server = NULL;
300f36db9c4Syasuoka 		break;
301f36db9c4Syasuoka 	case RADIUS_CODE_ACCESS_REJECT:
302f36db9c4Syasuoka 		log_info("%s: received Access-Reject for %s",
303f36db9c4Syasuoka 		    SPI_SA(req->rr_sa, __func__), req->rr_user);
304f36db9c4Syasuoka 		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
305f36db9c4Syasuoka 		req->rr_server = NULL;
306f36db9c4Syasuoka 		break;
307f36db9c4Syasuoka 	default:
308f36db9c4Syasuoka 		log_debug("%s: received an invalid RADIUS message: code %u",
309f36db9c4Syasuoka 		    __func__, (unsigned)code);
310f36db9c4Syasuoka 		break;
311f36db9c4Syasuoka 	}
312f36db9c4Syasuoka 
313f36db9c4Syasuoka 	/* get the length first */
314f36db9c4Syasuoka 	if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, NULL,
315f36db9c4Syasuoka 	    &attrlen) != 0) {
316f36db9c4Syasuoka 		log_info("%s: failed to retrieve the EAP message", __func__);
317f36db9c4Syasuoka 		goto fail;
318f36db9c4Syasuoka 	}
319f36db9c4Syasuoka 	/* allocate a buffer */
320f36db9c4Syasuoka 	if ((e = ibuf_new(NULL, attrlen)) == NULL) {
321f36db9c4Syasuoka 		log_info("%s: ibuf_new() failed: %s", __func__,
322f36db9c4Syasuoka 		    strerror(errno));
323f36db9c4Syasuoka 		goto fail;
324f36db9c4Syasuoka 	}
325f36db9c4Syasuoka 	/* copy the message to the buffer */
326f36db9c4Syasuoka 	if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE,
327f36db9c4Syasuoka 	    ibuf_data(e), &attrlen) != 0) {
328f36db9c4Syasuoka 		ibuf_free(e);
329f36db9c4Syasuoka 		log_info("%s: failed to retrieve the EAP message", __func__);
330f36db9c4Syasuoka 		goto fail;
331f36db9c4Syasuoka 	}
332ee519c7fSyasuoka 	radius_delete_packet(pkt);
333f36db9c4Syasuoka 	ikev2_send_ike_e(env, req->rr_sa, e, IKEV2_PAYLOAD_EAP,
334f36db9c4Syasuoka 	    IKEV2_EXCHANGE_IKE_AUTH, 1);
3358172eb65Syasuoka 	ibuf_free(e);
336ee519c7fSyasuoka 	/* keep request for challenge state and config parameters */
337ee519c7fSyasuoka 	req->rr_reqid = -1;	/* release reqid */
338f36db9c4Syasuoka 	return;
339f36db9c4Syasuoka  fail:
340ee519c7fSyasuoka 	radius_delete_packet(pkt);
341f36db9c4Syasuoka 	if (req->rr_server != NULL)
342f36db9c4Syasuoka 		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
343f36db9c4Syasuoka 	req->rr_server = NULL;
344f36db9c4Syasuoka 	if (req->rr_sa != NULL) {
345f36db9c4Syasuoka 		ikev2_ike_sa_setreason(req->rr_sa, "RADIUS request failed");
346f36db9c4Syasuoka 		sa_free(env, req->rr_sa);
347f36db9c4Syasuoka 	}
348f36db9c4Syasuoka }
349f36db9c4Syasuoka 
350f36db9c4Syasuoka void
351f36db9c4Syasuoka iked_radius_request_send(struct iked *env, void *ctx)
352f36db9c4Syasuoka {
353f36db9c4Syasuoka 	struct iked_radserver_req	*req = ctx, *req0;
354f36db9c4Syasuoka 	struct iked_radserver		*server = req->rr_server;
355f36db9c4Syasuoka 	const int			 timeouts[] = { 2, 4, 8 };
356f36db9c4Syasuoka 	uint8_t				 seq;
357f36db9c4Syasuoka 	int				 i, max_tries, max_failovers;
358f36db9c4Syasuoka 	struct sockaddr_storage		 ss;
359f36db9c4Syasuoka 	socklen_t			 sslen;
360f36db9c4Syasuoka 	struct iked_radservers		*radservers;
361f36db9c4Syasuoka 	struct timespec			 now;
362f36db9c4Syasuoka 
363f36db9c4Syasuoka 	if (!req->rr_accounting) {
364f36db9c4Syasuoka 		max_tries = env->sc_radauth.max_tries;
365f36db9c4Syasuoka 		max_failovers = env->sc_radauth.max_failovers;
366f36db9c4Syasuoka 		radservers = &env->sc_radauthservers;
367f36db9c4Syasuoka 	} else {
368f36db9c4Syasuoka 		max_tries = env->sc_radacct.max_tries;
369f36db9c4Syasuoka 		max_failovers = env->sc_radacct.max_failovers;
370f36db9c4Syasuoka 		radservers = &env->sc_radacctservers;
371f36db9c4Syasuoka 	}
372f36db9c4Syasuoka 
373f36db9c4Syasuoka 	if (req->rr_ntry > max_tries) {
374f36db9c4Syasuoka 		req->rr_ntry = 0;
375f36db9c4Syasuoka 		log_info("%s: RADIUS server %s failed", __func__,
376f36db9c4Syasuoka 		    print_addr(&server->rs_sockaddr));
377f36db9c4Syasuoka  next_server:
378f36db9c4Syasuoka 		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
379f36db9c4Syasuoka 		req->rr_server = NULL;
380f36db9c4Syasuoka 		if (req->rr_nfailover >= max_failovers ||
381f36db9c4Syasuoka 		    TAILQ_NEXT(server, rs_entry) == NULL) {
382f36db9c4Syasuoka 			log_info("%s: No more RADIUS server", __func__);
383f36db9c4Syasuoka 			goto fail;
384f36db9c4Syasuoka 		} else if (req->rr_state != NULL) {
385f36db9c4Syasuoka 			log_info("%s: Can't change RADIUS server: "
386f36db9c4Syasuoka 			    "client has a state already", __func__);
387f36db9c4Syasuoka 			goto fail;
388f36db9c4Syasuoka 		} else {
389f36db9c4Syasuoka 			TAILQ_REMOVE(radservers, server, rs_entry);
390f36db9c4Syasuoka 			TAILQ_INSERT_TAIL(radservers, server, rs_entry);
391f36db9c4Syasuoka 			server = TAILQ_FIRST(radservers);
392f36db9c4Syasuoka 			log_info("%s: RADIUS server %s is active",
393f36db9c4Syasuoka 			    __func__, print_addr(&server->rs_sockaddr));
394f36db9c4Syasuoka 		}
395f36db9c4Syasuoka 		req->rr_nfailover++;
396f36db9c4Syasuoka 	}
397f36db9c4Syasuoka 
398f36db9c4Syasuoka 	if (req->rr_server != NULL &&
399f36db9c4Syasuoka 	    req->rr_server != TAILQ_FIRST(radservers)) {
400f36db9c4Syasuoka 		/* Current server is marked fail */
401f36db9c4Syasuoka 		if (req->rr_state != NULL || req->rr_nfailover >= max_failovers)
402f36db9c4Syasuoka 			goto fail; /* can't fail over */
403f36db9c4Syasuoka 		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
404f36db9c4Syasuoka 		req->rr_server = NULL;
405f36db9c4Syasuoka 		req->rr_nfailover++;
406f36db9c4Syasuoka 	}
407f36db9c4Syasuoka 
408f36db9c4Syasuoka 	if (req->rr_server == NULL) {
409f36db9c4Syasuoka 		/* Select a new server */
410f36db9c4Syasuoka 		server = TAILQ_FIRST(radservers);
411f36db9c4Syasuoka 		if (server == NULL) {
412f36db9c4Syasuoka 			log_info("%s: No RADIUS server is configured",
413f36db9c4Syasuoka 			    __func__);
414f36db9c4Syasuoka 			goto fail;
415f36db9c4Syasuoka 		}
416f36db9c4Syasuoka 		TAILQ_INSERT_TAIL(&server->rs_reqs, req, rr_entry);
417f36db9c4Syasuoka 		req->rr_server = server;
418f36db9c4Syasuoka 
419f36db9c4Syasuoka 		/* Prepare NAS-IP-Address */
420f36db9c4Syasuoka 		if (server->rs_nas_ipv4.s_addr == INADDR_ANY &&
421f36db9c4Syasuoka 		    IN6_IS_ADDR_UNSPECIFIED(&server->rs_nas_ipv6)) {
422f36db9c4Syasuoka 			sslen = sizeof(ss);
423f36db9c4Syasuoka 			if (getsockname(server->rs_sock, (struct sockaddr *)&ss,
424f36db9c4Syasuoka 			    &sslen) == 0) {
425f36db9c4Syasuoka 				if (ss.ss_family == AF_INET)
426f36db9c4Syasuoka 					server->rs_nas_ipv4 =
427f36db9c4Syasuoka 					    ((struct sockaddr_in *)&ss)
428f36db9c4Syasuoka 					    ->sin_addr;
429f36db9c4Syasuoka 				else
430f36db9c4Syasuoka 					server->rs_nas_ipv6 =
431f36db9c4Syasuoka 					    ((struct sockaddr_in6 *)&ss)
432f36db9c4Syasuoka 					    ->sin6_addr;
433f36db9c4Syasuoka 			}
434f36db9c4Syasuoka 		}
435f36db9c4Syasuoka 	}
436f36db9c4Syasuoka 	if (req->rr_ntry == 0) {
437f36db9c4Syasuoka 		/* decide the ID */
438f36db9c4Syasuoka 		seq = ++server->rs_reqseq;
439ee519c7fSyasuoka 		for (i = 0; i <= UCHAR_MAX; i++) {
440f36db9c4Syasuoka 			TAILQ_FOREACH(req0, &server->rs_reqs, rr_entry) {
441ee519c7fSyasuoka 				if (req0->rr_reqid == -1)
442ee519c7fSyasuoka 					continue;
443f36db9c4Syasuoka 				if (req0->rr_reqid == seq)
444f36db9c4Syasuoka 					break;
445f36db9c4Syasuoka 			}
446f36db9c4Syasuoka 			if (req0 == NULL)
447f36db9c4Syasuoka 				break;
448f36db9c4Syasuoka 			seq++;
449f36db9c4Syasuoka 		}
450ee519c7fSyasuoka 		if (i > UCHAR_MAX) {
451f36db9c4Syasuoka 			log_info("%s: RADIUS server %s failed.  Too many "
452f36db9c4Syasuoka 			    "pending requests", __func__,
453f36db9c4Syasuoka 			    print_addr(&server->rs_sockaddr));
454f36db9c4Syasuoka 			if (TAILQ_NEXT(server, rs_entry) != NULL)
455f36db9c4Syasuoka 				goto next_server;
456f36db9c4Syasuoka 			goto fail;
457f36db9c4Syasuoka 		}
458f36db9c4Syasuoka 		req->rr_reqid = seq;
459f36db9c4Syasuoka 		radius_set_id(req->rr_reqpkt, req->rr_reqid);
460f36db9c4Syasuoka 	}
461f36db9c4Syasuoka 
462f36db9c4Syasuoka 	if (server->rs_nas_ipv4.s_addr != INADDR_ANY)
463f36db9c4Syasuoka 		radius_put_ipv4_attr(req->rr_reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS,
464f36db9c4Syasuoka 		    server->rs_nas_ipv4);
465f36db9c4Syasuoka 	else if (!IN6_IS_ADDR_UNSPECIFIED(&server->rs_nas_ipv6))
466f36db9c4Syasuoka 		radius_put_ipv6_attr(req->rr_reqpkt,
467f36db9c4Syasuoka 		    RADIUS_TYPE_NAS_IPV6_ADDRESS, &server->rs_nas_ipv6);
468f36db9c4Syasuoka 	/* Identifier */
469f36db9c4Syasuoka 	radius_put_string_attr(req->rr_reqpkt, RADIUS_TYPE_NAS_IDENTIFIER,
4704fa86b90Syasuoka 	    IKED_NAS_ID);
471f36db9c4Syasuoka 
472f36db9c4Syasuoka 	if (req->rr_accounting) {
473f36db9c4Syasuoka 		if (req->rr_ntry == 0 && req->rr_nfailover == 0)
474f36db9c4Syasuoka 			radius_put_uint32_attr(req->rr_reqpkt,
475f36db9c4Syasuoka 			    RADIUS_TYPE_ACCT_DELAY_TIME, 0);
476f36db9c4Syasuoka 		else {
477f36db9c4Syasuoka 			clock_gettime(CLOCK_MONOTONIC, &now);
478f36db9c4Syasuoka 			timespecsub(&now, &req->rr_accttime, &now);
479f36db9c4Syasuoka 			radius_put_uint32_attr(req->rr_reqpkt,
480f36db9c4Syasuoka 			    RADIUS_TYPE_ACCT_DELAY_TIME, now.tv_sec);
481f36db9c4Syasuoka 		}
482f36db9c4Syasuoka 		radius_set_accounting_request_authenticator(req->rr_reqpkt,
483f36db9c4Syasuoka 		    server->rs_secret);
484f36db9c4Syasuoka 	} else {
485f36db9c4Syasuoka 		radius_put_message_authenticator(req->rr_reqpkt,
486f36db9c4Syasuoka 		    server->rs_secret);
487f36db9c4Syasuoka 	}
488f36db9c4Syasuoka 
489f36db9c4Syasuoka 	if (radius_send(server->rs_sock, req->rr_reqpkt, 0) < 0)
490f36db9c4Syasuoka 		log_info("%s: sending a RADIUS message failed: %s", __func__,
491f36db9c4Syasuoka 		    strerror(errno));
492f36db9c4Syasuoka 
493f36db9c4Syasuoka 	if (req->rr_ntry >= (int)nitems(timeouts))
494f36db9c4Syasuoka 		timer_add(env, &req->rr_timer, timeouts[nitems(timeouts) - 1]);
495f36db9c4Syasuoka 	else
496f36db9c4Syasuoka 		timer_add(env, &req->rr_timer, timeouts[req->rr_ntry]);
497f36db9c4Syasuoka 	req->rr_ntry++;
498f36db9c4Syasuoka 	return;
499f36db9c4Syasuoka  fail:
500f36db9c4Syasuoka 	if (req->rr_server != NULL)
501f36db9c4Syasuoka 		TAILQ_REMOVE(&server->rs_reqs, req, rr_entry);
502f36db9c4Syasuoka 	req->rr_server = NULL;
503f36db9c4Syasuoka 	if (req->rr_sa != NULL) {
504f36db9c4Syasuoka 		ikev2_ike_sa_setreason(req->rr_sa, "RADIUS request failed");
505f36db9c4Syasuoka 		sa_free(env, req->rr_sa);
506f36db9c4Syasuoka 	}
507f36db9c4Syasuoka }
508f36db9c4Syasuoka 
509f36db9c4Syasuoka void
510f36db9c4Syasuoka iked_radius_fill_attributes(struct iked_sa *sa, RADIUS_PACKET *pkt)
511f36db9c4Syasuoka {
512f36db9c4Syasuoka 	/* NAS Port Type = Virtual */
513f36db9c4Syasuoka 	radius_put_uint32_attr(pkt,
514f36db9c4Syasuoka 	    RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL);
515f36db9c4Syasuoka 	/* Service Type =  Framed */
516f36db9c4Syasuoka 	radius_put_uint32_attr(pkt, RADIUS_TYPE_SERVICE_TYPE,
517f36db9c4Syasuoka 	    RADIUS_SERVICE_TYPE_FRAMED);
518f36db9c4Syasuoka 	/* Tunnel Type = EAP */
519f36db9c4Syasuoka 	radius_put_uint32_attr(pkt, RADIUS_TYPE_TUNNEL_TYPE,
520f36db9c4Syasuoka 	    RADIUS_TUNNEL_TYPE_ESP);
521f36db9c4Syasuoka 
522f36db9c4Syasuoka 	radius_put_string_attr(pkt, RADIUS_TYPE_CALLED_STATION_ID,
523f36db9c4Syasuoka 	    print_addr(&sa->sa_local.addr));
524f36db9c4Syasuoka 	radius_put_string_attr(pkt, RADIUS_TYPE_CALLING_STATION_ID,
525f36db9c4Syasuoka 	    print_addr(&sa->sa_peer.addr));
526f36db9c4Syasuoka }
527f36db9c4Syasuoka 
528f36db9c4Syasuoka void
529f36db9c4Syasuoka iked_radius_config(struct iked_radserver_req *req, const RADIUS_PACKET *pkt,
530f36db9c4Syasuoka     int cfg_type, uint32_t vendor_id, uint8_t attr_type)
531f36db9c4Syasuoka {
532f36db9c4Syasuoka 	unsigned int		 i;
533f36db9c4Syasuoka 	struct iked_sa		*sa = req->rr_sa;
534f36db9c4Syasuoka 	struct in_addr		 ia4;
535f36db9c4Syasuoka 	struct in6_addr		 ia6;
536f36db9c4Syasuoka 	struct sockaddr_in	*sin4;
537f36db9c4Syasuoka 	struct sockaddr_in6	*sin6;
538f36db9c4Syasuoka 	struct iked_addr	*addr;
539f36db9c4Syasuoka 	struct iked_cfg		*ikecfg;
540f36db9c4Syasuoka 
541f36db9c4Syasuoka 	for (i = 0; i < sa->sa_policy->pol_ncfg; i++) {
542f36db9c4Syasuoka 		ikecfg = &sa->sa_policy->pol_cfg[i];
543f36db9c4Syasuoka 		if (ikecfg->cfg_type == cfg_type &&
544f36db9c4Syasuoka 		    ikecfg->cfg_type != IKEV2_CFG_INTERNAL_IP4_ADDRESS)
545f36db9c4Syasuoka 			return;	/* use config rather than radius */
546f36db9c4Syasuoka 	}
547f36db9c4Syasuoka 	switch (cfg_type) {
548f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP4_ADDRESS:
549f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP4_NETMASK:
550f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP4_DNS:
551f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP4_NBNS:
552f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP4_DHCP:
553f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP4_SERVER:
554f36db9c4Syasuoka 		if (vendor_id == 0 && radius_has_attr(pkt, attr_type))
555f36db9c4Syasuoka 			radius_get_ipv4_attr(pkt, attr_type, &ia4);
556f36db9c4Syasuoka 		else if (vendor_id != 0 && radius_has_vs_attr(pkt, vendor_id,
557f36db9c4Syasuoka 		    attr_type))
558f36db9c4Syasuoka 			radius_get_vs_ipv4_attr(pkt, vendor_id, attr_type,
559f36db9c4Syasuoka 			    &ia4);
560f36db9c4Syasuoka 		else
561f36db9c4Syasuoka 			break; /* no attribute contained */
562f36db9c4Syasuoka 
563f36db9c4Syasuoka 		if (cfg_type == IKEV2_CFG_INTERNAL_IP4_NETMASK) {
564f36db9c4Syasuoka 			/*
565f36db9c4Syasuoka 			 * This assumes IKEV2_CFG_INTERNAL_IP4_ADDRESS is
566f36db9c4Syasuoka 			 * called before IKEV2_CFG_INTERNAL_IP4_NETMASK
567f36db9c4Syasuoka 			 */
568f36db9c4Syasuoka 			if (sa->sa_rad_addr == NULL) {
569f36db9c4Syasuoka 				/*
570f36db9c4Syasuoka 				 * RFC 7296, IKEV2_CFG_INTERNAL_IP4_NETMASK
571f36db9c4Syasuoka 				 * must be used with
572f36db9c4Syasuoka 				 * IKEV2_CFG_INTERNAL_IP4_ADDRESS
573f36db9c4Syasuoka 				 */
574f36db9c4Syasuoka 				break;
575f36db9c4Syasuoka 			}
576f36db9c4Syasuoka 			if (ia4.s_addr == 0) {
577f36db9c4Syasuoka 				log_debug("%s: netmask is wrong", __func__);
578f36db9c4Syasuoka 				break;
579f36db9c4Syasuoka 			}
580f36db9c4Syasuoka 			if (ia4.s_addr == htonl(0))
581f36db9c4Syasuoka 				sa->sa_rad_addr->addr_mask = 0;
582f36db9c4Syasuoka 			else
583f36db9c4Syasuoka 				sa->sa_rad_addr->addr_mask =
584f36db9c4Syasuoka 				    33 - ffs(ntohl(ia4.s_addr));
585f36db9c4Syasuoka 			if (sa->sa_rad_addr->addr_mask < 32)
586f36db9c4Syasuoka 				sa->sa_rad_addr->addr_net = 1;
587f36db9c4Syasuoka 		}
588f36db9c4Syasuoka 		if (cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) {
589f36db9c4Syasuoka 			if ((addr = calloc(1, sizeof(*addr))) == NULL) {
590f36db9c4Syasuoka 				log_warn("%s: calloc", __func__);
591f36db9c4Syasuoka 				return;
592f36db9c4Syasuoka 			}
593f36db9c4Syasuoka 			sa->sa_rad_addr = addr;
594f36db9c4Syasuoka 		} else {
595f36db9c4Syasuoka 			req->rr_cfg[req->rr_ncfg].cfg_action = IKEV2_CP_REPLY;
596f36db9c4Syasuoka 			req->rr_cfg[req->rr_ncfg].cfg_type = cfg_type;
597f36db9c4Syasuoka 			addr = &req->rr_cfg[req->rr_ncfg].cfg.address;
598f36db9c4Syasuoka 			req->rr_ncfg++;
599f36db9c4Syasuoka 		}
600f36db9c4Syasuoka 		addr->addr_af = AF_INET;
601f36db9c4Syasuoka 		sin4 = (struct sockaddr_in *)&addr->addr;
602f36db9c4Syasuoka 		sin4->sin_family = AF_INET;
603f36db9c4Syasuoka 		sin4->sin_len = sizeof(struct sockaddr_in);
604f36db9c4Syasuoka 		sin4->sin_addr = ia4;
605f36db9c4Syasuoka 		break;
606f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP6_ADDRESS:
607f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP6_DNS:
608f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP6_NBNS:
609f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP6_DHCP:
610f36db9c4Syasuoka 	case IKEV2_CFG_INTERNAL_IP6_SERVER:
611f36db9c4Syasuoka 		if (vendor_id == 0 && radius_has_attr(pkt, attr_type))
612f36db9c4Syasuoka 			radius_get_ipv6_attr(pkt, attr_type, &ia6);
613f36db9c4Syasuoka 		else if (vendor_id != 0 && radius_has_vs_attr(pkt, vendor_id,
614f36db9c4Syasuoka 		    attr_type))
615f36db9c4Syasuoka 			radius_get_vs_ipv6_attr(pkt, vendor_id, attr_type,
616f36db9c4Syasuoka 			    &ia6);
617f36db9c4Syasuoka 		else
618f36db9c4Syasuoka 			break; /* no attribute contained */
619f36db9c4Syasuoka 
620f36db9c4Syasuoka 		if (cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS) {
621f36db9c4Syasuoka 			if ((addr = calloc(1, sizeof(*addr))) == NULL) {
622f36db9c4Syasuoka 				log_warn("%s: calloc", __func__);
623f36db9c4Syasuoka 				return;
624f36db9c4Syasuoka 			}
625f36db9c4Syasuoka 			sa->sa_rad_addr = addr;
626f36db9c4Syasuoka 		} else {
627f36db9c4Syasuoka 			req->rr_cfg[req->rr_ncfg].cfg_action = IKEV2_CP_REPLY;
628f36db9c4Syasuoka 			req->rr_cfg[req->rr_ncfg].cfg_type = cfg_type;
629f36db9c4Syasuoka 			addr = &req->rr_cfg[req->rr_ncfg].cfg.address;
630f36db9c4Syasuoka 			req->rr_ncfg++;
631f36db9c4Syasuoka 		}
632f36db9c4Syasuoka 		addr->addr_af = AF_INET;
633f36db9c4Syasuoka 		sin6 = (struct sockaddr_in6 *)&addr->addr;
634f36db9c4Syasuoka 		sin6->sin6_family = AF_INET6;
635f36db9c4Syasuoka 		sin6->sin6_len = sizeof(struct sockaddr_in6);
636f36db9c4Syasuoka 		sin6->sin6_addr = ia6;
637f36db9c4Syasuoka 		break;
638f36db9c4Syasuoka 	}
639f36db9c4Syasuoka 	return;
640f36db9c4Syasuoka }
641f36db9c4Syasuoka 
642f36db9c4Syasuoka void
643f36db9c4Syasuoka iked_radius_acct_on(struct iked *env)
644f36db9c4Syasuoka {
645f36db9c4Syasuoka 	if (TAILQ_EMPTY(&env->sc_radacctservers))
646f36db9c4Syasuoka 		return;
647f36db9c4Syasuoka 	if (env->sc_radaccton == 0) {	/* trigger once */
648f36db9c4Syasuoka 		iked_radius_acct_request(env, NULL,
649f36db9c4Syasuoka 		    RADIUS_ACCT_STATUS_TYPE_ACCT_ON);
650f36db9c4Syasuoka 		env->sc_radaccton = 1;
651f36db9c4Syasuoka 	}
652f36db9c4Syasuoka }
653f36db9c4Syasuoka 
654f36db9c4Syasuoka void
655f36db9c4Syasuoka iked_radius_acct_off(struct iked *env)
656f36db9c4Syasuoka {
657f36db9c4Syasuoka 	iked_radius_acct_request(env, NULL, RADIUS_ACCT_STATUS_TYPE_ACCT_OFF);
658f36db9c4Syasuoka }
659f36db9c4Syasuoka 
660f36db9c4Syasuoka void
661f36db9c4Syasuoka iked_radius_acct_start(struct iked *env, struct iked_sa *sa)
662f36db9c4Syasuoka {
663f36db9c4Syasuoka 	iked_radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_START);
664f36db9c4Syasuoka }
665f36db9c4Syasuoka 
666f36db9c4Syasuoka void
667f36db9c4Syasuoka iked_radius_acct_stop(struct iked *env, struct iked_sa *sa)
668f36db9c4Syasuoka {
669f36db9c4Syasuoka 	iked_radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_STOP);
670f36db9c4Syasuoka }
671f36db9c4Syasuoka 
672f36db9c4Syasuoka void
673f36db9c4Syasuoka iked_radius_acct_request(struct iked *env, struct iked_sa *sa, uint8_t stype)
674f36db9c4Syasuoka {
675f36db9c4Syasuoka 	struct iked_radserver_req	*req;
676f36db9c4Syasuoka 	RADIUS_PACKET			*pkt;
677f36db9c4Syasuoka 	struct iked_addr		*addr4 = NULL;
678f36db9c4Syasuoka 	struct iked_addr		*addr6 = NULL;
679f36db9c4Syasuoka 	struct in_addr			 mask4;
680f36db9c4Syasuoka 	char				 sa_id[IKED_ID_SIZE];
681f36db9c4Syasuoka 	char				 sid[16 + 1];
682f36db9c4Syasuoka 	struct timespec			 now;
683f36db9c4Syasuoka 	int				 cause;
684f36db9c4Syasuoka 
685f36db9c4Syasuoka 	if (TAILQ_EMPTY(&env->sc_radacctservers))
686f36db9c4Syasuoka 		return;
687f36db9c4Syasuoka 	/*
688f36db9c4Syasuoka 	 * In RFC2866 5.6, "Users who are delivered service without
689f36db9c4Syasuoka 	 * being authenticated SHOULD NOT generate Accounting records
690f36db9c4Syasuoka 	 */
691f36db9c4Syasuoka 	if (sa != NULL && sa->sa_eapid == NULL) {
692f36db9c4Syasuoka 		/* fallback to IKEID for accounting */
693f36db9c4Syasuoka 		if (ikev2_print_id(IKESA_DSTID(sa), sa_id, sizeof(sa_id)) != -1)
694f36db9c4Syasuoka 			sa->sa_eapid = strdup(sa_id);
695f36db9c4Syasuoka 		if (sa->sa_eapid == NULL)
696f36db9c4Syasuoka 			return;
697f36db9c4Syasuoka 	}
698f36db9c4Syasuoka 
699f36db9c4Syasuoka 	if ((req = calloc(1, sizeof(struct iked_radserver_req))) == NULL) {
700f36db9c4Syasuoka 		log_debug("%s: calloc faile for iked_radserver_req: %s",
701f36db9c4Syasuoka 		    __func__, strerror(errno));
702f36db9c4Syasuoka 		return;
703f36db9c4Syasuoka 	}
704f36db9c4Syasuoka 	req->rr_accounting = 1;
705f36db9c4Syasuoka 	clock_gettime(CLOCK_MONOTONIC, &now);
706f36db9c4Syasuoka 	req->rr_accttime = now;
707f36db9c4Syasuoka 	timer_set(env, &req->rr_timer, iked_radius_request_send, req);
708f36db9c4Syasuoka 
709f36db9c4Syasuoka 	if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCOUNTING_REQUEST))
710f36db9c4Syasuoka 	    == NULL) {
711f36db9c4Syasuoka 		log_debug("%s: radius_new_request_packet failed %s", __func__,
712f36db9c4Syasuoka 		    strerror(errno));
713f36db9c4Syasuoka 		return;
714f36db9c4Syasuoka 	}
715f36db9c4Syasuoka 
716f36db9c4Syasuoka 	/* RFC 2866  5.1. Acct-Status-Type */
717f36db9c4Syasuoka 	radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_STATUS_TYPE, stype);
718f36db9c4Syasuoka 
719f36db9c4Syasuoka 	if (sa == NULL) {
720f36db9c4Syasuoka 		/* ASSERT(stype == RADIUS_ACCT_STATUS_TYPE_ACCT_ON ||
721f36db9c4Syasuoka 		    stype == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) */
722f36db9c4Syasuoka 		req->rr_reqpkt = pkt;
723f36db9c4Syasuoka 		req->rr_ntry = 0;
724f36db9c4Syasuoka 		iked_radius_request_send(env, req);
725f36db9c4Syasuoka 		return;
726f36db9c4Syasuoka 	}
727f36db9c4Syasuoka 
728f36db9c4Syasuoka 	iked_radius_fill_attributes(sa, pkt);
729f36db9c4Syasuoka 
730f36db9c4Syasuoka 	radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, sa->sa_eapid);
731f36db9c4Syasuoka 
732f36db9c4Syasuoka 	/* RFC 2866  5.5. Acct-Session-Id */
733f36db9c4Syasuoka 	snprintf(sid, sizeof(sid), "%016llx",
734f36db9c4Syasuoka 	    (unsigned long long)sa->sa_hdr.sh_ispi);
735f36db9c4Syasuoka 	radius_put_string_attr(pkt, RADIUS_TYPE_ACCT_SESSION_ID, sid);
736f36db9c4Syasuoka 
737f36db9c4Syasuoka 	/* Accounting Request must have Framed-IP-Address */
738f36db9c4Syasuoka 	addr4 = sa->sa_addrpool;
739f36db9c4Syasuoka 	if (addr4 != NULL) {
740f36db9c4Syasuoka 		radius_put_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_ADDRESS,
741f36db9c4Syasuoka 		    ((struct sockaddr_in *)&addr4->addr)->sin_addr);
742f36db9c4Syasuoka 		if (addr4->addr_mask != 0) {
743f36db9c4Syasuoka 			mask4.s_addr = htonl(
744f36db9c4Syasuoka 			    0xFFFFFFFFUL << (32 - addr4->addr_mask));
745f36db9c4Syasuoka 			radius_put_ipv4_attr(pkt,
746f36db9c4Syasuoka 			    RADIUS_TYPE_FRAMED_IP_NETMASK, mask4);
747f36db9c4Syasuoka 		}
748f36db9c4Syasuoka 	}
749f36db9c4Syasuoka 	addr6 = sa->sa_addrpool6;
750f36db9c4Syasuoka 	if (addr6 != NULL)
751f36db9c4Syasuoka 		radius_put_ipv6_attr(pkt, RADIUS_TYPE_FRAMED_IPV6_ADDRESS,
752f36db9c4Syasuoka 		    &((struct sockaddr_in6 *)&addr6->addr)->sin6_addr);
753f36db9c4Syasuoka 
754f36db9c4Syasuoka 	/* RFC2866 5.6 Acct-Authentic */
755f36db9c4Syasuoka 	radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_AUTHENTIC,
756f36db9c4Syasuoka 	    (sa->sa_radreq != NULL)? RADIUS_ACCT_AUTHENTIC_RADIUS :
757f36db9c4Syasuoka 	    RADIUS_ACCT_AUTHENTIC_LOCAL);
758f36db9c4Syasuoka 
759f36db9c4Syasuoka 	switch (stype) {
760f36db9c4Syasuoka 	case RADIUS_ACCT_STATUS_TYPE_START:
761*9ca241fcSyasuoka 		if (req->rr_sa && req->rr_sa->sa_eapclass != NULL)
762*9ca241fcSyasuoka 			radius_put_raw_attr(pkt, RADIUS_TYPE_CLASS,
763*9ca241fcSyasuoka 			    ibuf_data(req->rr_sa->sa_eapclass),
764*9ca241fcSyasuoka 			    ibuf_size(req->rr_sa->sa_eapclass));
765f36db9c4Syasuoka 		break;
766f36db9c4Syasuoka 	case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
767f36db9c4Syasuoka 	case RADIUS_ACCT_STATUS_TYPE_STOP:
768f36db9c4Syasuoka 		/* RFC 2866 5.7.  Acct-Session-Time */
769f36db9c4Syasuoka 		timespecsub(&now, &sa->sa_starttime, &now);
770f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_SESSION_TIME,
771f36db9c4Syasuoka 		    now.tv_sec);
772f36db9c4Syasuoka 		/* RFC 2866 5.10 Acct-Terminate-Cause */
773f36db9c4Syasuoka 		cause = RADIUS_TERMNATE_CAUSE_SERVICE_UNAVAIL;
774f36db9c4Syasuoka 		if (sa->sa_reason) {
775f36db9c4Syasuoka 			if (strcmp(sa->sa_reason, "received delete") == 0) {
776f36db9c4Syasuoka 				cause = RADIUS_TERMNATE_CAUSE_USER_REQUEST;
777f36db9c4Syasuoka 			} else if (strcmp(sa->sa_reason, "SA rekeyed") == 0) {
778f36db9c4Syasuoka 				cause = RADIUS_TERMNATE_CAUSE_SESSION_TIMEOUT;
779f36db9c4Syasuoka 			} else if (strncmp(sa->sa_reason, "retransmit",
780f36db9c4Syasuoka 			    strlen("retransmit")) == 0) {
781f36db9c4Syasuoka 				cause = RADIUS_TERMNATE_CAUSE_LOST_SERVICE;
782f36db9c4Syasuoka 			} else if (strcmp(sa->sa_reason,
783f36db9c4Syasuoka 			    "disconnect requested") == 0) {
784f36db9c4Syasuoka 				cause = RADIUS_TERMNATE_CAUSE_ADMIN_RESET;
785f36db9c4Syasuoka 			}
786f36db9c4Syasuoka 		}
787f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_TERMINATE_CAUSE,
788f36db9c4Syasuoka 		    cause);
789f36db9c4Syasuoka 		/* I/O statistics {Input,Output}-{Packets,Octets,Gigawords} */
790f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_PACKETS,
791f36db9c4Syasuoka 		    sa->sa_stats.sas_ipackets);
792f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_PACKETS,
793f36db9c4Syasuoka 		    sa->sa_stats.sas_opackets);
794f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_OCTETS,
795f36db9c4Syasuoka 		    sa->sa_stats.sas_ibytes & 0xffffffffUL);
796f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_OCTETS,
797f36db9c4Syasuoka 		    sa->sa_stats.sas_obytes & 0xffffffffUL);
798f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_GIGAWORDS,
799f36db9c4Syasuoka 		    sa->sa_stats.sas_ibytes >> 32);
800f36db9c4Syasuoka 		radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS,
801f36db9c4Syasuoka 		    sa->sa_stats.sas_obytes >> 32);
802f36db9c4Syasuoka 		break;
803f36db9c4Syasuoka 	}
804f36db9c4Syasuoka 	req->rr_reqpkt = pkt;
805f36db9c4Syasuoka 	req->rr_ntry = 0;
806f36db9c4Syasuoka 	iked_radius_request_send(env, req);
807f36db9c4Syasuoka }
808f36db9c4Syasuoka 
809f36db9c4Syasuoka void
810f36db9c4Syasuoka iked_radius_dae_on_event(int fd, short ev, void *ctx)
811f36db9c4Syasuoka {
812f36db9c4Syasuoka 	struct iked_raddae	*dae = ctx;
813f36db9c4Syasuoka 	struct iked		*env = dae->rd_env;
814f36db9c4Syasuoka 	RADIUS_PACKET		*req = NULL, *res = NULL;
815f36db9c4Syasuoka 	struct sockaddr_storage	 ss;
816f36db9c4Syasuoka 	socklen_t		 sslen;
817f36db9c4Syasuoka 	struct iked_radclient	*client;
818f36db9c4Syasuoka 	struct iked_sa		*sa = NULL;
8194fa86b90Syasuoka 	char			 attr[256], username[256];
8204fa86b90Syasuoka 	char			*endp, *reason, *nakcause = NULL;
821f36db9c4Syasuoka 	int			 code, n = 0;
822f36db9c4Syasuoka 	uint64_t		 ispi = 0;
823f36db9c4Syasuoka 	uint32_t		 u32, cause = 0;
824f36db9c4Syasuoka 	struct iked_addr	*addr4 = NULL;
825f36db9c4Syasuoka 
826f36db9c4Syasuoka 	reason = "disconnect requested";
827f36db9c4Syasuoka 
828f36db9c4Syasuoka 	sslen = sizeof(ss);
829f36db9c4Syasuoka 	req = radius_recvfrom(dae->rd_sock, 0, (struct sockaddr *)&ss, &sslen);
830f36db9c4Syasuoka 	if (req == NULL) {
831f36db9c4Syasuoka 		log_warn("%s: receiving a RADIUS message failed: %s", __func__,
832f36db9c4Syasuoka 		    strerror(errno));
833f36db9c4Syasuoka 		return;
834f36db9c4Syasuoka 	}
835f36db9c4Syasuoka 	TAILQ_FOREACH(client, &env->sc_raddaeclients, rc_entry) {
836f36db9c4Syasuoka 		if (sockaddr_cmp((struct sockaddr *)&client->rc_sockaddr,
837f36db9c4Syasuoka 		    (struct sockaddr *)&ss, -1) == 0)
838f36db9c4Syasuoka 			break;
839f36db9c4Syasuoka 	}
840f36db9c4Syasuoka 	if (client == NULL) {
841f36db9c4Syasuoka 		log_warnx("%s: received RADIUS message from %s: "
842f36db9c4Syasuoka 		    "unknown client", __func__, print_addr(&ss));
843f36db9c4Syasuoka 		goto out;
844f36db9c4Syasuoka 	}
845f36db9c4Syasuoka 
846f36db9c4Syasuoka 	if (radius_check_accounting_request_authenticator(req,
847f36db9c4Syasuoka 	    client->rc_secret) != 0) {
848f36db9c4Syasuoka 		log_warnx("%s: received an invalid RADIUS message from %s: bad "
849f36db9c4Syasuoka 		    "response authenticator", __func__, print_addr(&ss));
850f36db9c4Syasuoka 		goto out;
851f36db9c4Syasuoka 	}
852f36db9c4Syasuoka 
853f36db9c4Syasuoka 	if ((code = radius_get_code(req)) != RADIUS_CODE_DISCONNECT_REQUEST) {
854f36db9c4Syasuoka 		/* Code other than Disconnect-Request is not supported */
855f36db9c4Syasuoka 		if (code == RADIUS_CODE_COA_REQUEST) {
856f36db9c4Syasuoka 			code = RADIUS_CODE_COA_NAK;
857f36db9c4Syasuoka 			cause = RADIUS_ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED;
8583a580dd9Syasuoka 			nakcause = "Coa-Request is not supported";
859f36db9c4Syasuoka 			goto send;
860f36db9c4Syasuoka 		}
861f36db9c4Syasuoka 		log_warnx("%s: received an invalid RADIUS message "
862f36db9c4Syasuoka 		    "from %s: unknown code %d", __func__,
863f36db9c4Syasuoka 		    print_addr(&ss), code);
864f36db9c4Syasuoka 		goto out;
865f36db9c4Syasuoka 	}
866f36db9c4Syasuoka 
867f36db9c4Syasuoka 	log_info("received Disconnect-Request from %s", print_addr(&ss));
868f36db9c4Syasuoka 
8694fa86b90Syasuoka 	if (radius_get_string_attr(req, RADIUS_TYPE_NAS_IDENTIFIER, attr,
8704fa86b90Syasuoka 	    sizeof(attr)) == 0 && strcmp(attr, IKED_NAS_ID) != 0) {
8714fa86b90Syasuoka 		cause = RADIUS_ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH;
8724fa86b90Syasuoka 		nakcause = "NAS-Identifier is not matched";
8734fa86b90Syasuoka 		goto search_done;
8744fa86b90Syasuoka 	}
8754fa86b90Syasuoka 
8764fa86b90Syasuoka 	/* prepare User-Name attribute */
8774fa86b90Syasuoka 	memset(username, 0, sizeof(username));
8784fa86b90Syasuoka 	radius_get_string_attr(req, RADIUS_TYPE_USER_NAME, username,
8794fa86b90Syasuoka 	    sizeof(username));
8804fa86b90Syasuoka 
881f36db9c4Syasuoka 	if (radius_get_string_attr(req, RADIUS_TYPE_ACCT_SESSION_ID, attr,
882f36db9c4Syasuoka 	    sizeof(attr)) == 0) {
8834fa86b90Syasuoka 		/* the client is to disconnect a session */
884f36db9c4Syasuoka 		ispi = strtoull(attr, &endp, 16);
8854fa86b90Syasuoka 		if (attr[0] == '\0' || *endp != '\0' || errno == ERANGE ||
8864fa86b90Syasuoka 		    ispi == ULLONG_MAX) {
8874fa86b90Syasuoka 			cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE;
8884fa86b90Syasuoka 			nakcause = "Session-Id is wrong";
8894fa86b90Syasuoka 			goto search_done;
8904fa86b90Syasuoka 
8914fa86b90Syasuoka 		}
892f36db9c4Syasuoka 		RB_FOREACH(sa, iked_sas, &env->sc_sas) {
8934fa86b90Syasuoka 			if (sa->sa_hdr.sh_ispi == ispi)
8944fa86b90Syasuoka 				break;
8954fa86b90Syasuoka 		}
8964fa86b90Syasuoka 		if (sa == NULL)
8974fa86b90Syasuoka 			goto search_done;
8984fa86b90Syasuoka 		if (username[0] != '\0' && (sa->sa_eapid == NULL ||
8994fa86b90Syasuoka 		    strcmp(username, sa->sa_eapid) != 0)) {
9004fa86b90Syasuoka 			/* specified User-Name attribute is mismatched */
9014fa86b90Syasuoka 			cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE;
9024fa86b90Syasuoka 			nakcause = "User-Name is not matched";
9034fa86b90Syasuoka 			goto search_done;
9044fa86b90Syasuoka 		}
905f36db9c4Syasuoka 		ikev2_ike_sa_setreason(sa, reason);
906f36db9c4Syasuoka 		ikev2_ike_sa_delete(env, sa);
907f36db9c4Syasuoka 		n++;
9084fa86b90Syasuoka 	} else if (username[0] != '\0') {
909f36db9c4Syasuoka 		RB_FOREACH(sa, iked_sas, &env->sc_sas) {
910f36db9c4Syasuoka 			if (sa->sa_eapid != NULL &&
9114fa86b90Syasuoka 			    strcmp(sa->sa_eapid, username) == 0) {
912f36db9c4Syasuoka 				ikev2_ike_sa_setreason(sa, reason);
913f36db9c4Syasuoka 				ikev2_ike_sa_delete(env, sa);
914f36db9c4Syasuoka 				n++;
915f36db9c4Syasuoka 			}
916f36db9c4Syasuoka 		}
9174fa86b90Syasuoka 	} else if (radius_get_uint32_attr(req, RADIUS_TYPE_FRAMED_IP_ADDRESS,
9184fa86b90Syasuoka 	    &u32) == 0) {
91965246e30Syasuoka 		RB_FOREACH(sa, iked_sas, &env->sc_sas) {
920f36db9c4Syasuoka 			addr4 = sa->sa_addrpool;
921f36db9c4Syasuoka 			if (addr4 != NULL) {
922f36db9c4Syasuoka 				if (u32 == ((struct sockaddr_in *)&addr4->addr)
923f36db9c4Syasuoka 				    ->sin_addr.s_addr) {
924f36db9c4Syasuoka 					ikev2_ike_sa_setreason(sa, reason);
925f36db9c4Syasuoka 					ikev2_ike_sa_delete(env, sa);
926f36db9c4Syasuoka 					n++;
927f36db9c4Syasuoka 				}
928f36db9c4Syasuoka 			}
929f36db9c4Syasuoka 		}
930f36db9c4Syasuoka 	}
9314fa86b90Syasuoka  search_done:
932f36db9c4Syasuoka 	if (n > 0)
933f36db9c4Syasuoka 		code = RADIUS_CODE_DISCONNECT_ACK;
934f36db9c4Syasuoka 	else {
9354fa86b90Syasuoka 		if (nakcause == NULL)
9364fa86b90Syasuoka 			nakcause = "session not found";
9374fa86b90Syasuoka 		if (cause == 0)
938f36db9c4Syasuoka 			cause = RADIUS_ERROR_CAUSE_SESSION_NOT_FOUND;
9394fa86b90Syasuoka 		code = RADIUS_CODE_DISCONNECT_NAK;
940f36db9c4Syasuoka 	}
941f36db9c4Syasuoka  send:
942f36db9c4Syasuoka 	res = radius_new_response_packet(code, req);
943f36db9c4Syasuoka 	if (res == NULL) {
944f36db9c4Syasuoka 		log_warn("%s: radius_new_response_packet", __func__);
945f36db9c4Syasuoka 		goto out;
946f36db9c4Syasuoka 	}
947f36db9c4Syasuoka 	if (cause != 0)
948f36db9c4Syasuoka 		radius_put_uint32_attr(res, RADIUS_TYPE_ERROR_CAUSE, cause);
9494fa86b90Syasuoka 	radius_set_response_authenticator(res, client->rc_secret);
950f36db9c4Syasuoka 	if (radius_sendto(dae->rd_sock, res, 0, (struct sockaddr *)&ss, sslen)
951f36db9c4Syasuoka 	    == -1)
952f36db9c4Syasuoka 		log_warn("%s: sendto", __func__);
9534fa86b90Syasuoka 	log_info("send %s for %s%s%s",
954f36db9c4Syasuoka 	    (code == RADIUS_CODE_DISCONNECT_ACK)? "Disconnect-ACK" :
955f36db9c4Syasuoka 	    (code == RADIUS_CODE_DISCONNECT_NAK)? "Disconnect-NAK" : "CoA-NAK",
9564fa86b90Syasuoka 	    print_addr(&ss), (nakcause)? ": " : "", (nakcause)? nakcause : "");
957f36db9c4Syasuoka  out:
958f36db9c4Syasuoka 	radius_delete_packet(req);
959f36db9c4Syasuoka 	if (res != NULL)
960f36db9c4Syasuoka 		radius_delete_packet(res);
961f36db9c4Syasuoka }
962