/** * radius_client_init - Initialize RADIUS client * @ctx: Callback context to be used in hostapd_logger() calls * @conf: RADIUS client configuration (RADIUS servers) * Returns: Pointer to private RADIUS client context or %NULL on failure * * The caller is responsible for keeping the configuration data available for * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is * called for the returned context pointer. */ struct radius_client_data * radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { struct radius_client_data *radius; radius = os_zalloc(sizeof(struct radius_client_data)); if (radius == NULL) return NULL; radius->ctx = ctx; radius->conf = conf; radius->auth_serv_sock = radius->acct_serv_sock = radius->auth_serv_sock6 = radius->acct_serv_sock6 = radius->auth_sock = radius->acct_sock = -1; if (conf->auth_server && radius_client_init_auth(radius)) { radius_client_deinit(radius); return NULL; } if (conf->acct_server && radius_client_init_acct(radius)) { radius_client_deinit(radius); return NULL; } if (conf->retry_primary_interval) eloop_register_timeout(conf->retry_primary_interval, 0, radius_retry_primary_timer, radius, NULL); return radius; }
/* * Returns >0 if message queue was flushed (i.e., the message that triggered * the error is not available anymore) */ static int radius_client_handle_send_error(struct radius_client_data *radius, int s, RadiusType msg_type) { #ifndef CONFIG_NATIVE_WINDOWS int _errno = errno; wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" " try to connect again"); if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { radius_client_init_acct(radius); return 0; } else { radius_client_init_auth(radius); return 1; } } #endif /* CONFIG_NATIVE_WINDOWS */ return 0; }
struct radius_client_data * radius_client_init(struct hostapd_data *hapd) { struct radius_client_data *radius; radius = malloc(sizeof(struct radius_client_data)); if (radius == NULL) return NULL; memset(radius, 0, sizeof(struct radius_client_data)); radius->hapd = hapd; radius->auth_serv_sock = radius->acct_serv_sock = -1; if (hapd->conf->auth_server && radius_client_init_auth(radius)) { radius_client_deinit(radius); return NULL; } if (hapd->conf->acct_server && radius_client_init_acct(radius)) { radius_client_deinit(radius); return NULL; } if (hapd->conf->radius_retry_primary_interval) eloop_register_timeout(hapd->conf-> radius_retry_primary_interval, 0, radius_retry_primary_timer, radius, NULL); return radius; }
static void radius_client_handle_send_error(struct radius_client_data *radius, int s, RadiusType msg_type) { struct hostapd_data *hapd = radius->hapd; int _errno = errno; perror("send[RADIUS]"); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" " try to connect again"); eloop_unregister_read_sock(s); close(s); if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) radius_client_init_acct(radius); else radius_client_init_auth(radius); } }
int radius_client_init(hostapd *hapd) { hapd->radius = malloc(sizeof(struct radius_client_data)); if (hapd->radius == NULL) return -1; memset(hapd->radius, 0, sizeof(struct radius_client_data)); hapd->radius->auth_serv_sock = hapd->radius->acct_serv_sock = -1; if (hapd->conf->auth_server && radius_client_init_auth(hapd)) return -1; if (hapd->conf->acct_server && radius_client_init_acct(hapd)) return -1; if (hapd->conf->radius_retry_primary_interval) eloop_register_timeout(hapd->conf-> radius_retry_primary_interval, 0, radius_retry_primary_timer, hapd, NULL); return 0; }
int radius_client_init(struct wpa_supplicant *wpa_s) { wpa_s->radius = malloc(sizeof(struct radius_client_data)); if (wpa_s->radius == NULL) return -1; memset(wpa_s->radius, 0, sizeof(struct radius_client_data)); wpa_s->radius->auth_serv_sock = wpa_s->radius->acct_serv_sock = -1; if (wpa_s->auth_server && radius_client_init_auth(wpa_s)) return -1; if (wpa_s->acct_server && radius_client_init_acct(wpa_s)) return -1; if (wpa_s->radius_retry_primary_interval) eloop_register_timeout(wpa_s->radius_retry_primary_interval, 0, radius_retry_primary_timer, wpa_s, NULL); return 0; }
/** * radius_client_send - Send a RADIUS request * @radius: RADIUS client context from radius_client_init() * @msg: RADIUS message to be sent * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) * @addr: MAC address of the device related to this message or %NULL * Returns: 0 on success, -1 on failure * * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference * between accounting and interim accounting messages is that the interim * message will not be retransmitted. Instead, a callback is used to indicate * that the transmission failed for the specific station @addr so that a new * interim accounting update message can be generated with up-to-date session * data instead of trying to resend old information. * * The message is added on the retransmission queue and will be retransmitted * automatically until a response is received or maximum number of retries * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue * automatically on transmission failure. * * The related device MAC address can be used to identify pending messages that * can be removed with radius_client_flush_auth(). */ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr) { struct hostapd_radius_servers *conf = radius->conf; const u8 *shared_secret; size_t shared_secret_len; char *name; int s, res; struct wpabuf *buf; if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { if (conf->acct_server && radius->acct_sock < 0) radius_client_init_acct(radius); if (conf->acct_server == NULL || radius->acct_sock < 0 || conf->acct_server->shared_secret == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "No accounting server configured"); return -1; } shared_secret = conf->acct_server->shared_secret; shared_secret_len = conf->acct_server->shared_secret_len; radius_msg_finish_acct(msg, shared_secret, shared_secret_len); name = "accounting"; s = radius->acct_sock; conf->acct_server->requests++; } else { if (conf->auth_server && radius->auth_sock < 0) radius_client_init_auth(radius); if (conf->auth_server == NULL || radius->auth_sock < 0 || conf->auth_server->shared_secret == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "No authentication server configured"); return -1; } shared_secret = conf->auth_server->shared_secret; shared_secret_len = conf->auth_server->shared_secret_len; radius_msg_finish(msg, shared_secret, shared_secret_len); name = "authentication"; s = radius->auth_sock; conf->auth_server->requests++; } hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " "server", name); if (conf->msg_dumps) radius_msg_dump(msg); buf = radius_msg_get_buf(msg); res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); if (res < 0) radius_client_handle_send_error(radius, s, msg_type); radius_client_list_add(radius, msg, msg_type, shared_secret, shared_secret_len, addr); return 0; }
static int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, os_time_t now) { struct hostapd_radius_servers *conf = radius->conf; int s; struct wpabuf *buf; size_t prev_num_msgs; u8 *acct_delay_time; size_t acct_delay_time_len; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { if (radius->acct_sock < 0) radius_client_init_acct(radius); if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { prev_num_msgs = radius->num_msgs; radius_client_acct_failover(radius); if (prev_num_msgs != radius->num_msgs) return 0; } s = radius->acct_sock; if (entry->attempts == 0) conf->acct_server->requests++; else { conf->acct_server->timeouts++; conf->acct_server->retransmissions++; } } else { if (radius->auth_sock < 0) radius_client_init_auth(radius); if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { prev_num_msgs = radius->num_msgs; radius_client_auth_failover(radius); if (prev_num_msgs != radius->num_msgs) return 0; } s = radius->auth_sock; if (entry->attempts == 0) conf->auth_server->requests++; else { conf->auth_server->timeouts++; conf->auth_server->retransmissions++; } } if (entry->msg_type == RADIUS_ACCT_INTERIM) { wpa_printf(MSG_DEBUG, "RADIUS: Failed to transmit interim accounting update to " MACSTR " - drop message and request a new update", MAC2STR(entry->addr)); if (radius->interim_error_cb) radius->interim_error_cb(entry->addr, radius->interim_error_cb_ctx); return 1; } if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: No valid socket for retransmission"); return 1; } if (entry->msg_type == RADIUS_ACCT && radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, &acct_delay_time, &acct_delay_time_len, NULL) == 0 && acct_delay_time_len == 4) { struct radius_hdr *hdr; u32 delay_time; /* * Need to assign a new identifier since attribute contents * changes. */ hdr = radius_msg_get_hdr(entry->msg); hdr->identifier = radius_client_get_id(radius); /* Update Acct-Delay-Time to show wait time in queue */ delay_time = now - entry->first_try; WPA_PUT_BE32(acct_delay_time, delay_time); wpa_printf(MSG_DEBUG, "RADIUS: Updated Acct-Delay-Time to %u for retransmission", delay_time); radius_msg_finish_acct(entry->msg, entry->shared_secret, entry->shared_secret_len); if (radius->conf->msg_dumps) radius_msg_dump(entry->msg); } /* retransmit; remove entry if too many attempts */ entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { if (radius_client_handle_send_error(radius, s, entry->msg_type) > 0) return 0; } entry->next_try = now + entry->next_wait; entry->next_wait *= 2; if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) entry->next_wait = RADIUS_CLIENT_MAX_WAIT; if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } return 0; }
static int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, os_time_t now) { struct hostapd_radius_servers *conf = radius->conf; int s; struct wpabuf *buf; size_t prev_num_msgs; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { if (radius->acct_sock < 0) radius_client_init_acct(radius); if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { prev_num_msgs = radius->num_msgs; radius_client_acct_failover(radius); if (prev_num_msgs != radius->num_msgs) return 0; } s = radius->acct_sock; if (entry->attempts == 0) conf->acct_server->requests++; else { conf->acct_server->timeouts++; conf->acct_server->retransmissions++; } } else { if (radius->auth_sock < 0) radius_client_init_auth(radius); if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { prev_num_msgs = radius->num_msgs; radius_client_auth_failover(radius); if (prev_num_msgs != radius->num_msgs) return 0; } s = radius->auth_sock; if (entry->attempts == 0) conf->auth_server->requests++; else { conf->auth_server->timeouts++; conf->auth_server->retransmissions++; } } if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: No valid socket for retransmission"); return 1; } /* retransmit; remove entry if too many attempts */ entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { if (radius_client_handle_send_error(radius, s, entry->msg_type) > 0) return 0; } entry->next_try = now + entry->next_wait; entry->next_wait *= 2; if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) entry->next_wait = RADIUS_CLIENT_MAX_WAIT; if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } return 0; }