/* * 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; }