Пример #1
0
/**
 * eapol_sm_step - EAPOL state machine step function
 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 *
 * This function is called to notify the state machine about changed external
 * variables. It will step through the EAPOL state machines in loop to process
 * all triggered state changes.
 */
void eapol_sm_step(struct eapol_sm *sm)
{
	int i;

	/* In theory, it should be ok to run this in loop until !changed.
	 * However, it is better to use a limit on number of iterations to
	 * allow events (e.g., SIGTERM) to stop the program cleanly if the
	 * state machine were to generate a busy loop. */
	for (i = 0; i < 100; i++) {
		sm->changed = FALSE;
		SM_STEP_RUN(SUPP_PAE);
		SM_STEP_RUN(KEY_RX);
		SM_STEP_RUN(SUPP_BE);
		if (eap_sm_step(sm->eap))
			sm->changed = TRUE;
		if (!sm->changed)
			break;
	}

	if (sm->changed) {
		/* restart EAPOL state machine step from timeout call in order
		 * to allow other events to be processed. */
		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
	}

	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
		int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
		sm->cb_status = EAPOL_CB_IN_PROGRESS;
		sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
	}
}
Пример #2
0
static int radius_server_request(struct radius_server_data *data,
				 struct radius_msg *msg,
				 struct sockaddr *from, socklen_t fromlen,
				 struct radius_client *client,
				 const char *from_addr, int from_port,
				 struct radius_session *force_sess)
{
	u8 *eap = NULL;
	size_t eap_len;
	int res, state_included = 0;
	u8 statebuf[4], resp_id;
	unsigned int state;
	struct radius_session *sess;
	struct radius_msg *reply;
	struct eap_hdr *hdr;

	if (force_sess)
		sess = force_sess;
	else {
		res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
					  sizeof(statebuf));
		state_included = res >= 0;
		if (res == sizeof(statebuf)) {
			state = (statebuf[0] << 24) | (statebuf[1] << 16) |
				(statebuf[2] << 8) | statebuf[3];
			sess = radius_server_get_session(client, state);
		} else {
			sess = NULL;
		}
	}

	if (sess) {
		RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
	} else if (state_included) {
		RADIUS_DEBUG("State attribute included but no session found");
		radius_server_reject(data, client, msg, from, fromlen,
				     from_addr, from_port);
		return -1;
	} else {
		sess = radius_server_get_new_session(data, client, msg);
		if (sess == NULL) {
			RADIUS_DEBUG("Could not create a new session");
			radius_server_reject(data, client, msg, from, fromlen,
					     from_addr, from_port);
			return -1;
		}
	}

	if (sess->last_from_port == from_port &&
	    sess->last_identifier == msg->hdr->identifier &&
	    os_memcmp(sess->last_authenticator, msg->hdr->authenticator, 16) ==
	    0) {
		RADIUS_DEBUG("Duplicate message from %s", from_addr);
		data->counters.dup_access_requests++;
		client->counters.dup_access_requests++;

		if (sess->last_reply) {
			res = sendto(data->auth_sock, sess->last_reply->buf,
				     sess->last_reply->buf_used, 0,
				     (struct sockaddr *) from, fromlen);
			if (res < 0) {
				perror("sendto[RADIUS SRV]");
			}
			return 0;
		}

		RADIUS_DEBUG("No previous reply available for duplicate "
			     "message");
		return -1;
	}

	eap = radius_msg_get_eap(msg, &eap_len);
	if (eap == NULL) {
		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
			     from_addr);
		data->counters.packets_dropped++;
		client->counters.packets_dropped++;
		return -1;
	}

	RADIUS_DUMP("Received EAP data", eap, eap_len);
	if (eap_len >= sizeof(*hdr)) {
		hdr = (struct eap_hdr *) eap;
		resp_id = hdr->identifier;
	} else {
		resp_id = 0;
	}

	/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
	 * RFC3579 Sect. 2.6.2.
	 * Include EAP-Response/Nak with no preferred method if
	 * code == request.
	 * If code is not 1-4, discard the packet silently.
	 * Or is this already done by the EAP state machine? */

	eap_set_eapRespData(sess->eap, eap, eap_len);
	VM_FREE(eap);
	eap = NULL;
	sess->eapResp = TRUE;
	eap_sm_step(sess->eap);

	if (sess->eapReqData) {
		RADIUS_DUMP("EAP data from the state machine",
			    sess->eapReqData, sess->eapReqDataLen);
	} else if (sess->eapFail) {
		RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
			     "set - generate EAP-Failure");
		hdr = wpa_zalloc(sizeof(*hdr));
		if (hdr) {
			hdr->identifier = resp_id;
			hdr->length = htons(sizeof(*hdr));
			sess->eapReqData = (u8 *) hdr;
			sess->eapReqDataLen = sizeof(*hdr);
		}
	} else if (eap_sm_method_pending(sess->eap)) {
		if (sess->last_msg) {
			radius_msg_free(sess->last_msg);
			VM_FREE(sess->last_msg);
		}
		sess->last_msg = msg;
		sess->last_from_port = from_port;
		VM_FREE(sess->last_from_addr);
		sess->last_from_addr = VM_STRDUP(from_addr);
		sess->last_fromlen = fromlen;
		memcpy(&sess->last_from, from, fromlen);
		return -2;
	} else {
		RADIUS_DEBUG("No EAP data from the state machine - ignore this"
			     " Access-Request silently (assuming it was a "
			     "duplicate)");
		data->counters.packets_dropped++;
		client->counters.packets_dropped++;
		return -1;
	}

	reply = radius_server_encapsulate_eap(data, client, sess, msg);

	VM_FREE(sess->eapReqData);
	sess->eapReqData = NULL;
	sess->eapReqDataLen = 0;

	if (reply) {
		RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
		if (wpa_debug_level <= MSG_MSGDUMP) {
			radius_msg_dump(reply);
		}

		switch (reply->hdr->code) {
		case RADIUS_CODE_ACCESS_ACCEPT:
			data->counters.access_accepts++;
			client->counters.access_accepts++;
			break;
		case RADIUS_CODE_ACCESS_REJECT:
			data->counters.access_rejects++;
			client->counters.access_rejects++;
			break;
		case RADIUS_CODE_ACCESS_CHALLENGE:
			data->counters.access_challenges++;
			client->counters.access_challenges++;
			break;
		}
		res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0,
			     (struct sockaddr *) from, fromlen);
		if (res < 0) {
			perror("sendto[RADIUS SRV]");
		}
		if (sess->last_reply) {
			radius_msg_free(sess->last_reply);
			VM_FREE(sess->last_reply);
		}
		sess->last_reply = reply;
		sess->last_from_port = from_port;
		sess->last_identifier = msg->hdr->identifier;
		os_memcpy(sess->last_authenticator, msg->hdr->authenticator,
			  16);
	} else {
		data->counters.packets_dropped++;
		client->counters.packets_dropped++;
	}

	if (sess->eapSuccess || sess->eapFail) {
		RADIUS_DEBUG("Removing completed session 0x%x after timeout",
			     sess->sess_id);
		eloop_cancel_timeout(radius_server_session_remove_timeout,
				     data, sess);
		eloop_register_timeout(10, 0,
				       radius_server_session_remove_timeout,
				       data, sess);
	}

	return 0;
}
Пример #3
0
static int radius_server_request(struct radius_server_data *data,
				 struct radius_msg *msg,
				 struct sockaddr_in *from,
				 struct radius_client *client)
{
	u8 *eap = NULL;
	size_t eap_len;
	int res, state_included;
	u8 statebuf[4], resp_id;
	unsigned int state;
	struct radius_session *sess;
	struct radius_msg *reply;
	struct eap_hdr *hdr;

