/*
 *	If we're proxying EAP, then there may be magic we need
 *	to do.
 */
static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
{
	size_t		i;
	size_t		len;
	VALUE_PAIR	*vp;
	eap_handler_t	*handler;

	/*
	 *	Just in case the admin lists EAP in post-proxy-type Fail.
	 */
	if (!request->proxy_reply) return RLM_MODULE_NOOP;

	/*
	 *	If there was a handler associated with this request,
	 *	then it's a tunneled request which was proxied...
	 */
	handler = request_data_get(request, inst, REQUEST_DATA_eap_handler_t);
	if (handler != NULL) {
		rlm_rcode_t rcode;
		eap_tunnel_data_t *data;

		/*
		 *	Grab the tunnel callbacks from the request.
		 */
		data = (eap_tunnel_data_t *) request_data_get(request,
							      request->proxy,
							      REQUEST_DATA_EAP_TUNNEL_CALLBACK);
		if (!data) {
			radlog_request(L_ERR, 0, request, "Failed to retrieve callback for tunneled session!");
			eap_handler_free(inst, handler);
			return RLM_MODULE_FAIL;
		}

		/*
		 *	Do the callback...
		 */
		RDEBUG2("Doing post-proxy callback");
		rcode = data->callback(handler, data->tls_session);
		free(data);
		if (rcode == 0) {
			RDEBUG2("Failed in post-proxy callback");
			eap_fail(handler);
			eap_handler_free(inst, handler);
			return RLM_MODULE_REJECT;
		}

		/*
		 *	We are done, wrap the EAP-request in RADIUS to send
		 *	with all other required radius attributes
		 */
		eap_compose(handler);

		/*
		 *	Add to the list only if it is EAP-Request, OR if
		 *	it's LEAP, and a response.
		 */
		if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
		    (handler->eap_ds->request->type.num >= PW_EAP_MD5)) {
			if (!eaplist_add(inst, handler)) {
				eap_fail(handler);
				eap_handler_free(inst, handler);
				return RLM_MODULE_FAIL;
			}
			
		} else {	/* couldn't have been LEAP, there's no tunnel */
			RDEBUG2("Freeing handler");
			/* handler is not required any more, free it now */
			eap_handler_free(inst, handler);
		}

		/*
		 *	If it's an Access-Accept, RFC 2869, Section 2.3.1
		 *	says that we MUST include a User-Name attribute in the
		 *	Access-Accept.
		 */
		if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
		    request->username) {
			/*
			 *	Doesn't exist, add it in.
			 */
			vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
			if (!vp) {
				pairmake_reply("User-Name",
					       request->username->vp_strvalue,
					       T_OP_EQ);
			}
		}

		return RLM_MODULE_OK;
	} else {
		RDEBUG2("No pre-existing handler found");
	}

	/*
	 *	There may be more than one Cisco-AVPair.
	 *	Ensure we find the one with the LEAP attribute.
	 */
	vp = request->proxy_reply->vps;
	for (;;) {
		/*
		 *	Hmm... there's got to be a better way to
		 *	discover codes for vendor attributes.
		 *
		 *	This is vendor Cisco (9), Cisco-AVPair
		 *	attribute (1)
		 */
		vp = pairfind(vp, 1, 9, TAG_ANY);
		if (!vp) {
			return RLM_MODULE_NOOP;
		}

		/*
		 *	If it's "leap:session-key", then stop.
		 *
		 *	The format is VERY specific!
		 */
		if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) {
			break;
		}

		/*
		 *	Not this AV-pair.  Go to the next one.
		 */
		vp = vp->next;
	}

	/*
	 *	The format is very specific.
	 */
	if (vp->length != 17 + 34) {
		RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d",
		       vp->length, 17 + 34);
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Decrypt the session key, using the proxy data.
	 */
	i = 34;			/* starts off with 34 octets */
	len = rad_tunnel_pwdecode(vp->vp_octets + 17, &i,
				  request->home_server->secret,
				  request->proxy->vector);

	/*
	 *	FIXME: Assert that i == 16.
	 */

	/*
	 *	Encrypt the session key again, using the request data.
	 */
	rad_tunnel_pwencode(vp->vp_strvalue + 17, &len,
			    request->client->secret,
			    request->packet->vector);

	return RLM_MODULE_UPDATED;
}
/*
 *	Do EAP.
 */
