From: Tobias Heider Subject: Re: iked: RADIUS support To: YASUOKA Masahiko Cc: tech@openbsd.org, markus@openbsd.org, uwe@werler.is, bilias@edu.physics.uoc.gr, stu@spacehopper.org Date: Sat, 13 Jul 2024 14:16:42 +0200 On Fri, Jul 12, 2024 at 05:42:43PM +0200, YASUOKA Masahiko wrote: > Hello, > > On Mon, 26 Feb 2024 22:48:15 +0900 (JST) > YASUOKA Masahiko wrote: > > On Mon, 29 Jan 2024 09:43:55 +0900 (JST) > > YASUOKA Masahiko wrote: > >> On Thu, 25 Jan 2024 18:50:38 +0900 (JST) > >> YASUOKA Masahiko wrote: > >>> The diff adds RADIUS support for iked(8). > >>> > >>> --- > >>> ikev2 RAS passive esp \ > >>> from 0.0.0.0/0 to 0.0.0.0 \ > >>> local any peer any \ > >>> srcid (FQDN) \ > >>> eap radius \ > >>> config address 192.168.0.0/24 > >>> > >>> radius server 192.168.0.4 secret testing123 > >>> # radius accounting server 192.168.0.4 secret testing123 > >>> --- > >>> > >>> We can ask EAP for a RADIUS server which supports EAP. Unfortunetely > >>> radiusd(8) has no config which terminates EAP yet, so freeradius, > >>> Windows AD, or other is needed for test. > >>> > >>> Also > >>> > >>> - Use RADIUS attriubutes for configurations > >>> - RADIUS accouting is also supported > >>> > >>> comments? test? ok? > >> > >> Let me update the diff. Now I think it works with EAP methods other > >> than MSCHAP-V2. > >> > >> - feedbacks from markus > >> - support MSK which legnth != 16 > >> - give "iked_" for the functions in radiusd > >> - pass EAP messages which type isn't support eap.c > > > > Let me share the latest updated diff. There is no big difference. > > The followings are about the changes from the previous diff. > > > > - Fix wording in comments. Found by stu. > > - Check vendor-id and attribute-type in the config more strictly and > > accept hex. > > - Use imsg_get_fd(). > > - Use radius_get_eap_msk() in libradius (it's fixed now). > > - Use RADIUS_TYPE_FRAMED_IPV6_ADDRESS in (it's defined now). > > - Include NAS-Identifier in all RADIUS requests. > > - Send an Accounting-On message when RADIUS accouting is configured > > for the first time. > > Let me update the diff. Changes from the previous. > > - Add Dynamic Authorization Extensions (DAE) for RADIUS server > feature. (radiusd_ipcp(8) recently support DAE) > - When EAP is used without Identifier, overwrite eapid by > authenticated username. This is needed for accouting. > - Rearrange how to add the RADIUS attributes. > > The diff is getting bigger. How about committing the diff and > continue to work on the tree? I think you are right. let's get it in and continue working on it in-tree. ok tobhe@ > > > Index: sbin/iked/Makefile > =================================================================== > RCS file: /cvs/src/sbin/iked/Makefile,v > diff -u -p -u -p -r1.22 Makefile > --- sbin/iked/Makefile 28 May 2021 18:01:39 -0000 1.22 > +++ sbin/iked/Makefile 12 Jul 2024 15:22:40 -0000 > @@ -4,15 +4,15 @@ PROG= iked > SRCS= ca.c chap_ms.c config.c control.c crypto.c dh.c \ > eap.c iked.c ikev2.c ikev2_msg.c ikev2_pld.c \ > log.c ocsp.c pfkey.c policy.c print.c proc.c timer.c util.c \ > - imsg_util.c smult_curve25519_ref.c vroute.c > + imsg_util.c radius.c smult_curve25519_ref.c vroute.c > SRCS+= eap_map.c ikev2_map.c > SRCS+= crypto_hash.c sntrup761.c > SRCS+= parse.y > MAN= iked.conf.5 iked.8 > #NOMAN= yes > > -LDADD= -lutil -levent -lcrypto > -DPADD= ${LIBUTIL} ${LIBEVENT} ${LIBCRYPTO} > +LDADD= -lutil -levent -lcrypto -lradius > +DPADD= ${LIBUTIL} ${LIBEVENT} ${LIBCRYPTO} ${LIBRADIUS} > CFLAGS+= -Wall -I${.CURDIR} > CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes > CFLAGS+= -Wmissing-declarations > Index: sbin/iked/config.c > =================================================================== > RCS file: /cvs/src/sbin/iked/config.c,v > diff -u -p -u -p -r1.97 config.c > --- sbin/iked/config.c 15 Feb 2024 19:11:00 -0000 1.97 > +++ sbin/iked/config.c 12 Jul 2024 15:22:41 -0000 > @@ -123,6 +123,8 @@ config_free_sa(struct iked *env, struct > sa_configure_iface(env, sa, 0); > sa_free_flows(env, &sa->sa_flows); > > + iked_radius_acct_stop(env, sa); > + > if (sa->sa_addrpool) { > (void)RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa); > free(sa->sa_addrpool); > @@ -187,6 +189,10 @@ config_free_sa(struct iked *env, struct > ikestat_dec(env, ikes_sa_established_current); > ikestat_inc(env, ikes_sa_removed); > > + free(sa->sa_rad_addr); > + free(sa->sa_rad_addr6); > + iked_radius_request_free(env, sa->sa_radreq); > + > free(sa); > } > > @@ -591,6 +597,48 @@ config_doreset(struct iked *env, unsigne > } > } > > + if (mode == RESET_ALL || mode == RESET_RADIUS) { > + struct iked_radserver_req *req; > + struct iked_radserver *rad, *radt; > + struct iked_radcfgmap *cfg, *cfgt; > + struct iked_raddae *dae, *daet; > + struct iked_radclient *client, *clientt; > + > + TAILQ_FOREACH_SAFE(rad, &env->sc_radauthservers, rs_entry, > + radt) { > + close(rad->rs_sock); > + event_del(&rad->rs_ev); > + TAILQ_REMOVE(&env->sc_radauthservers, rad, rs_entry); > + while ((req = TAILQ_FIRST(&rad->rs_reqs)) != NULL) > + iked_radius_request_free(env, req); > + freezero(rad, sizeof(*rad)); > + } > + TAILQ_FOREACH_SAFE(rad, &env->sc_radacctservers, rs_entry, > + radt) { > + close(rad->rs_sock); > + event_del(&rad->rs_ev); > + TAILQ_REMOVE(&env->sc_radacctservers, rad, rs_entry); > + while ((req = TAILQ_FIRST(&rad->rs_reqs)) != NULL) > + iked_radius_request_free(env, req); > + freezero(rad, sizeof(*rad)); > + } > + TAILQ_FOREACH_SAFE(cfg, &env->sc_radcfgmaps, entry, cfgt) { > + TAILQ_REMOVE(&env->sc_radcfgmaps, cfg, entry); > + free(cfg); > + } > + TAILQ_FOREACH_SAFE(dae, &env->sc_raddaes, rd_entry, daet) { > + close(dae->rd_sock); > + event_del(&dae->rd_ev); > + TAILQ_REMOVE(&env->sc_raddaes, dae, rd_entry); > + free(dae); > + } > + TAILQ_FOREACH_SAFE(client, &env->sc_raddaeclients, rc_entry, > + clientt) { > + TAILQ_REMOVE(&env->sc_raddaeclients, client, rc_entry); > + free(client); > + } > + } > + > return (0); > } > > @@ -1089,6 +1137,285 @@ config_getkey(struct iked *env, struct i > > explicit_bzero(imsg->data, len); > ca_getkey(&env->sc_ps, &id, imsg->hdr.type); > + > + return (0); > +} > + > +int > +config_setradauth(struct iked *env) > +{ > + proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CFG_RADAUTH, > + &env->sc_radauth, sizeof(env->sc_radauth)); > + return (0); > +} > + > +int > +config_getradauth(struct iked *env, struct imsg *imsg) > +{ > + if (IMSG_DATA_SIZE(imsg) < sizeof(struct iked_radopts)) > + fatalx("%s: invalid radauth message", __func__); > + > + memcpy(&env->sc_radauth, imsg->data, sizeof(struct iked_radopts)); > + > + return (0); > +} > + > +int > +config_setradacct(struct iked *env) > +{ > + proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CFG_RADACCT, > + &env->sc_radacct, sizeof(env->sc_radacct)); > + return (0); > +} > + > +int > +config_getradacct(struct iked *env, struct imsg *imsg) > +{ > + if (IMSG_DATA_SIZE(imsg) < sizeof(struct iked_radopts)) > + fatalx("%s: invalid radacct message", __func__); > + > + memcpy(&env->sc_radacct, imsg->data, sizeof(struct iked_radopts)); > + > + return (0); > +} > + > +int > +config_setradserver(struct iked *env, struct sockaddr *sa, socklen_t salen, > + char *secret, int isaccounting) > +{ > + int sock = -1; > + struct iovec iov[2]; > + struct iked_radserver server; > + > + if (env->sc_opts & IKED_OPT_NOACTION) > + return (0); > + memset(&server, 0, sizeof(server)); > + memcpy(&server.rs_sockaddr, sa, salen); > + server.rs_accounting = isaccounting; > + if ((sock = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { > + log_warn("%s: socket() failed", __func__); > + goto error; > + } > + if (connect(sock, sa, salen) == -1) { > + log_warn("%s: connect() failed", __func__); > + goto error; > + } > + iov[0].iov_base = &server; > + iov[0].iov_len = offsetof(struct iked_radserver, rs_secret[0]); > + iov[1].iov_base = secret; > + iov[1].iov_len = strlen(secret) + 1; > + > + proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1, IMSG_CFG_RADSERVER, -1, > + sock, iov, 2); > + > + return (0); > + error: > + if (sock >= 0) > + close(sock); > + return (-1); > +} > + > +int > +config_getradserver(struct iked *env, struct imsg *imsg) > +{ > + size_t len; > + struct iked_radserver *server; > + > + len = IMSG_DATA_SIZE(imsg); > + if (len <= sizeof(*server)) > + fatalx("%s: invalid IMSG_CFG_RADSERVER message", __func__); > + > + if ((server = calloc(1, len)) == NULL) { > + log_warn("%s: calloc() failed", __func__); > + return (-1); > + } > + memcpy(server, imsg->data, len); > + explicit_bzero(imsg->data, len); > + TAILQ_INIT(&server->rs_reqs); > + server->rs_sock = imsg_get_fd(imsg); > + server->rs_env = env; > + > + if (!server->rs_accounting) > + TAILQ_INSERT_TAIL(&env->sc_radauthservers, server, rs_entry); > + else > + TAILQ_INSERT_TAIL(&env->sc_radacctservers, server, rs_entry); > + event_set(&server->rs_ev, server->rs_sock, EV_READ | EV_PERSIST, > + iked_radius_on_event, server); > + event_add(&server->rs_ev, NULL); > + > + return (0); > +} > + > +int > +config_setradcfgmap(struct iked *env, int cfg_type, uint32_t vendor_id, > + uint8_t attr_type) > +{ > + struct iked_radcfgmap cfgmap; > + > + if (env->sc_opts & IKED_OPT_NOACTION) > + return (0); > + memset(&cfgmap, 0, sizeof(cfgmap)); > + cfgmap.cfg_type = cfg_type; > + cfgmap.vendor_id = vendor_id; > + cfgmap.attr_type = attr_type; > + > + proc_compose_imsg(&env->sc_ps, PROC_IKEV2, -1, IMSG_CFG_RADCFGMAP, -1, > + -1, &cfgmap, sizeof(cfgmap)); > + > + return (0); > +} > + > +int > +config_getradcfgmap(struct iked *env, struct imsg *imsg) > +{ > + int i; > + size_t len; > + struct iked_radcfgmap *cfgmap, *cfgmap0; > + struct iked_radcfgmaps cfgmaps = TAILQ_HEAD_INITIALIZER(cfgmaps); > + > + len = IMSG_DATA_SIZE(imsg); > + if (len < sizeof(*cfgmap)) > + fatalx("%s: invalid IMSG_CFG_RADCFGMAP message", __func__); > + > + if (TAILQ_EMPTY(&env->sc_radcfgmaps)) { > + /* no customized config map yet */ > + for (i = 0; radius_cfgmaps[i].cfg_type != 0; i++) { > + if ((cfgmap = calloc(1, len)) == NULL) { > + while ((cfgmap = TAILQ_FIRST(&cfgmaps)) > + != NULL) { > + TAILQ_REMOVE(&cfgmaps, cfgmap, entry); > + free(cfgmap); > + } > + return (-1); > + } > + *cfgmap = radius_cfgmaps[i]; > + TAILQ_INSERT_TAIL(&cfgmaps, cfgmap, entry); > + } > + TAILQ_CONCAT(&env->sc_radcfgmaps, &cfgmaps, entry); > + } > + > + cfgmap0 = (struct iked_radcfgmap *)imsg->data; > + TAILQ_FOREACH(cfgmap, &env->sc_radcfgmaps, entry) { > + if (cfgmap->vendor_id == cfgmap0->vendor_id && > + cfgmap->attr_type == cfgmap0->attr_type) { > + /* override existing config map */ > + cfgmap->cfg_type = cfgmap0->cfg_type; > + break; > + } > + } > + if (cfgmap == NULL) { > + if ((cfgmap = calloc(1, len)) == NULL) { > + log_warn("%s: calloc() failed", __func__); > + return (-1); > + } > + memcpy(cfgmap, imsg->data, len); > + TAILQ_INSERT_TAIL(&env->sc_radcfgmaps, cfgmap, entry); > + } > + return (0); > +} > + > +int > +config_setraddae(struct iked *env, struct sockaddr *sa, socklen_t salen) > +{ > + int sock, on; > + struct iked_raddae dae; > + > + if (env->sc_opts & IKED_OPT_NOACTION) > + return (0); > + memset(&dae, 0, sizeof(dae)); > + memcpy(&dae.rd_sockaddr, sa, salen); > + if ((sock = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { > + log_warn("%s: socket() failed", __func__); > + goto error; > + } > + on = 1; > + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) > + log_warn("%s: setsockopt(,,SO_REUSEADDR) failed", __func__); > + /* REUSEPORT is needed because the old sockets may not be closed yet */ > + on = 1; > + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) == -1) > + log_warn("%s: setsockopt(,,SO_REUSEPORT) failed", __func__); > + if (bind(sock, sa, salen) == -1) { > + log_warn("%s: bind() failed", __func__); > + goto error; > + } > + > + proc_compose_imsg(&env->sc_ps, PROC_IKEV2, -1, IMSG_CFG_RADDAE, -1, > + sock, &dae, sizeof(dae)); > + > + return (0); > + error: > + if (sock >= 0) > + close(sock); > + return (-1); > +} > + > +int > +config_getraddae(struct iked *env, struct imsg *imsg) > +{ > + struct iked_raddae *dae; > + > + if (IMSG_DATA_SIZE(imsg) < sizeof(*dae)) > + fatalx("%s: invalid IMSG_CFG_RADDAE message", __func__); > + > + if ((dae = calloc(1, sizeof(*dae))) == NULL) { > + log_warn("%s: calloc() failed", __func__); > + return (-1); > + } > + memcpy(dae, imsg->data, sizeof(*dae)); > + dae->rd_sock = imsg_get_fd(imsg); > + dae->rd_env = env; > + > + event_set(&dae->rd_ev, dae->rd_sock, EV_READ | EV_PERSIST, > + iked_radius_dae_on_event, dae); > + event_add(&dae->rd_ev, NULL); > + > + TAILQ_INSERT_TAIL(&env->sc_raddaes, dae, rd_entry); > + > + return (0); > +} > + > +int > +config_setradclient(struct iked *env, struct sockaddr *sa, socklen_t salen, > + char *secret) > +{ > + struct iovec iov[2]; > + struct iked_radclient client; > + > + if (salen > sizeof(client.rc_sockaddr)) > + fatal("%s: invalid salen", __func__); > + > + memcpy(&client.rc_sockaddr, sa, salen); > + > + iov[0].iov_base = &client; > + iov[0].iov_len = offsetof(struct iked_radclient, rc_secret[0]); > + iov[1].iov_base = secret; > + iov[1].iov_len = strlen(secret); > + > + proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1, IMSG_CFG_RADDAECLIENT, > + -1, -1, iov, 2); > + > + return (0); > +} > + > +int > +config_getradclient(struct iked *env, struct imsg *imsg) > +{ > + struct iked_radclient *client; > + u_int len; > + > + len = IMSG_DATA_SIZE(imsg); > + > + if (len < sizeof(*client)) > + fatalx("%s: invalid IMSG_CFG_RADDAE message", __func__); > + > + if ((client = calloc(1, len + 1)) == NULL) { > + log_warn("%s: calloc() failed", __func__); > + return (-1); > + } > + memcpy(client, imsg->data, len); > + > + TAILQ_INSERT_TAIL(&env->sc_raddaeclients, client, rc_entry); > > return (0); > } > Index: sbin/iked/eap.c > =================================================================== > RCS file: /cvs/src/sbin/iked/eap.c,v > diff -u -p -u -p -r1.26 eap.c > --- sbin/iked/eap.c 24 Mar 2024 00:05:01 -0000 1.26 > +++ sbin/iked/eap.c 12 Jul 2024 15:22:41 -0000 > @@ -583,9 +583,12 @@ eap_parse(struct iked *env, const struct > > return (eap_mschap(env, sa, msg, eap)); > default: > - log_debug("%s: unsupported EAP type %s", __func__, > - print_map(eap->eap_type, eap_type_map)); > - return (-1); > + if (sa->sa_policy->pol_auth.auth_eap != EAP_TYPE_RADIUS) { > + log_debug("%s: unsupported EAP type %s", __func__, > + print_map(eap->eap_type, eap_type_map)); > + return (-1); > + } /* else, when RADIUS, pass it to the client */ > + break; > } > > return (0); > Index: sbin/iked/eap.h > =================================================================== > RCS file: /cvs/src/sbin/iked/eap.h,v > diff -u -p -u -p -r1.6 eap.h > --- sbin/iked/eap.h 16 Sep 2020 21:37:35 -0000 1.6 > +++ sbin/iked/eap.h 12 Jul 2024 15:22:41 -0000 > @@ -93,6 +93,7 @@ extern struct iked_constmap eap_code_map > #define EAP_TYPE_PWD 52 /* RFC-harkins-emu-eap-pwd-12.txt */ > #define EAP_TYPE_EXPANDED_TYPE 254 /* RFC3748 */ > #define EAP_TYPE_EXPERIMENTAL 255 /* RFC3748 */ > +#define EAP_TYPE_RADIUS 10001 /* internal use for EAP RADIUS */ > > extern struct iked_constmap eap_type_map[]; > > Index: sbin/iked/iked.c > =================================================================== > RCS file: /cvs/src/sbin/iked/iked.c,v > diff -u -p -u -p -r1.70 iked.c > --- sbin/iked/iked.c 15 Feb 2024 20:10:45 -0000 1.70 > +++ sbin/iked/iked.c 12 Jul 2024 15:22:42 -0000 > @@ -307,6 +307,8 @@ parent_configure(struct iked *env) > config_setstatic(env); > config_setcoupled(env, env->sc_decoupled ? 0 : 1); > config_setocsp(env); > + config_setradauth(env); > + config_setradacct(env); > /* Must be last */ > config_setmode(env, env->sc_passive ? 1 : 0); > > @@ -324,6 +326,7 @@ parent_reload(struct iked *env, int rese > > if (reset == RESET_RELOAD) { > config_setreset(env, RESET_POLICY, PROC_IKEV2); > + config_setreset(env, RESET_RADIUS, PROC_IKEV2); > if (config_setkeys(env) == -1) > fatalx("%s: failed to send keys", __func__); > config_setreset(env, RESET_CA, PROC_CERT); > Index: sbin/iked/iked.conf.5 > =================================================================== > RCS file: /cvs/src/sbin/iked/iked.conf.5,v > diff -u -p -u -p -r1.96 iked.conf.5 > --- sbin/iked/iked.conf.5 13 Apr 2024 12:11:08 -0000 1.96 > +++ sbin/iked/iked.conf.5 12 Jul 2024 15:22:42 -0000 > @@ -648,11 +648,18 @@ for more information. > .Bl -tag -width $domain -compact -offset indent > .It Ic eap Ar type > Use EAP to authenticate the initiator. > -The only supported EAP > -.Ar type > -is currently > -.Ar MSCHAP-V2 . > +Currently > +.Ar MSCHAP-V2 > +or > +.Ar RADIUS > +is supported for EAP > +.Ar type . > The responder will use RSA public key authentication. > +To use RADIUS for EAP, > +at least one RADIUS server should be configured. > +See > +.Sx RADIUS > +section for the RADIUS support. > .It Ic ecdsa256 > Use ECDSA with a 256-bit elliptic curve key and SHA2-256 for authentication. > .It Ic ecdsa384 > @@ -779,6 +786,118 @@ for filtering and monitoring. > The traffic will be blocked if the specified > .Ar interface > does not exist. > +.El > +.Sh RADIUS CONFIGURATION > +.Pp > +The configuration options for RADIUS are as follows: > +.Bl -tag -width xxxx > +.It Ic radius config Oo Ar af Oc Ar option Oo Ar vendor Oc Ar attr > +When the RADIUS authentication succeeded, > +.Xr iked 8 > +uses the RADIUS attributes contained the response from the RADIUS server to > +construct IKEv2 configuration payloads (CP). > +This configuration option defines a mapping from a RADIUS attribute to an IKE > +CP with the following parameters: > +.Pp > +.Bl -tag -width "vendor attr" -compact > +.It Op Ar af > +Specify either > +.Ar inet > +or > +.Ar inet6 > +for the address family of the IKE CP option. > +.It Ar option > +Specify an IKE CP option. > +Choose from > +.Sx AUTOMATIC KEYING POLICIES > +config options > +.Po > +.Ic address , > +.Ic netmask , > +.Ic name-server , > +.Ic netbios-server , > +.Ic dhcp-server , > +and > +.Ic access-server > +.Pc , > +or use > +.Ic none > +to disable the existing or default mapping. > +.It Ar attr > +For a standard RADIUS attribute, > +specify its Attribute-Type for > +.Ar attr . > +.It Ar vendor Ar attr > +For a vendor specific RADIUS attribute, > +specify its Vendor-ID for > +.Ar vendor > +and the Attribute-Type for > +.Ar attr . > +.El > +.Pp > +By default, > +.Xr iked 8 > +uses the following attributes for the options: > +.Bl -column "inet6 netbios-server" "Vendor" "Type" "MS-Secondary-NBNS-Server" \ > +-offset "XX" > +.It Em "Option" Ta Em "Vendor" Ta Em "Type" Ta Em "Attribute Name" > +.It Li "inet address" Ta "" Ta "8" Ta "Framed-IP-Address" > +.It Li "inet netmask" Ta "" Ta "9" Ta "Framed-IP-Netmask" > +.It Li "inet name-server" Ta "0x137" Ta "28" Ta "MS-Primary-DNS-Server" > +.It Li "inet name-server" Ta "0x137" Ta "29" Ta "MS-Secondary-DNS-Server" > +.It Li "inet netbios-server" Ta "0x137" Ta "30" Ta "MS-Primary-NBNS-Server" > +.It Li "inet netbios-server" Ta "0x137" Ta "31" Ta "MS-Secondary-NBNS-Server" > +.El > +.It Ic radius Oo Ic accounting Oc Ic server Ar address Oo port Ar number Oc \ > +secret Ar secret > +Specify the RADIUS server's IP address and the shared secret with the server. > +For a RADIUS accounting server, > +specify optional > +.Ic accounting > +keyword. > +Optionally specify the port number, > +otherwise the default port number, > +1812 for authentication or > +1813 for accounting, > +is used as the default. > +.It Ic radius Oo Ic accounting Oc Ic max-tries Ar number > +Specify the maximum number of retransmissions for a server. > +.Xr iked 8 > +will retransmit 2, 6, 14, 22, 30 seconds after the first transmission > +and subsequent retransmissions will occur every 8 seconds. > +If the number of retransmissions per server reaches this value, > +the current server is marked as failed, > +and the next server is used for subsequent requests. > +For RADIUS accounting requests, > +specify optional > +.Ic accounting > +keyword. > +The default value is 3. > +.It Ic radius Oo Ic accounting Oc Ic max-failovers Ar number > +If a positive number is specified, > +.Xr iked 8 > +will failover to the next server when the current server is marked > +.Dq fail . > +This key and value specifies the maximum number of failovers. > +For RADIUS accounting requests, > +specify optional > +.Ic accounting > +keyword. > +The default value is 0. > +.It Ic radius dae listen on Ar address Oo port Ar number Oc > +Specify the local > +.Ar address > +.Xr iked 8 > +should listen on for the Dynamic Authorization Extensions > +.Po DAE, RFC 5176 Pc requests, > +Optionally specify a port > +.Ar number, > +the default port number is 3799. > +.It Ic radius dae client Ar address Ic secret Ar secret > +Specify > +.Ar address > +for a DAE client and > +.Ar secret . > .El > .Sh PACKET FILTERING > IPsec traffic appears unencrypted on the > Index: sbin/iked/iked.h > =================================================================== > RCS file: /cvs/src/sbin/iked/iked.h,v > diff -u -p -u -p -r1.230 iked.h > --- sbin/iked/iked.h 2 Mar 2024 16:16:07 -0000 1.230 > +++ sbin/iked/iked.h 12 Jul 2024 15:22:42 -0000 > @@ -20,6 +20,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -217,8 +218,8 @@ struct iked_static_id { > > struct iked_auth { > uint8_t auth_method; > - uint8_t auth_eap; /* optional EAP */ > uint8_t auth_length; /* zero if EAP */ > + uint16_t auth_eap; /* optional EAP */ > uint8_t auth_data[IKED_PSK_SIZE]; > }; > > @@ -403,6 +404,15 @@ struct iked_ipcomp { > uint8_t ic_transform; /* transform */ > }; > > +struct iked_sastats { > + uint64_t sas_ipackets; > + uint64_t sas_opackets; > + uint64_t sas_ibytes; > + uint64_t sas_obytes; > + uint64_t sas_idrops; > + uint64_t sas_odrops; > +}; > + > struct iked_sa { > struct iked_sahdr sa_hdr; > uint32_t sa_msgid; /* Last request rcvd */ > @@ -485,6 +495,7 @@ struct iked_sa { > struct iked_proposals sa_proposals; /* SA proposals */ > struct iked_childsas sa_childsas; /* IPsec Child SAs */ > struct iked_saflows sa_flows; /* IPsec flows */ > + struct iked_sastats sa_stats; > > struct iked_sa *sa_nexti; /* initiated IKE SA */ > struct iked_sa *sa_previ; /* matching back pointer */ > @@ -533,6 +544,11 @@ struct iked_sa { > RB_ENTRY(iked_sa) sa_addrpool6_entry; /* pool entries */ > time_t sa_last_recvd; > #define IKED_IKE_SA_LAST_RECVD_TIMEOUT 300 /* 5 minutes */ > + struct timespec sa_starttime; > + > + struct iked_radserver_req *sa_radreq; > + struct iked_addr *sa_rad_addr; /* requested address */ > + struct iked_addr *sa_rad_addr6; /* requested address */ > }; > RB_HEAD(iked_sas, iked_sa); > RB_HEAD(iked_dstid_sas, iked_sa); > @@ -648,6 +664,7 @@ struct iked_message { > uint8_t msg_transform; > uint16_t msg_flags; > struct eap_msg msg_eap; > + struct ibuf *msg_eapmsg; > size_t msg_del_spisize; > size_t msg_del_cnt; > struct ibuf *msg_del_buf; > @@ -702,6 +719,72 @@ struct iked_user { > }; > RB_HEAD(iked_users, iked_user); > > +struct iked_radserver_req; > + > +struct iked_radserver { > + int rs_sock; > + int rs_accounting; > + struct event rs_ev; > + struct iked *rs_env; > + struct sockaddr_storage rs_sockaddr; > + TAILQ_ENTRY(iked_radserver) rs_entry; > + struct in_addr rs_nas_ipv4; > + struct in6_addr rs_nas_ipv6; > + unsigned int rs_reqseq; > + TAILQ_HEAD(, iked_radserver_req) rs_reqs; > + char rs_secret[]; > +}; > +TAILQ_HEAD(iked_radservers, iked_radserver); > + > +struct iked_raddae { > + int rd_sock; > + struct event rd_ev; > + struct iked *rd_env; > + struct sockaddr_storage rd_sockaddr; > + TAILQ_ENTRY(iked_raddae) rd_entry; > +}; > +TAILQ_HEAD(iked_raddaes, iked_raddae); > + > +struct iked_radclient { > + struct iked *rc_env; > + struct sockaddr_storage rc_sockaddr; > + TAILQ_ENTRY(iked_radclient) rc_entry; > + char rc_secret[]; > +}; > +TAILQ_HEAD(iked_radclients , iked_radclient); > + > +struct iked_radopts { > + int max_tries; > + int max_failovers; > +}; > + > +struct iked_radcfgmap { > + uint16_t cfg_type; > + uint32_t vendor_id; > + uint8_t attr_type; > + TAILQ_ENTRY(iked_radcfgmap) entry; > +}; > +TAILQ_HEAD(iked_radcfgmaps, iked_radcfgmap); > + > +extern const struct iked_radcfgmap radius_cfgmaps[]; > + > +struct iked_radserver_req { > + struct iked_radserver *rr_server; > + struct iked_sa *rr_sa; > + struct iked_timer rr_timer; > + int rr_reqid; > + int rr_accounting; > + struct timespec rr_accttime; > + void *rr_reqpkt; > + struct ibuf *rr_state; > + char *rr_user; > + int rr_ntry; > + int rr_nfailover; > + struct iked_cfg rr_cfg[IKED_CFG_MAX]; > + unsigned int rr_ncfg; > + TAILQ_ENTRY(iked_radserver_req) rr_entry; > +}; > + > struct privsep_pipes { > int *pp_pipes[PROC_MAX]; > }; > @@ -810,6 +893,14 @@ struct iked { > struct iked_activesas sc_activesas; > struct iked_flows sc_activeflows; > struct iked_users sc_users; > + struct iked_radopts sc_radauth; > + struct iked_radopts sc_radacct; > + int sc_radaccton; > + struct iked_radservers sc_radauthservers; > + struct iked_radservers sc_radacctservers; > + struct iked_radcfgmaps sc_radcfgmaps; > + struct iked_raddaes sc_raddaes; > + struct iked_radclients sc_raddaeclients; > > struct iked_stats sc_stats; > > @@ -941,6 +1032,20 @@ int config_setkeys(struct iked *); > int config_getkey(struct iked *, struct imsg *); > int config_setstatic(struct iked *); > int config_getstatic(struct iked *, struct imsg *); > +int config_setradauth(struct iked *); > +int config_getradauth(struct iked *, struct imsg *); > +int config_setradacct(struct iked *); > +int config_getradacct(struct iked *, struct imsg *); > +int config_setradserver(struct iked *, struct sockaddr *, socklen_t, > + char *, int); > +int config_getradserver(struct iked *, struct imsg *); > +int config_setradcfgmap(struct iked *, int, uint32_t, uint8_t); > +int config_getradcfgmap(struct iked *, struct imsg *); > +int config_setraddae(struct iked *, struct sockaddr *, socklen_t); > +int config_getraddae(struct iked *, struct imsg *); > +int config_setradclient(struct iked *, struct sockaddr *, socklen_t, > + char *); > +int config_getradclient(struct iked *, struct imsg *); > > /* policy.c */ > void policy_init(struct iked *); > @@ -1156,6 +1261,17 @@ int eap_mschap_challenge(struct iked *, > uint8_t *, size_t); > int eap_mschap_success(struct iked *, struct iked_sa *, int); > int eap_challenge_request(struct iked *, struct iked_sa *, int); > + > +/* radius.c */ > +int iked_radius_request(struct iked *, struct iked_sa *, > + struct iked_message *); > +void iked_radius_request_free(struct iked *, struct iked_radserver_req *); > +void iked_radius_on_event(int, short, void *); > +void iked_radius_acct_on(struct iked *); > +void iked_radius_acct_off(struct iked *); > +void iked_radius_acct_start(struct iked *, struct iked_sa *); > +void iked_radius_acct_stop(struct iked *, struct iked_sa *); > +void iked_radius_dae_on_event(int, short, void *); > > /* pfkey.c */ > int pfkey_couple(struct iked *, struct iked_sas *, int); > Index: sbin/iked/ikev2.c > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2.c,v > diff -u -p -u -p -r1.386 ikev2.c > --- sbin/iked/ikev2.c 21 Mar 2024 22:08:49 -0000 1.386 > +++ sbin/iked/ikev2.c 12 Jul 2024 15:22:44 -0000 > @@ -36,6 +36,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -284,6 +285,7 @@ ikev2_dispatch_parent(int fd, struct pri > timer_add(env, &env->sc_inittmr, > IKED_INITIATOR_INITIAL); > } > + iked_radius_acct_on(env); > return (0); > case IMSG_UDP_SOCKET: > return (config_getsocket(env, imsg, ikev2_msg_cb)); > @@ -295,6 +297,18 @@ ikev2_dispatch_parent(int fd, struct pri > return (config_getflow(env, imsg)); > case IMSG_CFG_USER: > return (config_getuser(env, imsg)); > + case IMSG_CFG_RADAUTH: > + return (config_getradauth(env, imsg)); > + case IMSG_CFG_RADACCT: > + return (config_getradacct(env, imsg)); > + case IMSG_CFG_RADSERVER: > + return (config_getradserver(env, imsg)); > + case IMSG_CFG_RADCFGMAP: > + return (config_getradcfgmap(env, imsg)); > + case IMSG_CFG_RADDAE: > + return (config_getraddae(env, imsg)); > + case IMSG_CFG_RADDAECLIENT: > + return (config_getradclient(env, imsg)); > case IMSG_COMPILE: > return (config_getcompile(env)); > case IMSG_CTL_STATIC: > @@ -1782,6 +1796,7 @@ ikev2_init_done(struct iked *env, struct > ret = ikev2_childsa_enable(env, sa); > if (ret == 0) { > sa_state(env, sa, IKEV2_STATE_ESTABLISHED); > + iked_radius_acct_start(env, sa); > /* Delete exchange timeout. */ > timer_del(env, &sa->sa_timer); > ikev2_enable_timer(env, sa); > @@ -2456,7 +2471,7 @@ ikev2_add_cp(struct iked *env, struct ik > struct ikev2_cp *cp; > struct ikev2_cfg *cfg; > struct iked_cfg *ikecfg; > - unsigned int i; > + unsigned int i, rad_ncfg = 0; > uint32_t mask4; > size_t len; > struct sockaddr_in *in4; > @@ -2479,8 +2494,15 @@ ikev2_add_cp(struct iked *env, struct ik > return (-1); > } > > - for (i = 0; i < pol->pol_ncfg; i++) { > - ikecfg = &pol->pol_cfg[i]; > + if (sa->sa_radreq != NULL) > + rad_ncfg = sa->sa_radreq->rr_ncfg; > + > + for (i = 0; i < pol->pol_ncfg + rad_ncfg; i++) { > + if (i < pol->pol_ncfg) > + ikecfg = &pol->pol_cfg[i]; > + else > + ikecfg = &sa->sa_radreq->rr_cfg[i - pol->pol_ncfg]; > + > if (ikecfg->cfg_action != cp->cp_type) > continue; > /* only return one address in case of multiple pools */ > @@ -3857,6 +3879,8 @@ ikev2_resp_ike_eap(struct iked *env, str > switch (sa->sa_policy->pol_auth.auth_eap) { > case EAP_TYPE_MSCHAP_V2: > return ikev2_resp_ike_eap_mschap(env, sa, msg); > + case EAP_TYPE_RADIUS: > + return iked_radius_request(env, sa, msg); > } > return -1; > } > @@ -4012,6 +4036,7 @@ ikev2_resp_ike_auth(struct iked *env, st > ret = ikev2_childsa_enable(env, sa); > if (ret == 0) { > sa_state(env, sa, IKEV2_STATE_ESTABLISHED); > + iked_radius_acct_start(env, sa); > /* Delete exchange timeout. */ > timer_del(env, &sa->sa_timer); > ikev2_enable_timer(env, sa); > @@ -4746,10 +4771,10 @@ ikev2_ikesa_enable(struct iked *env, str > nsa->sa_tag = sa->sa_tag; > sa->sa_tag = NULL; > } > - if (sa->sa_eapid) { > - nsa->sa_eapid = sa->sa_eapid; > - sa->sa_eapid = NULL; > - } > + /* sa_eapid needs to be set on both for radius accounting */ > + if (sa->sa_eapid) > + nsa->sa_eapid = strdup(sa->sa_eapid); > + > log_info("%srekeyed as new IKESA %s (enc %s%s%s group %s prf %s)", > SPI_SA(sa, NULL), print_spi(nsa->sa_hdr.sh_ispi, 8), > print_xf(nsa->sa_encr->encr_id, cipher_keylength(nsa->sa_encr) - > @@ -4760,6 +4785,8 @@ ikev2_ikesa_enable(struct iked *env, str > print_xf(nsa->sa_dhgroup->id, 0, groupxfs), > print_xf(nsa->sa_prf->hash_id, hash_keylength(sa->sa_prf), prfxfs)); > sa_state(env, nsa, IKEV2_STATE_ESTABLISHED); > + clock_gettime(CLOCK_MONOTONIC, &nsa->sa_starttime); > + iked_radius_acct_start(env, nsa); > ikev2_enable_timer(env, nsa); > > ikestat_inc(env, ikes_sa_rekeyed); > @@ -7028,6 +7055,7 @@ ikev2_cp_setaddr(struct iked *env, struc > const char *errstr = NULL; > int ret, pass, passes; > size_t i; > + struct sockaddr_in *in4; > > switch (family) { > case AF_INET: > @@ -7045,8 +7073,23 @@ ikev2_cp_setaddr(struct iked *env, struc > return (0); > /* default if no pool configured */ > ret = 0; > + > + /* handle the special addresses from RADIUS */ > + if (sa->sa_rad_addr != NULL) { > + in4 = (struct sockaddr_in *)&sa->sa_rad_addr->addr; > + /* 0xFFFFFFFF allows the user to select an address (RFC 2865) */ > + if (in4->sin_addr.s_addr == htonl(0xFFFFFFFF)) > + ;/* this is default behavior if the user selects */ > + /* 0xFFFFFFFE indicated the NAS should select (RFC 2865) */ > + else if (in4->sin_addr.s_addr == htonl(0xFFFFFFFE)) { > + free(sa->sa_cp_addr); > + sa->sa_cp_addr = NULL; > + } > + } > + > /* two passes if client requests from specific pool */ > - passes = (sa->sa_cp_addr != NULL || sa->sa_cp_addr6 != NULL) ? 2 : 1; > + passes = (sa->sa_cp_addr != NULL || sa->sa_cp_addr6 != NULL || > + sa->sa_rad_addr != NULL || sa->sa_rad_addr6 != NULL) ? 2 : 1; > for (pass = 0; pass < passes; pass++) { > /* loop over all address pool configs (addr_net) */ > for (i = 0; i < pol->pol_ncfg; i++) { > @@ -7062,13 +7105,16 @@ ikev2_cp_setaddr(struct iked *env, struc > return (0); > } > } > - if (sa->sa_cp_addr != NULL) { > + if (family == AF_INET) { > free(sa->sa_cp_addr); > sa->sa_cp_addr = NULL; > - } > - if (sa->sa_cp_addr6 != NULL) { > + free(sa->sa_rad_addr); > + sa->sa_rad_addr = NULL; > + } else { > free(sa->sa_cp_addr6); > sa->sa_cp_addr6 = NULL; > + free(sa->sa_rad_addr6); > + sa->sa_rad_addr6 = NULL; > } > } > > @@ -7088,7 +7134,7 @@ ikev2_cp_setaddr_pool(struct iked *env, > char idstr[IKED_ID_SIZE]; > struct iked_addr addr; > uint32_t mask, host, lower, upper, start, nhost; > - int requested = 0; > + int requested = 0, rad_requested = 0; > > /* > * failure: pool configured, but not requested. > @@ -7165,8 +7211,14 @@ ikev2_cp_setaddr_pool(struct iked *env, > case AF_INET: > cfg4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; > mask = prefixlen2mask(ikecfg->cfg.address.addr_mask); > - if (sa->sa_cp_addr != NULL) { > - memcpy(&addr, sa->sa_cp_addr, sizeof(addr)); > + if (sa->sa_cp_addr != NULL || sa->sa_rad_addr != NULL) { > + if (sa->sa_rad_addr != NULL) { > + rad_requested = 1; > + memcpy(&addr, sa->sa_rad_addr, sizeof(addr)); > + } else { > + requested = 1; > + memcpy(&addr, sa->sa_cp_addr, sizeof(addr)); > + } > key.sa_addrpool = &addr; > in4 = (struct sockaddr_in *)&addr.addr; > if ((in4->sin_addr.s_addr & mask) != > @@ -7179,10 +7231,16 @@ ikev2_cp_setaddr_pool(struct iked *env, > *errstr = "requested addr in use"; > return (-1); > } > - sa->sa_addrpool = sa->sa_cp_addr; > - sa->sa_cp_addr = NULL; > + if (sa->sa_rad_addr != NULL) { > + sa->sa_addrpool = sa->sa_rad_addr; > + sa->sa_rad_addr = NULL; > + } else { > + sa->sa_addrpool = sa->sa_cp_addr; > + sa->sa_cp_addr = NULL; > + } > + free(sa->sa_cp_addr); > + free(sa->sa_rad_addr); > RB_INSERT(iked_addrpool, &env->sc_addrpool, sa); > - requested = 1; > goto done; > } > in4 = (struct sockaddr_in *)&addr.addr; > @@ -7194,7 +7252,7 @@ ikev2_cp_setaddr_pool(struct iked *env, > case AF_INET6: > cfg6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr; > in6 = (struct sockaddr_in6 *)&addr.addr; > - if (sa->sa_cp_addr6 != NULL) { > + if (sa->sa_cp_addr6 != NULL || sa->sa_rad_addr6 != NULL) { > /* XXX not yet supported */ > } > in6->sin6_family = AF_INET6; > @@ -7280,9 +7338,10 @@ ikev2_cp_setaddr_pool(struct iked *env, > done: > if (ikev2_print_id(IKESA_DSTID(sa), idstr, sizeof(idstr)) == -1) > bzero(idstr, sizeof(idstr)); > - log_info("%sassigned address %s to %s%s", SPI_SA(sa, NULL), > + log_info("%sassigned address %s to %s%s%s", SPI_SA(sa, NULL), > print_addr(&addr.addr), > - idstr, requested ? " (requested by peer)" : ""); > + idstr, requested ? " (requested by peer)" : "", > + rad_requested? "(requested by RADIUS)" : ""); > return (0); > } > > @@ -7627,6 +7686,8 @@ void > ikev2_log_established(struct iked_sa *sa) > { > char dstid[IKED_ID_SIZE], srcid[IKED_ID_SIZE]; > + > + clock_gettime(CLOCK_MONOTONIC, &sa->sa_starttime); > > if (ikev2_print_id(IKESA_DSTID(sa), dstid, sizeof(dstid)) == -1) > bzero(dstid, sizeof(dstid)); > Index: sbin/iked/ikev2_msg.c > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v > diff -u -p -u -p -r1.101 ikev2_msg.c > --- sbin/iked/ikev2_msg.c 2 Mar 2024 16:16:07 -0000 1.101 > +++ sbin/iked/ikev2_msg.c 12 Jul 2024 15:22:45 -0000 > @@ -203,6 +203,7 @@ ikev2_msg_cleanup(struct iked *env, stru > ibuf_free(msg->msg_cookie); > ibuf_free(msg->msg_cookie2); > ibuf_free(msg->msg_del_buf); > + ibuf_free(msg->msg_eapmsg); > free(msg->msg_eap.eam_user); > free(msg->msg_cp_addr); > free(msg->msg_cp_addr6); > @@ -219,6 +220,7 @@ ikev2_msg_cleanup(struct iked *env, stru > msg->msg_cookie = NULL; > msg->msg_cookie2 = NULL; > msg->msg_del_buf = NULL; > + msg->msg_eapmsg = NULL; > msg->msg_eap.eam_user = NULL; > msg->msg_cp_addr = NULL; > msg->msg_cp_addr6 = NULL; > Index: sbin/iked/ikev2_pld.c > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v > diff -u -p -u -p -r1.135 ikev2_pld.c > --- sbin/iked/ikev2_pld.c 2 Apr 2024 19:58:28 -0000 1.135 > +++ sbin/iked/ikev2_pld.c 12 Jul 2024 15:22:45 -0000 > @@ -2104,6 +2104,15 @@ ikev2_pld_eap(struct iked *env, struct i > > if (eap_parse(env, sa, msg, eap, msg->msg_response) == -1) > return (-1); > + if (msg->msg_parent->msg_eapmsg != NULL) { > + log_info("%s: duplicate EAP in payload", __func__); > + return (-1); > + } > + if ((msg->msg_parent->msg_eapmsg = ibuf_new(eap, eap_len)) > + == NULL) { > + log_debug("%s: failed to save eap", __func__); > + return (-1); > + } > msg->msg_parent->msg_eap.eam_found = 1; > } > > Index: sbin/iked/parse.y > =================================================================== > RCS file: /cvs/src/sbin/iked/parse.y,v > diff -u -p -u -p -r1.146 parse.y > --- sbin/iked/parse.y 25 Apr 2024 14:24:54 -0000 1.146 > +++ sbin/iked/parse.y 12 Jul 2024 15:22:45 -0000 > @@ -38,9 +38,12 @@ > #include > #include > #include > +#include > #include > #include > +#include > #include > +#include > #include > #include > #include > @@ -107,6 +110,8 @@ static char *ocsp_url = NULL; > static long ocsp_tolerate = 0; > static long ocsp_maxage = -1; > static int cert_partial_chain = 0; > +static struct iked_radopts > + radauth, radacct; > > struct iked_transform ikev2_default_ike_transforms[] = { > { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 }, > @@ -394,6 +399,8 @@ static int expand_flows(struct iked_po > struct ipsec_addr_wrap *); > static struct ipsec_addr_wrap * > expand_keyword(struct ipsec_addr_wrap *); > +struct iked_radserver * > + create_radserver(const char *, u_short, const char *); > > struct ipsec_transforms *ipsec_transforms; > struct ipsec_filters *ipsec_filters; > @@ -407,6 +414,7 @@ typedef struct { > uint8_t ikemode; > uint8_t dir; > uint8_t satype; > + uint8_t accounting; > char *string; > uint16_t port; > struct ipsec_hosts *hosts; > @@ -427,6 +435,10 @@ typedef struct { > struct ipsec_transforms *transforms; > struct ipsec_filters *filters; > struct ipsec_mode *mode; > + struct { > + uint32_t vendorid; > + uint8_t attrtype; > + } radattr; > } v; > int lineno; > } YYSTYPE; > @@ -446,6 +458,8 @@ typedef struct { > %token TOLERATE MAXAGE DYNAMIC > %token CERTPARTIALCHAIN > %token REQUEST IFACE > +%token RADIUS ACCOUNTING SERVER SECRET MAX_TRIES MAX_FAILOVERS > +%token CLIENT DAE LISTEN ON > %token STRING > %token NUMBER > %type string > @@ -453,7 +467,7 @@ typedef struct { > %type proto proto_list protoval > %type hosts hosts_list > %type port > -%type portval af rdomain > +%type portval af rdomain hexdecnumber > %type peers > %type anyhost > %type host host_spec > @@ -470,6 +484,8 @@ typedef struct { > %type name iface > %type cfg ikecfg ikecfgvals > %type transform_esn > +%type accounting > +%type radattr > %% > > grammar : /* empty */ > @@ -478,6 +494,7 @@ grammar : /* empty */ > | grammar set '\n' > | grammar user '\n' > | grammar ikev2rule '\n' > + | grammar radius '\n' > | grammar varset '\n' > | grammar otherrule skipline '\n' > | grammar error '\n' { file->errors++; } > @@ -1039,6 +1056,11 @@ ikeauth : /* empty */ { > $$.auth_eap = 0; > explicit_bzero(&$2, sizeof($2)); > } > + | EAP RADIUS { > + $$.auth_method = IKEV2_AUTH_SIG_ANY; > + $$.auth_eap = EAP_TYPE_RADIUS; > + $$.auth_length = 0; > + } > | EAP STRING { > unsigned int i; > > @@ -1046,7 +1068,11 @@ ikeauth : /* empty */ { > if ($2[i] == '-') > $2[i] = '_'; > > - if (strcasecmp("mschap_v2", $2) != 0) { > + if (strcasecmp("mschap_v2", $2) == 0) > + $$.auth_eap = EAP_TYPE_MSCHAP_V2; > + else if (strcasecmp("radius", $2) == 0) > + $$.auth_eap = EAP_TYPE_RADIUS; > + else { > yyerror("unsupported EAP method: %s", $2); > free($2); > YYERROR; > @@ -1054,7 +1080,6 @@ ikeauth : /* empty */ { > free($2); > > $$.auth_method = IKEV2_AUTH_SIG_ANY; > - $$.auth_eap = EAP_TYPE_MSCHAP_V2; > $$.auth_length = 0; > } > | STRING { > @@ -1245,6 +1270,202 @@ string : string STRING > | STRING > ; > > +radius : RADIUS accounting SERVER STRING port SECRET STRING > + { > + int ret, gai_err; > + struct addrinfo hints, *ai; > + u_short port; > + > + memset(&hints, 0, sizeof(hints)); > + hints.ai_family = PF_UNSPEC; > + hints.ai_socktype = SOCK_DGRAM; > + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; > + if ((gai_err = getaddrinfo($4, NULL, &hints, &ai)) > + != 0) { > + yyerror("could not parse the address: %s: %s", > + $4, gai_strerror(gai_err)); > + free($4); > + explicit_bzero($7, strlen($7)); > + free($7); > + YYERROR; > + } > + port = $5; > + if (port == 0) > + port = htons((!$2)? RADIUS_DEFAULT_PORT : > + RADIUS_ACCT_DEFAULT_PORT); > + socket_af(ai->ai_addr, port); > + if ((ret = config_setradserver(env, ai->ai_addr, > + ai->ai_addrlen, $7, $2)) != 0) { > + yyerror("could not set radius server"); > + free($4); > + explicit_bzero($7, strlen($7)); > + free($7); > + YYERROR; > + } > + explicit_bzero($7, strlen($7)); > + freeaddrinfo(ai); > + free($4); > + free($7); > + } > + | RADIUS accounting MAX_TRIES NUMBER { > + if ($4 <= 0) { > + yyerror("max-tries must a positive value"); > + YYERROR; > + } > + if ($2) > + radacct.max_tries = $4; > + else > + radauth.max_tries = $4; > + } > + | RADIUS accounting MAX_FAILOVERS NUMBER { > + if ($4 < 0) { > + yyerror("max-failovers must be 0 or a " > + "positive value"); > + YYERROR; > + } > + if ($2) > + radacct.max_failovers = $4; > + else > + radauth.max_failovers = $4; > + } > + | RADIUS CONFIG af STRING radattr { > + const struct ipsec_xf *xf; > + int af, cfgtype; > + > + af = $3; > + if (af == AF_UNSPEC) > + af = AF_INET; > + if (strcmp($4, "none") == 0) > + cfgtype = 0; > + else { > + if ((xf = parse_xf($4, af, cpxfs)) == NULL || > + xf->id == IKEV2_CFG_INTERNAL_IP4_SUBNET || > + xf->id == IKEV2_CFG_INTERNAL_IP6_SUBNET) { > + yyerror("not a valid ikecfg option"); > + free($4); > + YYERROR; > + } > + cfgtype = xf->id; > + } > + free($4); > + config_setradcfgmap(env, cfgtype, $5.vendorid, > + $5.attrtype); > + } > + | RADIUS DAE LISTEN ON STRING port { > + int ret, gai_err; > + struct addrinfo hints, *ai; > + u_short port; > + > + memset(&hints, 0, sizeof(hints)); > + hints.ai_family = PF_UNSPEC; > + hints.ai_socktype = SOCK_DGRAM; > + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; > + if ((gai_err = getaddrinfo($5, NULL, &hints, &ai)) > + != 0) { > + yyerror("could not parse the address: %s: %s", > + $5, gai_strerror(gai_err)); > + free($5); > + YYERROR; > + } > + port = $6; > + if (port == 0) > + port = htons(RADIUS_DAE_DEFAULT_PORT); > + socket_af(ai->ai_addr, port); > + if ((ret = config_setraddae(env, ai->ai_addr, > + ai->ai_addrlen)) != 0) { > + yyerror("could not set radius server"); > + free($5); > + YYERROR; > + } > + freeaddrinfo(ai); > + free($5); > + } > + | RADIUS DAE CLIENT STRING SECRET STRING { > + int gai_err; > + struct addrinfo hints, *ai; > + > + memset(&hints, 0, sizeof(hints)); > + hints.ai_family = PF_UNSPEC; > + hints.ai_socktype = SOCK_DGRAM; > + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; > + if ((gai_err = getaddrinfo($4, NULL, &hints, &ai)) > + != 0) { > + yyerror("could not parse the address: %s: %s", > + $4, gai_strerror(gai_err)); > + free($4); > + explicit_bzero($6, strlen($6)); > + free($6); > + YYERROR; > + } > + config_setradclient(env, ai->ai_addr, ai->ai_addrlen, > + $6); > + free($4); > + explicit_bzero($6, strlen($6)); > + free($6); > + freeaddrinfo(ai); > + } > + ; > + > +radattr : hexdecnumber hexdecnumber { > + if ($1 < 0 || 0xffffffL < $1) { > + yyerror("vendor-id must be in 0-0xffffff"); > + YYERROR; > + } > + if ($2 < 0 || 256 <= $2) { > + yyerror("attribute type must be in 0-255"); > + YYERROR; > + } > + $$.vendorid = $1; > + $$.attrtype = $2; > + } > + | hexdecnumber { > + if ($1 < 0 || 256 <= $1) { > + yyerror("attribute type must be in 0-255"); > + YYERROR; > + } > + $$.vendorid = 0; > + $$.attrtype = $1; > + } > + > +hexdecnumber : STRING { > + const char *errstr; > + char *ep; > + uintmax_t ul; > + > + if ($1[0] == '0' && $1[1] == 'x' && isxdigit($1[2])) { > + ul = strtoumax($1 + 2, &ep, 16); > + if (*ep != '\0') { > + yyerror("`%s' is not a number", $1); > + free($1); > + YYERROR; > + } > + if (ul == UINTMAX_MAX || ul > UINT64_MAX) { > + yyerror("`%s' is out-of-range", $1); > + free($1); > + YYERROR; > + } > + $$ = ul; > + } else { > + $$ = strtonum($1, 0, UINT64_MAX, &errstr); > + if (errstr != NULL) { > + yyerror("`%s' is %s", $1, errstr); > + free($1); > + YYERROR; > + } > + } > + free($1); > + } > + | NUMBER > + ; > + > +accounting : { > + $$ = 0; > + } > + | ACCOUNTING { > + $$ = 1; > + } > + ; > + > varset : STRING '=' string > { > char *s = $1; > @@ -1336,6 +1557,7 @@ lookup(char *s) > { > /* this has to be sorted always */ > static const struct keywords keywords[] = { > + { "accounting", ACCOUNTING }, > { "active", ACTIVE }, > { "ah", AH }, > { "any", ANY }, > @@ -1343,8 +1565,10 @@ lookup(char *s) > { "bytes", BYTES }, > { "cert_partial_chain", CERTPARTIALCHAIN }, > { "childsa", CHILDSA }, > + { "client", CLIENT }, > { "config", CONFIG }, > { "couple", COUPLE }, > + { "dae", DAE }, > { "decouple", DECOUPLE }, > { "default", DEFAULT }, > { "dpd_check_interval", DPD_CHECK_INTERVAL }, > @@ -1370,7 +1594,10 @@ lookup(char *s) > { "inet6", INET6 }, > { "ipcomp", IPCOMP }, > { "lifetime", LIFETIME }, > + { "listen", LISTEN }, > { "local", LOCAL }, > + { "max-failovers", MAX_FAILOVERS}, > + { "max-tries", MAX_TRIES }, > { "maxage", MAXAGE }, > { "mobike", MOBIKE }, > { "name", NAME }, > @@ -1381,6 +1608,7 @@ lookup(char *s) > { "nostickyaddress", NOSTICKYADDRESS }, > { "novendorid", NOVENDORID }, > { "ocsp", OCSP }, > + { "on", ON }, > { "passive", PASSIVE }, > { "peer", PEER }, > { "port", PORT }, > @@ -1388,9 +1616,12 @@ lookup(char *s) > { "proto", PROTO }, > { "psk", PSK }, > { "quick", QUICK }, > + { "radius", RADIUS }, > { "rdomain", RDOMAIN }, > { "request", REQUEST }, > { "sa", SA }, > + { "secret", SECRET }, > + { "server", SERVER }, > { "set", SET }, > { "skip", SKIP }, > { "srcid", SRCID }, > @@ -1792,6 +2023,10 @@ parse_config(const char *filename, struc > dpd_interval = IKED_IKE_SA_ALIVE_TIMEOUT; > decouple = passive = 0; > ocsp_url = NULL; > + radauth.max_tries = 3; > + radauth.max_failovers = 0; > + radacct.max_tries = 3; > + radacct.max_failovers = 0; > > if (env->sc_opts & IKED_OPT_PASSIVE) > passive = 1; > @@ -1812,6 +2047,8 @@ parse_config(const char *filename, struc > env->sc_ocsp_maxage = ocsp_maxage; > env->sc_cert_partial_chain = cert_partial_chain; > env->sc_vendorid = vendorid; > + env->sc_radauth = radauth; > + env->sc_radacct = radacct; > > if (!rules) > log_warnx("%s: no valid configuration rules found", > Index: sbin/iked/pfkey.c > =================================================================== > RCS file: /cvs/src/sbin/iked/pfkey.c,v > diff -u -p -u -p -r1.84 pfkey.c > --- sbin/iked/pfkey.c 14 Aug 2023 12:02:02 -0000 1.84 > +++ sbin/iked/pfkey.c 12 Jul 2024 15:22:46 -0000 > @@ -111,8 +111,11 @@ int pfkey_write(struct iked *, struct sa > uint8_t **, ssize_t *); > int pfkey_reply(int, uint8_t **, ssize_t *); > void pfkey_dispatch(int, short, void *); > -int pfkey_sa_lookup(struct iked *, struct iked_childsa *, uint64_t *); > +int pfkey_sa_lookup(struct iked *, struct iked_childsa *, uint64_t *, > + struct iked_sastats *); > int pfkey_sa_check_exists(struct iked *, struct iked_childsa *); > +int pfkey_sa_sastats(struct iked *, struct iked_childsa *, > + struct iked_sastats *); > > struct sadb_ident * > pfkey_id2ident(struct iked_id *, unsigned int); > @@ -872,7 +875,8 @@ pfkey_sa(struct iked *env, uint8_t satyp > } > > int > -pfkey_sa_lookup(struct iked *env, struct iked_childsa *sa, uint64_t *last_used) > +pfkey_sa_lookup(struct iked *env, struct iked_childsa *sa, uint64_t *last_used, > + struct iked_sastats *stats) > { > struct iked_policy *pol = sa->csa_ikesa->sa_policy; > struct sadb_msg *msg, smsg; > @@ -880,6 +884,7 @@ pfkey_sa_lookup(struct iked *env, struct > struct sadb_sa sadb; > struct sadb_x_rdomain sa_rdomain; > struct sadb_lifetime *sa_life; > + struct sadb_x_counter *sa_counter; > struct sockaddr_storage ssrc, sdst; > struct iovec iov[IOV_CNT]; > uint64_t pad = 0; > @@ -1012,6 +1017,20 @@ pfkey_sa_lookup(struct iked *env, struct > *last_used = sa_life->sadb_lifetime_usetime; > log_debug("%s: last_used %llu", __func__, *last_used); > } > + if (stats) { > + if ((sa_counter = pfkey_find_ext(data, n, > + SADB_X_EXT_COUNTER)) == NULL) { > + /* has never been used */ > + ret = -1; > + goto done; > + } > + stats->sas_ibytes = sa_counter->sadb_x_counter_ibytes; > + stats->sas_obytes = sa_counter->sadb_x_counter_obytes; > + stats->sas_ipackets = sa_counter->sadb_x_counter_ipackets; > + stats->sas_opackets = sa_counter->sadb_x_counter_opackets; > + stats->sas_idrops = sa_counter->sadb_x_counter_idrops; > + stats->sas_odrops = sa_counter->sadb_x_counter_odrops; > + } > > #undef PAD > done: > @@ -1022,13 +1041,20 @@ done: > int > pfkey_sa_last_used(struct iked *env, struct iked_childsa *sa, uint64_t *last_used) > { > - return pfkey_sa_lookup(env, sa, last_used); > + return pfkey_sa_lookup(env, sa, last_used, NULL); > } > > int > pfkey_sa_check_exists(struct iked *env, struct iked_childsa *sa) > { > - return pfkey_sa_lookup(env, sa, NULL); > + return pfkey_sa_lookup(env, sa, NULL, NULL); > +} > + > +int > +pfkey_sa_sastats(struct iked *env, struct iked_childsa *sa, > + struct iked_sastats *stats) > +{ > + return pfkey_sa_lookup(env, sa, NULL, stats); > } > > int > @@ -1582,7 +1608,8 @@ pfkey_sa_update_addresses(struct iked *e > int > pfkey_sa_delete(struct iked *env, struct iked_childsa *sa) > { > - uint8_t satype; > + uint8_t satype; > + struct iked_sastats sas; > > if (!sa->csa_loaded || sa->csa_spi.spi == 0) > return (0); > @@ -1590,11 +1617,23 @@ pfkey_sa_delete(struct iked *env, struct > if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) > return (-1); > > + /* preserve the statistics */ > + memset(&sas, 0, sizeof(sas)); > + pfkey_sa_sastats(env, sa, &sas); > + > if (pfkey_sa(env, satype, SADB_DELETE, sa) == -1 && > pfkey_sa_check_exists(env, sa) == 0) > return (-1); > > sa->csa_loaded = 0; > + > + sa->csa_ikesa->sa_stats.sas_ipackets += sas.sas_ipackets; > + sa->csa_ikesa->sa_stats.sas_opackets += sas.sas_opackets; > + sa->csa_ikesa->sa_stats.sas_ibytes += sas.sas_ibytes; > + sa->csa_ikesa->sa_stats.sas_obytes += sas.sas_obytes; > + sa->csa_ikesa->sa_stats.sas_idrops += sas.sas_idrops; > + sa->csa_ikesa->sa_stats.sas_odrops += sas.sas_odrops; > + > return (0); > } > > Index: sbin/iked/policy.c > =================================================================== > RCS file: /cvs/src/sbin/iked/policy.c,v > diff -u -p -u -p -r1.98 policy.c > --- sbin/iked/policy.c 3 Feb 2024 00:54:14 -0000 1.98 > +++ sbin/iked/policy.c 12 Jul 2024 15:22:46 -0000 > @@ -60,6 +60,11 @@ policy_init(struct iked *env) > { > TAILQ_INIT(&env->sc_policies); > TAILQ_INIT(&env->sc_ocsp); > + TAILQ_INIT(&env->sc_radauthservers); > + TAILQ_INIT(&env->sc_radacctservers); > + TAILQ_INIT(&env->sc_radcfgmaps); > + TAILQ_INIT(&env->sc_raddaes); > + TAILQ_INIT(&env->sc_raddaeclients); > RB_INIT(&env->sc_users); > RB_INIT(&env->sc_sas); > RB_INIT(&env->sc_dstid_sas); > Index: sbin/iked/radius.c > =================================================================== > RCS file: sbin/iked/radius.c > diff -N sbin/iked/radius.c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ sbin/iked/radius.c 12 Jul 2024 15:22:46 -0000 > @@ -0,0 +1,937 @@ > +/* $OpenBSD$ */ > + > +/* > + * Copyright (c) 2024 Internet Initiative Japan Inc. > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "iked.h" > +#include "eap.h" > +#include "ikev2.h" > +#include "types.h" > + > +void iked_radius_request_send(struct iked *, void *); > +void iked_radius_fill_attributes(struct iked_sa *, RADIUS_PACKET *); > +void iked_radius_config(struct iked_radserver_req *, const RADIUS_PACKET *, > + int, uint32_t, uint8_t); > +void iked_radius_acct_request(struct iked *, struct iked_sa *, uint8_t); > + > +const struct iked_radcfgmap radius_cfgmaps[] = { > + { IKEV2_CFG_INTERNAL_IP4_ADDRESS, 0, RADIUS_TYPE_FRAMED_IP_ADDRESS }, > + { IKEV2_CFG_INTERNAL_IP4_NETMASK, 0, RADIUS_TYPE_FRAMED_IP_NETMASK }, > + { IKEV2_CFG_INTERNAL_IP4_DNS, RADIUS_VENDOR_MICROSOFT, > + RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER }, > + { IKEV2_CFG_INTERNAL_IP4_DNS, RADIUS_VENDOR_MICROSOFT, > + RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER }, > + { IKEV2_CFG_INTERNAL_IP4_NBNS, RADIUS_VENDOR_MICROSOFT, > + RADIUS_VTYPE_MS_PRIMARY_NBNS_SERVER }, > + { IKEV2_CFG_INTERNAL_IP4_NBNS, RADIUS_VENDOR_MICROSOFT, > + RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER }, > + { 0 } > +}; > + > +int > +iked_radius_request(struct iked *env, struct iked_sa *sa, > + struct iked_message *msg) > +{ > + struct eap_message *eap; > + RADIUS_PACKET *pkt; > + size_t len; > + > + eap = ibuf_data(msg->msg_eapmsg); > + len = betoh16(eap->eap_length); > + if (eap->eap_code != EAP_CODE_RESPONSE) { > + log_debug("%s: eap_code is not response %u", __func__, > + (unsigned)eap->eap_code); > + return -1; > + } > + > + if (eap->eap_type == EAP_TYPE_IDENTITY) { > + if ((sa->sa_radreq = calloc(1, > + sizeof(struct iked_radserver_req))) == NULL) { > + log_debug( > + "%s: calloc failed for iked_radserver_req: %s", > + __func__, strerror(errno)); > + return (-1); > + } > + timer_set(env, &sa->sa_radreq->rr_timer, > + iked_radius_request_send, sa->sa_radreq); > + sa->sa_radreq->rr_user = strdup(msg->msg_eap.eam_identity); > + } > + > + if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) > + == NULL) { > + log_debug("%s: radius_new_request_packet failed %s", __func__, > + strerror(errno)); > + return -1; > + } > + > + radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, > + sa->sa_radreq->rr_user); > + if (sa->sa_radreq->rr_state != NULL) > + radius_put_raw_attr(pkt, RADIUS_TYPE_STATE, > + ibuf_data(sa->sa_radreq->rr_state), > + ibuf_size(sa->sa_radreq->rr_state)); > + > + if (radius_put_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, > + (uint8_t *)eap, len) == -1) { > + log_debug("%s: radius_put_raw_attr_cat failed %s", __func__, > + strerror(errno)); > + return -1; > + } > + > + iked_radius_fill_attributes(sa, pkt); > + > + /* save the request, it'll be needed for message authentication */ > + if (sa->sa_radreq->rr_reqpkt != NULL) > + radius_delete_packet(sa->sa_radreq->rr_reqpkt); > + sa->sa_radreq->rr_reqpkt = pkt; > + sa->sa_radreq->rr_sa = sa; > + sa->sa_radreq->rr_ntry = 0; > + > + iked_radius_request_send(env, sa->sa_radreq); > + > + return 0; > +} > + > +void > +iked_radius_request_free(struct iked *env, struct iked_radserver_req *req) > +{ > + if (req == NULL) > + return; > + timer_del(env, &req->rr_timer); > + free(req->rr_user); > + ibuf_free(req->rr_state); > + if (req->rr_reqpkt) > + radius_delete_packet(req->rr_reqpkt); > + if (req->rr_sa) > + req->rr_sa->sa_radreq = NULL; > + if (req->rr_server) > + TAILQ_REMOVE(&req->rr_server->rs_reqs, req, rr_entry); > + free(req); > +} > + > +void > +iked_radius_on_event(int fd, short ev, void *ctx) > +{ > + struct iked *env; > + struct iked_radserver *server = ctx; > + struct iked_radserver_req *req; > + const struct iked_radcfgmap *cfgmap; > + RADIUS_PACKET *pkt; > + int i, resid; > + struct ibuf *e; > + const void *attrval; > + size_t attrlen; > + uint8_t code; > + char username[256]; > + u_char eapmsk[128]; > + /* RFC 3748 defines the MSK minimum size is 64 bytes */ > + size_t eapmsksiz = sizeof(eapmsk); > + > + env = server->rs_env; > + pkt = radius_recv(server->rs_sock, 0); > + if (pkt == NULL) { > + log_info("%s: receiving a RADIUS message failed: %s", __func__, > + strerror(errno)); > + return; > + } > + resid = radius_get_id(pkt); > + > + TAILQ_FOREACH(req, &server->rs_reqs, rr_entry) { > + if (req->rr_reqid == resid) > + break; > + } > + if (req == NULL) { > + log_debug("%s: received an unknown RADIUS message: id=%u", > + __func__, (unsigned)resid); > + return; > + } > + > + radius_set_request_packet(pkt, req->rr_reqpkt); > + if (radius_check_response_authenticator(pkt, server->rs_secret) != 0) { > + log_info("%s: received an invalid RADIUS message: bad " > + "response authenticator", __func__); > + return; > + } > + if (req->rr_accounting) { > + /* accounting */ > + code = radius_get_code(pkt); > + switch (code) { > + case RADIUS_CODE_ACCOUNTING_RESPONSE: /* Expected */ > + break; > + default: > + log_info("%s: received an invalid RADIUS message: " > + "code %u", __func__, (unsigned)code); > + } > + timer_del(env, &req->rr_timer); > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + free(req); > + return; > + } > + > + /* authentication */ > + if (radius_check_message_authenticator(pkt, server->rs_secret) != 0) { > + log_info("%s: received an invalid RADIUS message: bad " > + "message authenticator", __func__); > + return; > + } > + > + timer_del(env, &req->rr_timer); > + req->rr_ntry = 0; > + > + if (req->rr_sa == NULL) > + goto fail; > + > + code = radius_get_code(pkt); > + switch (code) { > + case RADIUS_CODE_ACCESS_CHALLENGE: > + if (radius_get_raw_attr_ptr(pkt, RADIUS_TYPE_STATE, &attrval, > + &attrlen) != 0) { > + log_info("%s: received an invalid RADIUS message: no " > + "state attribute", __func__); > + goto fail; > + } > + if ((req->rr_state != NULL && > + ibuf_set(req->rr_state, 0, attrval, attrlen) != 0) || > + (req->rr_state = ibuf_new(attrval, attrlen)) == NULL) { > + log_info("%s: ibuf_new() failed: %s", __func__, > + strerror(errno)); > + goto fail; > + } > + break; > + case RADIUS_CODE_ACCESS_ACCEPT: > + log_info("%s: received Access-Accept for %s", > + SPI_SA(req->rr_sa, __func__), req->rr_user); > + /* Try to retrieve the EAP MSK from the RADIUS response */ > + if (radius_get_eap_msk(pkt, eapmsk, &eapmsksiz, > + server->rs_secret) == 0) { > + ibuf_free(req->rr_sa->sa_eapmsk); > + if ((req->rr_sa->sa_eapmsk = ibuf_new(eapmsk, > + eapmsksiz)) == NULL) { > + log_info("%s: ibuf_new() failed: %s", __func__, > + strerror(errno)); > + goto fail; > + } > + } else > + log_debug("Could not retrieve the EAP MSK from the " > + "RADIUS message"); > + > + free(req->rr_sa->sa_eapid); > + /* The EAP identity might be protected (RFC 3748 7.3) */ > + if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, > + username, sizeof(username)) == 0 && > + strcmp(username, req->rr_user) != 0) { > + /* > + * The Access-Accept might have a User-Name. It > + * should be used for Accouting (RFC 2865 5.1). > + */ > + free(req->rr_user); > + req->rr_sa->sa_eapid = strdup(username); > + } else > + req->rr_sa->sa_eapid = req->rr_user; > + req->rr_user = NULL; > + > + sa_state(env, req->rr_sa, IKEV2_STATE_AUTH_SUCCESS); > + > + /* Map RADIUS attributes to cp */ > + if (TAILQ_EMPTY(&env->sc_radcfgmaps)) { > + for (i = 0; radius_cfgmaps[i].cfg_type != 0; i++) { > + cfgmap = &radius_cfgmaps[i]; > + iked_radius_config(req, pkt, cfgmap->cfg_type, > + cfgmap->vendor_id, cfgmap->attr_type); > + } > + } else { > + TAILQ_FOREACH(cfgmap, &env->sc_radcfgmaps, entry) > + iked_radius_config(req, pkt, cfgmap->cfg_type, > + cfgmap->vendor_id, cfgmap->attr_type); > + } > + > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + break; > + case RADIUS_CODE_ACCESS_REJECT: > + log_info("%s: received Access-Reject for %s", > + SPI_SA(req->rr_sa, __func__), req->rr_user); > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + break; > + default: > + log_debug("%s: received an invalid RADIUS message: code %u", > + __func__, (unsigned)code); > + break; > + } > + > + /* get the length first */ > + if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, NULL, > + &attrlen) != 0) { > + log_info("%s: failed to retrieve the EAP message", __func__); > + goto fail; > + } > + /* allocate a buffer */ > + if ((e = ibuf_new(NULL, attrlen)) == NULL) { > + log_info("%s: ibuf_new() failed: %s", __func__, > + strerror(errno)); > + goto fail; > + } > + /* copy the message to the buffer */ > + if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, > + ibuf_data(e), &attrlen) != 0) { > + ibuf_free(e); > + log_info("%s: failed to retrieve the EAP message", __func__); > + goto fail; > + } > + ikev2_send_ike_e(env, req->rr_sa, e, IKEV2_PAYLOAD_EAP, > + IKEV2_EXCHANGE_IKE_AUTH, 1); > + return; > + fail: > + if (req->rr_server != NULL) > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + if (req->rr_sa != NULL) { > + ikev2_ike_sa_setreason(req->rr_sa, "RADIUS request failed"); > + sa_free(env, req->rr_sa); > + } > +} > + > +void > +iked_radius_request_send(struct iked *env, void *ctx) > +{ > + struct iked_radserver_req *req = ctx, *req0; > + struct iked_radserver *server = req->rr_server; > + const int timeouts[] = { 2, 4, 8 }; > + uint8_t seq; > + int i, max_tries, max_failovers; > + struct sockaddr_storage ss; > + socklen_t sslen; > + struct iked_radservers *radservers; > + struct timespec now; > + > + if (!req->rr_accounting) { > + max_tries = env->sc_radauth.max_tries; > + max_failovers = env->sc_radauth.max_failovers; > + radservers = &env->sc_radauthservers; > + } else { > + max_tries = env->sc_radacct.max_tries; > + max_failovers = env->sc_radacct.max_failovers; > + radservers = &env->sc_radacctservers; > + } > + > + if (req->rr_ntry > max_tries) { > + req->rr_ntry = 0; > + log_info("%s: RADIUS server %s failed", __func__, > + print_addr(&server->rs_sockaddr)); > + next_server: > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + if (req->rr_nfailover >= max_failovers || > + TAILQ_NEXT(server, rs_entry) == NULL) { > + log_info("%s: No more RADIUS server", __func__); > + goto fail; > + } else if (req->rr_state != NULL) { > + log_info("%s: Can't change RADIUS server: " > + "client has a state already", __func__); > + goto fail; > + } else { > + TAILQ_REMOVE(radservers, server, rs_entry); > + TAILQ_INSERT_TAIL(radservers, server, rs_entry); > + server = TAILQ_FIRST(radservers); > + log_info("%s: RADIUS server %s is active", > + __func__, print_addr(&server->rs_sockaddr)); > + } > + req->rr_nfailover++; > + } > + > + if (req->rr_server != NULL && > + req->rr_server != TAILQ_FIRST(radservers)) { > + /* Current server is marked fail */ > + if (req->rr_state != NULL || req->rr_nfailover >= max_failovers) > + goto fail; /* can't fail over */ > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + req->rr_nfailover++; > + } > + > + if (req->rr_server == NULL) { > + /* Select a new server */ > + server = TAILQ_FIRST(radservers); > + if (server == NULL) { > + log_info("%s: No RADIUS server is configured", > + __func__); > + goto fail; > + } > + TAILQ_INSERT_TAIL(&server->rs_reqs, req, rr_entry); > + req->rr_server = server; > + > + /* Prepare NAS-IP-Address */ > + if (server->rs_nas_ipv4.s_addr == INADDR_ANY && > + IN6_IS_ADDR_UNSPECIFIED(&server->rs_nas_ipv6)) { > + sslen = sizeof(ss); > + if (getsockname(server->rs_sock, (struct sockaddr *)&ss, > + &sslen) == 0) { > + if (ss.ss_family == AF_INET) > + server->rs_nas_ipv4 = > + ((struct sockaddr_in *)&ss) > + ->sin_addr; > + else > + server->rs_nas_ipv6 = > + ((struct sockaddr_in6 *)&ss) > + ->sin6_addr; > + } > + } > + } > + if (req->rr_ntry == 0) { > + /* decide the ID */ > + seq = ++server->rs_reqseq; > + for (i = 0; i < UCHAR_MAX; i++) { > + TAILQ_FOREACH(req0, &server->rs_reqs, rr_entry) { > + if (req0->rr_reqid == seq) > + break; > + } > + if (req0 == NULL) > + break; > + seq++; > + } > + if (i >= UCHAR_MAX) { > + log_info("%s: RADIUS server %s failed. Too many " > + "pending requests", __func__, > + print_addr(&server->rs_sockaddr)); > + if (TAILQ_NEXT(server, rs_entry) != NULL) > + goto next_server; > + goto fail; > + } > + req->rr_reqid = seq; > + radius_set_id(req->rr_reqpkt, req->rr_reqid); > + } > + > + if (server->rs_nas_ipv4.s_addr != INADDR_ANY) > + radius_put_ipv4_attr(req->rr_reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS, > + server->rs_nas_ipv4); > + else if (!IN6_IS_ADDR_UNSPECIFIED(&server->rs_nas_ipv6)) > + radius_put_ipv6_attr(req->rr_reqpkt, > + RADIUS_TYPE_NAS_IPV6_ADDRESS, &server->rs_nas_ipv6); > + /* Identifier */ > + radius_put_string_attr(req->rr_reqpkt, RADIUS_TYPE_NAS_IDENTIFIER, > + IKED_NAS_ID); > + > + if (req->rr_accounting) { > + if (req->rr_ntry == 0 && req->rr_nfailover == 0) > + radius_put_uint32_attr(req->rr_reqpkt, > + RADIUS_TYPE_ACCT_DELAY_TIME, 0); > + else { > + clock_gettime(CLOCK_MONOTONIC, &now); > + timespecsub(&now, &req->rr_accttime, &now); > + radius_put_uint32_attr(req->rr_reqpkt, > + RADIUS_TYPE_ACCT_DELAY_TIME, now.tv_sec); > + } > + radius_set_accounting_request_authenticator(req->rr_reqpkt, > + server->rs_secret); > + } else { > + radius_put_message_authenticator(req->rr_reqpkt, > + server->rs_secret); > + } > + > + if (radius_send(server->rs_sock, req->rr_reqpkt, 0) < 0) > + log_info("%s: sending a RADIUS message failed: %s", __func__, > + strerror(errno)); > + > + if (req->rr_ntry >= (int)nitems(timeouts)) > + timer_add(env, &req->rr_timer, timeouts[nitems(timeouts) - 1]); > + else > + timer_add(env, &req->rr_timer, timeouts[req->rr_ntry]); > + req->rr_ntry++; > + return; > + fail: > + if (req->rr_server != NULL) > + TAILQ_REMOVE(&server->rs_reqs, req, rr_entry); > + req->rr_server = NULL; > + if (req->rr_sa != NULL) { > + ikev2_ike_sa_setreason(req->rr_sa, "RADIUS request failed"); > + sa_free(env, req->rr_sa); > + } > +} > + > +void > +iked_radius_fill_attributes(struct iked_sa *sa, RADIUS_PACKET *pkt) > +{ > + /* NAS Port Type = Virtual */ > + radius_put_uint32_attr(pkt, > + RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL); > + /* Service Type = Framed */ > + radius_put_uint32_attr(pkt, RADIUS_TYPE_SERVICE_TYPE, > + RADIUS_SERVICE_TYPE_FRAMED); > + /* Tunnel Type = EAP */ > + radius_put_uint32_attr(pkt, RADIUS_TYPE_TUNNEL_TYPE, > + RADIUS_TUNNEL_TYPE_ESP); > + > + radius_put_string_attr(pkt, RADIUS_TYPE_CALLED_STATION_ID, > + print_addr(&sa->sa_local.addr)); > + radius_put_string_attr(pkt, RADIUS_TYPE_CALLING_STATION_ID, > + print_addr(&sa->sa_peer.addr)); > +} > + > +void > +iked_radius_config(struct iked_radserver_req *req, const RADIUS_PACKET *pkt, > + int cfg_type, uint32_t vendor_id, uint8_t attr_type) > +{ > + unsigned int i; > + struct iked_sa *sa = req->rr_sa; > + struct in_addr ia4; > + struct in6_addr ia6; > + struct sockaddr_in *sin4; > + struct sockaddr_in6 *sin6; > + struct iked_addr *addr; > + struct iked_cfg *ikecfg; > + > + for (i = 0; i < sa->sa_policy->pol_ncfg; i++) { > + ikecfg = &sa->sa_policy->pol_cfg[i]; > + if (ikecfg->cfg_type == cfg_type && > + ikecfg->cfg_type != IKEV2_CFG_INTERNAL_IP4_ADDRESS) > + return; /* use config rather than radius */ > + } > + switch (cfg_type) { > + case IKEV2_CFG_INTERNAL_IP4_ADDRESS: > + case IKEV2_CFG_INTERNAL_IP4_NETMASK: > + case IKEV2_CFG_INTERNAL_IP4_DNS: > + case IKEV2_CFG_INTERNAL_IP4_NBNS: > + case IKEV2_CFG_INTERNAL_IP4_DHCP: > + case IKEV2_CFG_INTERNAL_IP4_SERVER: > + if (vendor_id == 0 && radius_has_attr(pkt, attr_type)) > + radius_get_ipv4_attr(pkt, attr_type, &ia4); > + else if (vendor_id != 0 && radius_has_vs_attr(pkt, vendor_id, > + attr_type)) > + radius_get_vs_ipv4_attr(pkt, vendor_id, attr_type, > + &ia4); > + else > + break; /* no attribute contained */ > + > + if (cfg_type == IKEV2_CFG_INTERNAL_IP4_NETMASK) { > + /* > + * This assumes IKEV2_CFG_INTERNAL_IP4_ADDRESS is > + * called before IKEV2_CFG_INTERNAL_IP4_NETMASK > + */ > + if (sa->sa_rad_addr == NULL) { > + /* > + * RFC 7296, IKEV2_CFG_INTERNAL_IP4_NETMASK > + * must be used with > + * IKEV2_CFG_INTERNAL_IP4_ADDRESS > + */ > + break; > + } > + if (ia4.s_addr == 0) { > + log_debug("%s: netmask is wrong", __func__); > + break; > + } > + if (ia4.s_addr == htonl(0)) > + sa->sa_rad_addr->addr_mask = 0; > + else > + sa->sa_rad_addr->addr_mask = > + 33 - ffs(ntohl(ia4.s_addr)); > + if (sa->sa_rad_addr->addr_mask < 32) > + sa->sa_rad_addr->addr_net = 1; > + } > + if (cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) { > + if ((addr = calloc(1, sizeof(*addr))) == NULL) { > + log_warn("%s: calloc", __func__); > + return; > + } > + sa->sa_rad_addr = addr; > + } else { > + req->rr_cfg[req->rr_ncfg].cfg_action = IKEV2_CP_REPLY; > + req->rr_cfg[req->rr_ncfg].cfg_type = cfg_type; > + addr = &req->rr_cfg[req->rr_ncfg].cfg.address; > + req->rr_ncfg++; > + } > + addr->addr_af = AF_INET; > + sin4 = (struct sockaddr_in *)&addr->addr; > + sin4->sin_family = AF_INET; > + sin4->sin_len = sizeof(struct sockaddr_in); > + sin4->sin_addr = ia4; > + break; > + case IKEV2_CFG_INTERNAL_IP6_ADDRESS: > + case IKEV2_CFG_INTERNAL_IP6_DNS: > + case IKEV2_CFG_INTERNAL_IP6_NBNS: > + case IKEV2_CFG_INTERNAL_IP6_DHCP: > + case IKEV2_CFG_INTERNAL_IP6_SERVER: > + if (vendor_id == 0 && radius_has_attr(pkt, attr_type)) > + radius_get_ipv6_attr(pkt, attr_type, &ia6); > + else if (vendor_id != 0 && radius_has_vs_attr(pkt, vendor_id, > + attr_type)) > + radius_get_vs_ipv6_attr(pkt, vendor_id, attr_type, > + &ia6); > + else > + break; /* no attribute contained */ > + > + if (cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS) { > + if ((addr = calloc(1, sizeof(*addr))) == NULL) { > + log_warn("%s: calloc", __func__); > + return; > + } > + sa->sa_rad_addr = addr; > + } else { > + req->rr_cfg[req->rr_ncfg].cfg_action = IKEV2_CP_REPLY; > + req->rr_cfg[req->rr_ncfg].cfg_type = cfg_type; > + addr = &req->rr_cfg[req->rr_ncfg].cfg.address; > + req->rr_ncfg++; > + } > + addr->addr_af = AF_INET; > + sin6 = (struct sockaddr_in6 *)&addr->addr; > + sin6->sin6_family = AF_INET6; > + sin6->sin6_len = sizeof(struct sockaddr_in6); > + sin6->sin6_addr = ia6; > + break; > + } > + return; > +} > + > +void > +iked_radius_acct_on(struct iked *env) > +{ > + if (TAILQ_EMPTY(&env->sc_radacctservers)) > + return; > + if (env->sc_radaccton == 0) { /* trigger once */ > + iked_radius_acct_request(env, NULL, > + RADIUS_ACCT_STATUS_TYPE_ACCT_ON); > + env->sc_radaccton = 1; > + } > +} > + > +void > +iked_radius_acct_off(struct iked *env) > +{ > + iked_radius_acct_request(env, NULL, RADIUS_ACCT_STATUS_TYPE_ACCT_OFF); > +} > + > +void > +iked_radius_acct_start(struct iked *env, struct iked_sa *sa) > +{ > + iked_radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_START); > +} > + > +void > +iked_radius_acct_stop(struct iked *env, struct iked_sa *sa) > +{ > + iked_radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_STOP); > +} > + > +void > +iked_radius_acct_request(struct iked *env, struct iked_sa *sa, uint8_t stype) > +{ > + struct iked_radserver_req *req; > + RADIUS_PACKET *pkt; > + struct iked_addr *addr4 = NULL; > + struct iked_addr *addr6 = NULL; > + struct in_addr mask4; > + char sa_id[IKED_ID_SIZE]; > + char sid[16 + 1]; > + struct timespec now; > + int cause; > + > + if (TAILQ_EMPTY(&env->sc_radacctservers)) > + return; > + /* > + * In RFC2866 5.6, "Users who are delivered service without > + * being authenticated SHOULD NOT generate Accounting records > + */ > + if (sa != NULL && sa->sa_eapid == NULL) { > + /* fallback to IKEID for accounting */ > + if (ikev2_print_id(IKESA_DSTID(sa), sa_id, sizeof(sa_id)) != -1) > + sa->sa_eapid = strdup(sa_id); > + if (sa->sa_eapid == NULL) > + return; > + } > + > + if ((req = calloc(1, sizeof(struct iked_radserver_req))) == NULL) { > + log_debug("%s: calloc faile for iked_radserver_req: %s", > + __func__, strerror(errno)); > + return; > + } > + req->rr_accounting = 1; > + clock_gettime(CLOCK_MONOTONIC, &now); > + req->rr_accttime = now; > + timer_set(env, &req->rr_timer, iked_radius_request_send, req); > + > + if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCOUNTING_REQUEST)) > + == NULL) { > + log_debug("%s: radius_new_request_packet failed %s", __func__, > + strerror(errno)); > + return; > + } > + > + /* RFC 2866 5.1. Acct-Status-Type */ > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_STATUS_TYPE, stype); > + > + if (sa == NULL) { > + /* ASSERT(stype == RADIUS_ACCT_STATUS_TYPE_ACCT_ON || > + stype == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) */ > + req->rr_reqpkt = pkt; > + req->rr_ntry = 0; > + iked_radius_request_send(env, req); > + return; > + } > + > + iked_radius_fill_attributes(sa, pkt); > + > + radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, sa->sa_eapid); > + > + /* RFC 2866 5.5. Acct-Session-Id */ > + snprintf(sid, sizeof(sid), "%016llx", > + (unsigned long long)sa->sa_hdr.sh_ispi); > + radius_put_string_attr(pkt, RADIUS_TYPE_ACCT_SESSION_ID, sid); > + > + /* Accounting Request must have Framed-IP-Address */ > + addr4 = sa->sa_addrpool; > + if (addr4 != NULL) { > + radius_put_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_ADDRESS, > + ((struct sockaddr_in *)&addr4->addr)->sin_addr); > + if (addr4->addr_mask != 0) { > + mask4.s_addr = htonl( > + 0xFFFFFFFFUL << (32 - addr4->addr_mask)); > + radius_put_ipv4_attr(pkt, > + RADIUS_TYPE_FRAMED_IP_NETMASK, mask4); > + } > + } > + addr6 = sa->sa_addrpool6; > + if (addr6 != NULL) > + radius_put_ipv6_attr(pkt, RADIUS_TYPE_FRAMED_IPV6_ADDRESS, > + &((struct sockaddr_in6 *)&addr6->addr)->sin6_addr); > + > + /* RFC2866 5.6 Acct-Authentic */ > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_AUTHENTIC, > + (sa->sa_radreq != NULL)? RADIUS_ACCT_AUTHENTIC_RADIUS : > + RADIUS_ACCT_AUTHENTIC_LOCAL); > + > + switch (stype) { > + case RADIUS_ACCT_STATUS_TYPE_START: > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_STATUS_TYPE, > + RADIUS_ACCT_STATUS_TYPE_START); > + break; > + case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE: > + case RADIUS_ACCT_STATUS_TYPE_STOP: > + /* RFC 2866 5.7. Acct-Session-Time */ > + timespecsub(&now, &sa->sa_starttime, &now); > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_SESSION_TIME, > + now.tv_sec); > + /* RFC 2866 5.10 Acct-Terminate-Cause */ > + cause = RADIUS_TERMNATE_CAUSE_SERVICE_UNAVAIL; > + if (sa->sa_reason) { > + if (strcmp(sa->sa_reason, "received delete") == 0) { > + cause = RADIUS_TERMNATE_CAUSE_USER_REQUEST; > + } else if (strcmp(sa->sa_reason, "SA rekeyed") == 0) { > + cause = RADIUS_TERMNATE_CAUSE_SESSION_TIMEOUT; > + } else if (strncmp(sa->sa_reason, "retransmit", > + strlen("retransmit")) == 0) { > + cause = RADIUS_TERMNATE_CAUSE_LOST_SERVICE; > + } else if (strcmp(sa->sa_reason, > + "disconnect requested") == 0) { > + cause = RADIUS_TERMNATE_CAUSE_ADMIN_RESET; > + } > + } > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_TERMINATE_CAUSE, > + cause); > + /* I/O statistics {Input,Output}-{Packets,Octets,Gigawords} */ > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_PACKETS, > + sa->sa_stats.sas_ipackets); > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_PACKETS, > + sa->sa_stats.sas_opackets); > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_OCTETS, > + sa->sa_stats.sas_ibytes & 0xffffffffUL); > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_OCTETS, > + sa->sa_stats.sas_obytes & 0xffffffffUL); > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_INPUT_GIGAWORDS, > + sa->sa_stats.sas_ibytes >> 32); > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS, > + sa->sa_stats.sas_obytes >> 32); > + break; > + } > + req->rr_reqpkt = pkt; > + req->rr_ntry = 0; > + iked_radius_request_send(env, req); > +} > + > +void > +iked_radius_dae_on_event(int fd, short ev, void *ctx) > +{ > + struct iked_raddae *dae = ctx; > + struct iked *env = dae->rd_env; > + RADIUS_PACKET *req = NULL, *res = NULL; > + struct sockaddr_storage ss; > + socklen_t sslen; > + struct iked_radclient *client; > + struct iked_sa *sa = NULL; > + char attr[256], username[256]; > + char *endp, *reason, *nakcause = NULL; > + int code, n = 0; > + uint64_t ispi = 0; > + uint32_t u32, cause = 0; > + struct iked_addr *addr4 = NULL; > + > + reason = "disconnect requested"; > + > + sslen = sizeof(ss); > + req = radius_recvfrom(dae->rd_sock, 0, (struct sockaddr *)&ss, &sslen); > + if (req == NULL) { > + log_warn("%s: receiving a RADIUS message failed: %s", __func__, > + strerror(errno)); > + return; > + } > + TAILQ_FOREACH(client, &env->sc_raddaeclients, rc_entry) { > + if (sockaddr_cmp((struct sockaddr *)&client->rc_sockaddr, > + (struct sockaddr *)&ss, -1) == 0) > + break; > + } > + if (client == NULL) { > + log_warnx("%s: received RADIUS message from %s: " > + "unknown client", __func__, print_addr(&ss)); > + goto out; > + } > + > + if (radius_check_accounting_request_authenticator(req, > + client->rc_secret) != 0) { > + log_warnx("%s: received an invalid RADIUS message from %s: bad " > + "response authenticator", __func__, print_addr(&ss)); > + goto out; > + } > + > + if ((code = radius_get_code(req)) != RADIUS_CODE_DISCONNECT_REQUEST) { > + /* Code other than Disconnect-Request is not supported */ > + if (code == RADIUS_CODE_COA_REQUEST) { > + code = RADIUS_CODE_COA_NAK; > + cause = RADIUS_ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED; > + nakcause = "Coa-Request is not supprted"; > + goto send; > + } > + log_warnx("%s: received an invalid RADIUS message " > + "from %s: unknown code %d", __func__, > + print_addr(&ss), code); > + goto out; > + } > + > + log_info("received Disconnect-Request from %s", print_addr(&ss)); > + > + if (radius_get_string_attr(req, RADIUS_TYPE_NAS_IDENTIFIER, attr, > + sizeof(attr)) == 0 && strcmp(attr, IKED_NAS_ID) != 0) { > + cause = RADIUS_ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH; > + nakcause = "NAS-Identifier is not matched"; > + goto search_done; > + } > + > + /* prepare User-Name attribute */ > + memset(username, 0, sizeof(username)); > + radius_get_string_attr(req, RADIUS_TYPE_USER_NAME, username, > + sizeof(username)); > + > + if (radius_get_string_attr(req, RADIUS_TYPE_ACCT_SESSION_ID, attr, > + sizeof(attr)) == 0) { > + /* the client is to disconnect a session */ > + ispi = strtoull(attr, &endp, 16); > + if (attr[0] == '\0' || *endp != '\0' || errno == ERANGE || > + ispi == ULLONG_MAX) { > + cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE; > + nakcause = "Session-Id is wrong"; > + goto search_done; > + > + } > + RB_FOREACH(sa, iked_sas, &env->sc_sas) { > + if (sa->sa_hdr.sh_ispi == ispi) > + break; > + } > + if (sa == NULL) > + goto search_done; > + if (username[0] != '\0' && (sa->sa_eapid == NULL || > + strcmp(username, sa->sa_eapid) != 0)) { > + /* specified User-Name attribute is mismatched */ > + cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE; > + nakcause = "User-Name is not matched"; > + goto search_done; > + } > + ikev2_ike_sa_setreason(sa, reason); > + ikev2_ike_sa_delete(env, sa); > + n++; > + } else if (username[0] != '\0') { > + RB_FOREACH(sa, iked_sas, &env->sc_sas) { > + if (sa->sa_eapid != NULL && > + strcmp(sa->sa_eapid, username) == 0) { > + ikev2_ike_sa_setreason(sa, reason); > + ikev2_ike_sa_delete(env, sa); > + n++; > + } > + } > + } else if (radius_get_uint32_attr(req, RADIUS_TYPE_FRAMED_IP_ADDRESS, > + &u32) == 0) { > + addr4 = sa->sa_addrpool; > + if (addr4 != NULL) { > + RB_FOREACH(sa, iked_sas, &env->sc_sas) { > + if (u32 == ((struct sockaddr_in *)&addr4->addr) > + ->sin_addr.s_addr) { > + ikev2_ike_sa_setreason(sa, reason); > + ikev2_ike_sa_delete(env, sa); > + n++; > + } > + } > + } > + } > + search_done: > + if (n > 0) > + code = RADIUS_CODE_DISCONNECT_ACK; > + else { > + if (nakcause == NULL) > + nakcause = "session not found"; > + if (cause == 0) > + cause = RADIUS_ERROR_CAUSE_SESSION_NOT_FOUND; > + code = RADIUS_CODE_DISCONNECT_NAK; > + } > + send: > + res = radius_new_response_packet(code, req); > + if (res == NULL) { > + log_warn("%s: radius_new_response_packet", __func__); > + goto out; > + } > + if (cause != 0) > + radius_put_uint32_attr(res, RADIUS_TYPE_ERROR_CAUSE, cause); > + radius_set_response_authenticator(res, client->rc_secret); > + if (radius_sendto(dae->rd_sock, res, 0, (struct sockaddr *)&ss, sslen) > + == -1) > + log_warn("%s: sendto", __func__); > + log_info("send %s for %s%s%s", > + (code == RADIUS_CODE_DISCONNECT_ACK)? "Disconnect-ACK" : > + (code == RADIUS_CODE_DISCONNECT_NAK)? "Disconnect-NAK" : "CoA-NAK", > + print_addr(&ss), (nakcause)? ": " : "", (nakcause)? nakcause : ""); > + out: > + radius_delete_packet(req); > + if (res != NULL) > + radius_delete_packet(res); > +} > Index: sbin/iked/types.h > =================================================================== > RCS file: /cvs/src/sbin/iked/types.h,v > diff -u -p -u -p -r1.54 types.h > --- sbin/iked/types.h 15 Feb 2024 20:10:45 -0000 1.54 > +++ sbin/iked/types.h 12 Jul 2024 15:22:46 -0000 > @@ -42,6 +42,7 @@ > #define IKED_PUBKEY "local.pub" > > #define IKED_VENDOR_ID "OpenIKED-" > +#define IKED_NAS_ID "OpenIKED" > > #define IKED_OCSP_RESPCERT "ocsp/responder.crt" > > @@ -112,6 +113,12 @@ enum imsg_type { > IMSG_CFG_POLICY, > IMSG_CFG_FLOW, > IMSG_CFG_USER, > + IMSG_CFG_RADAUTH, > + IMSG_CFG_RADACCT, > + IMSG_CFG_RADSERVER, > + IMSG_CFG_RADCFGMAP, > + IMSG_CFG_RADDAE, > + IMSG_CFG_RADDAECLIENT, > IMSG_CERTREQ, > IMSG_CERT, > IMSG_CERTVALID, > @@ -150,6 +157,7 @@ enum flushmode { > RESET_POLICY, > RESET_SA, > RESET_USER, > + RESET_RADIUS, > }; > > #ifndef nitems >