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