static rlm_rcode_t eap_authenticate(void *instance, REQUEST *request)
{
	rlm_eap_t	*inst;
	EAP_HANDLER	*handler;
	void		*data;
	int		data_len;
	rlm_rcode_t	rcode;
	VALUE_PAIR	*vp;

	inst = (rlm_eap_t *) instance;

	vp = pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
	if (!vp) {
		RDEBUG("No EAP-Message.  Not doing EAP.");
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Get the eap packet  to start with
	 */
	data = NULL;
	data_len = 0;
	if (eap_vp2data(request->packet->vps, &data, &data_len) < 0) {
		radlog(L_ERR, "rlm_eap2: Malformed EAP Message");
		return RLM_MODULE_FAIL;
	}

	vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
	if (vp) {
		handler = eaplist_find(inst, request);
		if (!handler) {
			RDEBUG("No handler found");
			return RLM_MODULE_FAIL;
		}
	} else {
		handler = malloc(sizeof(*handler));
		if (!handler) return RLM_MODULE_FAIL;

		memset(handler, 0, sizeof(*handler));

		handler->inst = inst;
		handler->eap_cb.get_eap_user = server_get_eap_user;
		handler->eap_cb.get_eap_req_id_text = server_get_eap_req_id_text;

		handler->eap_conf.eap_server = 1;
		handler->eap_conf.ssl_ctx = inst->tls_ctx;

		/*
		 *	Copy EAP-FAST parameters.
		 */
		handler->eap_conf.pac_opaque_encr_key = inst->pac_opaque_encr_key; 
		handler->eap_conf.eap_fast_a_id = inst->eap_fast_a_id; 
		handler->eap_conf.eap_fast_a_id_len = strlen(inst->eap_fast_a_id); 
		handler->eap_conf.eap_fast_a_id_info = inst->eap_fast_a_id_info; 
		handler->eap_conf.eap_fast_prov = inst->eap_fast_prov; 
		handler->eap_conf.pac_key_lifetime = inst->pac_key_lifetime; 
		handler->eap_conf.pac_key_refresh_time = inst->pac_key_refresh_time; 
		handler->eap_conf.backend_auth = inst->backend_auth; 
		
		handler->server_ctx.eap = eap_server_sm_init(handler,
							     &handler->eap_cb,
							     &handler->eap_conf);
		if (handler->server_ctx.eap == NULL) {
			free(handler);
			return RLM_MODULE_FAIL;
		}
		
		handler->server_ctx.eap_if = eap_get_interface(handler->server_ctx.eap);
		
		/* Enable "port" and request EAP to start authentication. */
		handler->server_ctx.eap_if->portEnabled = TRUE;
		handler->server_ctx.eap_if->eapRestart = TRUE;
	}

	handler->request = request;
	wpabuf_free(handler->server_ctx.eap_if->eapRespData);
	handler->server_ctx.eap_if->eapRespData = wpabuf_alloc_copy(data, data_len);
	if (handler->server_ctx.eap_if->eapRespData) {
		handler->server_ctx.eap_if->eapResp = TRUE;
	}
	
	if (eap_example_server_step(handler) < 0) {
		RDEBUG("Failed in EAP library");
		goto fail;
	}

	if (handler->server_ctx.eap_if->eapSuccess) {
		request->reply->code = PW_AUTHENTICATION_ACK;
		rcode = RLM_MODULE_OK;

	} else if (handler->server_ctx.eap_if->eapFail) {
	fail:
		request->reply->code = PW_AUTHENTICATION_REJECT;
		rcode = RLM_MODULE_REJECT;

	} else {
		request->reply->code = PW_ACCESS_CHALLENGE;
		rcode = RLM_MODULE_HANDLED;
	}

	if (handler->server_ctx.eap_if->eapFail ||
	    handler->server_ctx.eap_if->eapSuccess) {
		RDEBUG2("Freeing handler");
		/* handler is not required any more, free it now */
		eap_handler_free(handler);
		handler = NULL;
	} else {
		eaplist_add(inst, handler);
	}

	/*
	 *	If it's an Access-Accept, RFC 2869, Section 2.3.1
	 *	says that we MUST include a User-Name attribute in the
	 *	Access-Accept.
	 */
	if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
	    request->username) {
		/*
		 *	Doesn't exist, add it in.
		 */
		vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
		if (!vp) {
			vp = pairmake("User-Name", request->username->vp_strvalue,
				      T_OP_EQ);
			rad_assert(vp != NULL);
			pairadd(&(request->reply->vps), vp);
		}

		/*
		 *	Cisco AP1230 has a bug and needs a zero
		 *	terminated string in Access-Accept.
		 */
		if ((inst->cisco_accounting_username_bug) &&
		    (vp->length < (int) sizeof(vp->vp_strvalue))) {
			vp->vp_strvalue[vp->length] = '\0';
			vp->length++;
		}
	}

	vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
	if (!vp) {
		vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
		memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN);
		vp->length = AUTH_VECTOR_LEN;
		pairadd(&(request->reply->vps), vp);
	}
	return rcode;
}
/*
 *	For backwards compatibility.
 */
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
{
	rlm_eap_t		*inst;
	eap_handler_t		*handler;
	eap_packet_raw_t	*eap_packet;
	eap_rcode_t		status;
	rlm_rcode_t		rcode;

	inst = (rlm_eap_t *) instance;

	if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
		RDEBUGE("You set 'Auth-Type = EAP' for a request that does "
			"not contain an EAP-Message attribute!");
		return RLM_MODULE_INVALID;
	}

	/*
	 *	Get the eap packet  to start with
	 */
	eap_packet = eap_vp2packet(request, request->packet->vps);
	if (!eap_packet) {
		radlog_request(L_ERR, 0, request, "Malformed EAP Message");
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Create the eap handler.  The eap_packet will end up being
	 *	"swallowed" into the handler, so we can't access it after
	 *	this call.
	 */
	handler = eap_handler(inst, &eap_packet, request);
	if (!handler) {
		RDEBUG2("Failed in handler");
		return RLM_MODULE_INVALID;
	}

	/*
	 *	Select the appropriate method or default to the
	 *	configured one
	 */
	status = eap_method_select(inst, handler);

	/*
	 *	If it failed, die.
	 */
	if (status == EAP_INVALID) {
		eap_fail(handler);
		eap_handler_free(inst, handler);
		RDEBUG2("Failed in EAP select");
		return RLM_MODULE_INVALID;
	}

#ifdef WITH_PROXY
	/*
	 *	If we're doing horrible tunneling work, remember it.
	 */
	if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
		RDEBUG2("  Not-EAP proxy set.  Not composing EAP");
		/*
		 *	Add the handle to the proxied list, so that we
		 *	can retrieve it in the post-proxy stage, and
		 *	send a response.
		 */
		handler->inst_holder = inst;
		status = request_data_add(request,
					  inst, REQUEST_DATA_eap_handler_t,
					  handler, (void *) eap_opaque_free);
		rad_assert(status == 0);
		return RLM_MODULE_HANDLED;
	}
