From: Uwe Werler Subject: Re: iked: RADIUS support To: YASUOKA Masahiko Cc: tobhe@openbsd.org, tech@openbsd.org, markus@openbsd.org Date: Thu, 25 Jan 2024 17:34:39 +0100 Hi Masahiko, that's really cool. If I use the patched version locally at my laptop I can't use active mode anymore - I always get a config error and iked denies to start. Best regards Uwe On 25 Jan 18:50, YASUOKA Masahiko wrote: > Hello, > > 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? > > Index: sbin/iked/Makefile > =================================================================== > RCS file: /cvs/src/sbin/iked/Makefile,v > diff -u -p -r1.22 Makefile > --- sbin/iked/Makefile 28 May 2021 18:01:39 -0000 1.22 > +++ sbin/iked/Makefile 25 Jan 2024 09:30:49 -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 -r1.95 config.c > --- sbin/iked/config.c 17 Jan 2024 08:25:02 -0000 1.95 > +++ sbin/iked/config.c 25 Jan 2024 09:30:49 -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); > > + 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); > + radius_request_free(env, sa->sa_radreq); > + > free(sa); > } > > @@ -585,6 +591,35 @@ 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; > + > + 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) > + 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) > + 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); > + } > + } > + > return (0); > } > > @@ -1071,5 +1106,178 @@ 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->fd; > + 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, > + 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); > } > Index: sbin/iked/eap.c > =================================================================== > RCS file: /cvs/src/sbin/iked/eap.c,v > diff -u -p -r1.25 eap.c > --- sbin/iked/eap.c 18 Jul 2023 15:07:41 -0000 1.25 > +++ sbin/iked/eap.c 25 Jan 2024 09:30:49 -0000 > @@ -21,6 +21,7 @@ > #include > > #include > +#include > #include > > #include > Index: sbin/iked/eap.h > =================================================================== > RCS file: /cvs/src/sbin/iked/eap.h,v > diff -u -p -r1.6 eap.h > --- sbin/iked/eap.h 16 Sep 2020 21:37:35 -0000 1.6 > +++ sbin/iked/eap.h 25 Jan 2024 09:30:49 -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 -r1.67 iked.c > --- sbin/iked/iked.c 15 Jan 2024 15:29:00 -0000 1.67 > +++ sbin/iked/iked.c 25 Jan 2024 09:30:49 -0000 > @@ -301,6 +301,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); > > @@ -318,6 +320,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 -r1.95 iked.conf.5 > --- sbin/iked/iked.conf.5 22 Jul 2022 20:31:39 -0000 1.95 > +++ sbin/iked/iked.conf.5 25 Jan 2024 09:30:49 -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 > @@ -775,6 +782,102 @@ 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 disables 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 accouting server, > +specify optional > +.Ic accounting > +keyword. > +Optionally specify the port number, > +otherwise the default port number, > +1812 for authentication or > +1813 for accouting, > +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 accouting requests, > +specify optional > +.Ic accouting > +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 accouting requests, > +specify optional > +.Ic accouting > +keyword. > +The default value is 0. > .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 -r1.226 iked.h > --- sbin/iked/iked.h 24 Jan 2024 10:09:07 -0000 1.226 > +++ sbin/iked/iked.h 25 Jan 2024 09:30:50 -0000 > @@ -20,6 +20,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -220,8 +221,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]; > }; > > @@ -406,6 +407,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 */ > @@ -488,6 +498,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 */ > @@ -536,6 +547,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); > @@ -651,6 +667,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; > @@ -704,6 +721,55 @@ 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_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]; > }; > @@ -811,6 +877,11 @@ 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; > + struct iked_radservers sc_radauthservers; > + struct iked_radservers sc_radacctservers; > + struct iked_radcfgmaps sc_radcfgmaps; > > struct iked_stats sc_stats; > > @@ -941,6 +1012,15 @@ 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 *); > > /* policy.c */ > void policy_init(struct iked *); > @@ -1154,6 +1234,15 @@ 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 radius_request(struct iked *, struct iked_sa *, struct iked_message *); > +void radius_request_free(struct iked *, struct iked_radserver_req *); > +void radius_on_event(int, short, void *); > +void radius_acct_on(struct iked *); > +void radius_acct_off(struct iked *); > +void radius_acct_start(struct iked *, struct iked_sa *); > +void radius_acct_stop(struct iked *, struct iked_sa *); > > /* 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 -r1.383 ikev2.c > --- sbin/iked/ikev2.c 24 Jan 2024 10:09:07 -0000 1.383 > +++ sbin/iked/ikev2.c 25 Jan 2024 09:30:51 -0000 > @@ -36,6 +36,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -294,6 +295,14 @@ 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_COMPILE: > return (config_getcompile(env)); > case IMSG_CTL_STATIC: > @@ -1781,6 +1790,7 @@ ikev2_init_done(struct iked *env, struct > ret = ikev2_childsa_enable(env, sa); > if (ret == 0) { > sa_state(env, sa, IKEV2_STATE_ESTABLISHED); > + radius_acct_start(env, sa); > /* Delete exchange timeout. */ > timer_del(env, &sa->sa_timer); > ikev2_enable_timer(env, sa); > @@ -2455,7 +2465,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; > @@ -2478,8 +2488,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 */ > @@ -3856,6 +3873,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 radius_request(env, sa, msg); > } > return -1; > } > @@ -4011,6 +4030,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); > + radius_acct_start(env, sa); > /* Delete exchange timeout. */ > timer_del(env, &sa->sa_timer); > ikev2_enable_timer(env, sa); > @@ -4745,10 +4765,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) - > @@ -4759,6 +4779,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); > + radius_acct_start(env, nsa); > ikev2_enable_timer(env, nsa); > > ikestat_inc(env, ikes_sa_rekeyed); > @@ -7028,6 +7050,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 +7068,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 +7100,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 +7129,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 +7206,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 +7226,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 +7247,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 +7333,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 +7681,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 -r1.100 ikev2_msg.c > --- sbin/iked/ikev2_msg.c 4 Aug 2023 19:06:25 -0000 1.100 > +++ sbin/iked/ikev2_msg.c 25 Jan 2024 09:30:52 -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 -r1.133 ikev2_pld.c > --- sbin/iked/ikev2_pld.c 2 Sep 2023 18:36:30 -0000 1.133 > +++ sbin/iked/ikev2_pld.c 25 Jan 2024 09:30:52 -0000 > @@ -2098,6 +2098,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, 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 -r1.144 parse.y > --- sbin/iked/parse.y 11 Aug 2023 11:24:55 -0000 1.144 > +++ sbin/iked/parse.y 25 Jan 2024 09:30:52 -0000 > @@ -40,7 +40,9 @@ > #include > #include > #include > +#include > #include > +#include > #include > #include > #include > @@ -107,6 +109,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 +398,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 +413,7 @@ typedef struct { > uint8_t ikemode; > uint8_t dir; > uint8_t satype; > + uint8_t accouting; > char *string; > uint16_t port; > struct ipsec_hosts *hosts; > @@ -446,6 +453,7 @@ typedef struct { > %token TOLERATE MAXAGE DYNAMIC > %token CERTPARTIALCHAIN > %token REQUEST IFACE > +%token RADIUS ACCOUNTING SERVER SECRET MAX_TRIES MAX_FAILOVERS > %token STRING > %token NUMBER > %type string > @@ -453,7 +461,7 @@ typedef struct { > %type proto proto_list protoval > %type hosts hosts_list > %type port > -%type portval af rdomain > +%type portval af rdomain number > %type peers > %type anyhost > %type host host_spec > @@ -470,6 +478,7 @@ typedef struct { > %type name iface > %type cfg ikecfg ikecfgvals > %type transform_esn > +%type accounting > %% > > grammar : /* empty */ > @@ -478,6 +487,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 +1049,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 +1061,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 +1073,6 @@ ikeauth : /* empty */ { > free($2); > > $$.auth_method = IKEV2_AUTH_SIG_ANY; > - $$.auth_eap = EAP_TYPE_MSCHAP_V2; > $$.auth_length = 0; > } > | STRING { > @@ -1245,6 +1263,103 @@ 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 NUMBER number { > + 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; > + } > + if ($6 != -1) > + config_setradcfgmap(env, cfgtype, $5, $6); > + else > + config_setradcfgmap(env, cfgtype, 0, $5); > + free($4); > + } > + ; > + > +number : { $$ = -1; } > + | NUMBER { $$ = $1; } > + ; > + > +accounting : { > + $$ = 0; > + } > + | ACCOUNTING { > + $$ = 1; > + } > + ; > + > varset : STRING '=' string > { > char *s = $1; > @@ -1338,6 +1453,7 @@ lookup(char *s) > static const struct keywords keywords[] = { > { "active", ACTIVE }, > { "ah", AH }, > + { "accounting", ACCOUNTING }, > { "any", ANY }, > { "auth", AUTHXF }, > { "bytes", BYTES }, > @@ -1371,6 +1487,8 @@ lookup(char *s) > { "ipcomp", IPCOMP }, > { "lifetime", LIFETIME }, > { "local", LOCAL }, > + { "max-failovers", MAX_FAILOVERS}, > + { "max-tries", MAX_TRIES }, > { "maxage", MAXAGE }, > { "mobike", MOBIKE }, > { "name", NAME }, > @@ -1388,9 +1506,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 +1913,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 +1937,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 -r1.84 pfkey.c > --- sbin/iked/pfkey.c 14 Aug 2023 12:02:02 -0000 1.84 > +++ sbin/iked/pfkey.c 25 Jan 2024 09:30:52 -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 -r1.97 policy.c > --- sbin/iked/policy.c 10 Nov 2023 08:03:02 -0000 1.97 > +++ sbin/iked/policy.c 25 Jan 2024 09:30:52 -0000 > @@ -60,6 +60,9 @@ 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); > 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 25 Jan 2024 09:30:52 -0000 > @@ -0,0 +1,741 @@ > +/* $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 radius_reset_timeout(struct iked_sa *); > +void radius_request_send(struct iked *, void *); > +void radius_config(struct iked_radserver_req *, const RADIUS_PACKET *, int, > + uint32_t, uint8_t); > +void 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 > +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, 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; > + } > + > + /* 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; > + > + radius_request_send(env, sa->sa_radreq); > + > + return 0; > +} > + > +void > +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 > +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; > + 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) { > + /* accouting */ > + 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: > + /* Try to retribute 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 retribute the EAP MSK from the " > + "RADIUS message"); > + free(req->rr_sa->sa_eapid); > + 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]; > + radius_config(req, pkt, cfgmap->cfg_type, > + cfgmap->vendor_id, cfgmap->attr_type); > + } > + } else { > + TAILQ_FOREACH(cfgmap, &env->sc_radcfgmaps, entry) > + 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: > + 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 retribute 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 retribute 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 > +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); > + > + /* NAS Port Type = Virtual */ > + radius_put_uint32_attr(req->rr_reqpkt, > + RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL); > + /* Service Type = Framed */ > + radius_put_uint32_attr(req->rr_reqpkt, > + RADIUS_TYPE_SERVICE_TYPE, RADIUS_SERVICE_TYPE_FRAMED); > + > + 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 > +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 > +radius_acct_on(struct iked *env) > +{ > + radius_acct_request(env, NULL, RADIUS_ACCT_STATUS_TYPE_ACCT_ON); > +} > + > +void > +radius_acct_off(struct iked *env) > +{ > + radius_acct_request(env, NULL, RADIUS_ACCT_STATUS_TYPE_ACCT_OFF); > +} > + > +void > +radius_acct_start(struct iked *env, struct iked_sa *sa) > +{ > + radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_START); > +} > + > +void > +radius_acct_stop(struct iked *env, struct iked_sa *sa) > +{ > + radius_acct_request(env, sa, RADIUS_ACCT_STATUS_TYPE_STOP); > +} > + > +void > +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 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) > + 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, 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; > + radius_request_send(env, req); > + return; > + } > + > + radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, sa->sa_eapid); > + > + /* RFC 2866 5.5. Acct-Session-Id */ > + snprintf(sid, sizeof(sid), "%16llx", > + (unsigned long long)sa->sa_hdr.sh_ispi); > + radius_put_string_attr(pkt, RADIUS_TYPE_ACCT_SESSION_ID, sid); > + > + /* Accouting 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); > + } > + } > +#ifndef RADIUS_TYPE_FRAMED_IPV6_ADDRESS > +#define RADIUS_TYPE_FRAMED_IPV6_ADDRESS 168 > +#endif > + 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; > + } > + } > + radius_put_uint32_attr(pkt, RADIUS_TYPE_ACCT_TERMINATE_CAUSE, > + cause); > + /* I/O statistsics {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); > + /* Identifier */ > + radius_put_string_attr(pkt, RADIUS_TYPE_NAS_IDENTIFIER, > + "OpenIKED"); > + radius_put_string_attr(pkt, RADIUS_TYPE_CALLED_STATION_ID, > + print_addr((struct sockaddr *)&sa->sa_local.addr)); > + radius_put_string_attr(pkt, RADIUS_TYPE_CALLING_STATION_ID, > + print_addr((struct sockaddr *)&sa->sa_peer.addr)); > + break; > + } > + req->rr_reqpkt = pkt; > + req->rr_ntry = 0; > + radius_request_send(env, req); > +} > Index: sbin/iked/types.h > =================================================================== > RCS file: /cvs/src/sbin/iked/types.h,v > diff -u -p -r1.53 types.h > --- sbin/iked/types.h 15 Jan 2024 15:29:00 -0000 1.53 > +++ sbin/iked/types.h 25 Jan 2024 09:30:52 -0000 > @@ -112,6 +112,10 @@ 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_CERTREQ, > IMSG_CERT, > IMSG_CERTVALID, > @@ -149,6 +153,7 @@ enum flushmode { > RESET_POLICY, > RESET_SA, > RESET_USER, > + RESET_RADIUS, > }; > > #ifndef nitems > -- wq: ~uw