	/* TODO: Implement duplicate packet processing */

	res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
				  sizeof(statebuf));
	state_included = res >= 0;
	if (res == sizeof(statebuf)) {
		state = (statebuf[0] << 24) | (statebuf[1] << 16) |
			(statebuf[2] << 8) | statebuf[3];
		sess = radius_server_get_session(client, state);
	} else {
		sess = NULL;
	}

	if (sess) {
		RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
	} else if (state_included) {
		RADIUS_DEBUG("State attribute included but no session found");
		radius_server_reject(data, client, msg, from);
		return -1;
	} else {
		sess = radius_server_get_new_session(data, client, msg);
		if (sess == NULL) {
			RADIUS_DEBUG("Could not create a new session");
			return -1;
		}
	}

	eap = radius_msg_get_eap(msg, &eap_len);
	if (eap == NULL) {
		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
			     inet_ntoa(from->sin_addr));
		return -1;
	}

	RADIUS_DUMP("Received EAP data", eap, eap_len);
	if (eap_len >= sizeof(*hdr)) {
		hdr = (struct eap_hdr *) eap;
		resp_id = hdr->identifier;
	} else {
		resp_id = 0;
	}

	eap_set_eapRespData(sess->eap, eap, eap_len);
	free(eap);
	eap = NULL;
	sess->eapResp = TRUE;
	eap_sm_step(sess->eap);

	if (sess->eapReqData) {
		RADIUS_DUMP("EAP data from the state machine",
			    sess->eapReqData, sess->eapReqDataLen);
	} else if (sess->eapFail) {
		RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
			     "set - generate EAP-Failure");
		hdr = malloc(sizeof(*hdr));
		if (hdr) {
			memset(hdr, 0, sizeof(*hdr));
			hdr->identifier = resp_id;
			hdr->length = htons(sizeof(*hdr));
			sess->eapReqData = (u8 *) hdr;
			sess->eapReqDataLen = sizeof(*hdr);
		}
	} else {
		RADIUS_DEBUG("No EAP data from the state machine - ignore this"
			     " Access-Request silently (assuming it was a "
			     "duplicate)");
		return -1;
	}

	reply = radius_server_encapsulate_eap(data, client, sess, msg);

	free(sess->eapReqData);
	sess->eapReqData = NULL;
	sess->eapReqDataLen = 0;

	if (reply) {
		RADIUS_DEBUG("Reply to %s:%d", inet_ntoa(from->sin_addr),
			     ntohs(from->sin_port));
		if (wpa_debug_level <= MSG_MSGDUMP) {
			radius_msg_dump(reply);
		}

		res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0,
			     (struct sockaddr *) from, sizeof(*from));
		if (res < 0) {
			perror("sendto[RADIUS SRV]");
		}
		radius_msg_free(reply);
		free(reply);
	}

	if (sess->eapSuccess || sess->eapFail) {
		RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
		radius_server_session_remove(data, sess);
	}

	return 0;
}