#endif

#ifdef WITH_PROXY
	/*
	 *	Maybe the request was marked to be proxied.  If so,
	 *	proxy it.
	 */
	if (request->proxy != NULL) {
		VALUE_PAIR *vp = NULL;

		rad_assert(!request->proxy_reply);

		/*
		 *	Add the handle to the proxied list, so that we
		 *	can retrieve it in the post-proxy stage, and
		 *	send a response.
		 */
		handler->inst_holder = inst;
		status = request_data_add(request,
					  inst, REQUEST_DATA_eap_handler_t,
					  handler,
					  (void *) eap_opaque_free);
		rad_assert(status == 0);

		/*
		 *	Some simple sanity checks.  These should really
		 *	be handled by the radius library...
		 */
		vp = pairfind(request->proxy->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
		if (vp) {
			vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
			if (!vp) {
				pairmake(request->proxy,
					 &request->proxy->vps,
					 "Message-Authenticator",
					 "0x00", T_OP_EQ);
			}
		}

		/*
		 *	Delete the "proxied to" attribute, as it's
		 *	set to 127.0.0.1 for tunneled requests, and
		 *	we don't want to tell the world that...
		 */
		pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);

		RDEBUG2("  Tunneled session will be proxied.  Not doing EAP.");
		return RLM_MODULE_HANDLED;
	}
#endif

	/*
	 *	We are done, wrap the EAP-request in RADIUS to send
	 *	with all other required radius attributes
	 */
	rcode = eap_compose(handler);

	/*
	 *	Add to the list only if it is EAP-Request, OR if
	 *	it's LEAP, and a response.
	 */
	if (((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
	    (handler->eap_ds->request->type.num >= PW_EAP_MD5)) ||

		/*
		 *	LEAP is a little different.  At Stage 4,
		 *	it sends an EAP-Success message, but we still
		 *	need to keep the State attribute & session
		 *	data structure around for the AP Challenge.
		 *
		 *	At stage 6, LEAP sends an EAP-Response, which
		 *	isn't put into the list.
		 */
	    ((handler->eap_ds->response->code == PW_EAP_RESPONSE) &&
	     (handler->eap_ds->response->type.num == PW_EAP_LEAP) &&
	     (handler->eap_ds->request->code == PW_EAP_SUCCESS) &&
	     (handler->eap_ds->request->type.num == 0))) {

		/*
		 *	Return FAIL if we can't remember the handler.
		 *	This is actually disallowed by the
		 *	specification, as unexpected FAILs could have
		 *	been forged.  However, we want to signal to
		 *	everyone else involved that we are
		 *	intentionally failing the session, as opposed
		 *	to accidentally failing it.
		 */
		if (!eaplist_add(inst, handler)) {
			RDEBUG("Failed adding handler to the list");
			eap_fail(handler);
			eap_handler_free(inst, handler);
			return RLM_MODULE_FAIL;
		}

	} else {
		RDEBUG2("Freeing handler");
		/* handler is not required any more, free it now */
		eap_handler_free(inst, handler);
	}

	/*
	 *	If it's an Access-Accept, RFC 2869, Section 2.3.1
	 *	says that we MUST include a User-Name attribute in the
	 *	Access-Accept.
	 */
	if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
	    request->username) {
		VALUE_PAIR *vp;

		/*
		 *	Doesn't exist, add it in.
		 */
		vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
		if (!vp) {
			vp = pairmake_reply("User-Name", "",
				      T_OP_EQ);
			strlcpy(vp->vp_strvalue, request->username->vp_strvalue,
				sizeof(vp->vp_strvalue));
			vp->length = request->username->length;
		}

		/*
		 *	Cisco AP1230 has a bug and needs a zero
		 *	terminated string in Access-Accept.
		 */
		if ((inst->mod_accounting_username_bug) &&
		    (vp->length < (int) sizeof(vp->vp_strvalue))) {
			vp->vp_strvalue[vp->length] = '\0';
			vp->length++;
		}
	}

	return rcode;
}