static int eap_post_auth(void *instance, REQUEST *request) { rlm_eap_t *inst = instance; VALUE_PAIR *vp; EAP_HANDLER *handler; eap_packet_t *eap_packet; /* * Only build a failure message if something previously rejected the request */ vp = pairfind(request->config_items, PW_POSTAUTHTYPE, 0, TAG_ANY); if (!vp || (vp->vp_integer != PW_POSTAUTHTYPE_REJECT)) return RLM_MODULE_NOOP; if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) { RDEBUG2("Request didn't contain an EAP-Message, not inserting EAP-Failure"); return RLM_MODULE_NOOP; } if (pairfind(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) { RDEBUG2("Reply already contained an EAP-Message, not inserting EAP-Failure"); return RLM_MODULE_NOOP; } eap_packet = eap_vp2packet(request->packet->vps); if (eap_packet == NULL) { radlog_request(L_ERR, 0, request, "Malformed EAP Message"); return RLM_MODULE_FAIL; } handler = eap_handler(inst, &eap_packet, request); if (handler == NULL) { RDEBUG2("Failed to get handler, probably already removed, not inserting EAP-Failure"); return RLM_MODULE_NOOP; } RDEBUG2("Request was previously rejected, inserting EAP-Failure"); eap_fail(handler); eap_handler_free(inst, handler); /* * Make sure there's a message authenticator attribute in the response * RADIUS protocol code will calculate the correct value later... */ vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY); if (!vp) { vp = pairmake("Message-Authenticator", "0x00", T_OP_EQ); rad_assert(vp != NULL); pairadd(&(request->reply->vps), vp); } return RLM_MODULE_UPDATED; }
/* * given a radius request with an EAP-Message body, decode it specific * attributes. */ static void unmap_eap_methods(RADIUS_PACKET *rep) { VALUE_PAIR *eap1; eap_packet_raw_t *e; int len; int type; if (!rep) return; /* find eap message */ e = eap_vp2packet(NULL, rep->vps); /* nothing to do! */ if(!e) return; /* create EAP-ID and EAP-CODE attributes to start */ eap1 = paircreate(rep, ATTRIBUTE_EAP_ID, 0); eap1->vp_integer = e->id; pairadd(&(rep->vps), eap1); eap1 = paircreate(rep, ATTRIBUTE_EAP_CODE, 0); eap1->vp_integer = e->code; pairadd(&(rep->vps), eap1); switch(e->code) { default: case PW_EAP_SUCCESS: case PW_EAP_FAILURE: /* no data */ break; case PW_EAP_REQUEST: case PW_EAP_RESPONSE: /* there is a type field, which we use to create * a new attribute */ /* the length was decode already into the attribute * length, and was checked already. Network byte * order, just pull it out using math. */ len = e->length[0]*256 + e->length[1]; /* verify the length is big enough to hold type */ if(len < 5) { talloc_free(e); return; } type = e->data[0]; type += ATTRIBUTE_EAP_BASE; len -= 5; if (len > MAX_STRING_LEN) { len = MAX_STRING_LEN; } eap1 = paircreate(rep, type, 0); pairmemcpy(eap1, e->data + 1, len); pairadd(&(rep->vps), eap1); break; } talloc_free(e); return; }
/* * 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; }
/* * calculate the MAC for the EAP message, given the key. * The "extra" will be appended to the EAP message and included in the * HMAC. * */ int eapsim_checkmac(VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen, uint8_t calcmac[20]) { int ret; eap_packet_t *e; uint8_t *buffer; int elen,len; VALUE_PAIR *mac; mac = pairfind(rvps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0); if(mac == NULL || mac->length != 18) { /* can't check a packet with no AT_MAC attribute */ return 0; } /* get original copy of EAP message, note that it was sanitized * to have a valid length, which we depend upon. */ e = eap_vp2packet(rvps); if(e == NULL) { return 0; } /* make copy big enough for everything */ elen = e->length[0] * 256 + e->length[1]; len = elen + extralen; buffer = malloc(len); if(buffer == NULL) { free(e); return 0; } memcpy(buffer, e, elen); memcpy(buffer+elen, extra, extralen); /* * now look for the AT_MAC attribute in the copy of the buffer * and make sure that the checksum is zero. * */ { uint8_t *attr; /* first attribute is 8 bytes into the EAP packet. * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved. */ attr = buffer+8; while(attr < (buffer+elen)) { if(attr[0] == PW_EAP_SIM_MAC) { /* zero the data portion, after making sure * the size is >=5. Maybe future versions. * will use more bytes, so be liberal. */ if(attr[1] < 5) { ret = 0; goto done; } memset(&attr[4], 0, (attr[1]-1)*4); } /* advance the pointer */ attr += attr[1]*4; } } /* now, HMAC-SHA1 it with the key. */ fr_hmac_sha1(buffer, len, key, 16, calcmac); if(memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0) { ret = 1; } else { ret = 0; } done: free(e); free(buffer); return(ret); }
/* * calculate the MAC for the EAP message, given the key. * The "extra" will be appended to the EAP message and included in the * HMAC. * */ int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen, uint8_t calcmac[20]) { int ret; eap_packet_raw_t *e; uint8_t *buffer; int elen,len; VALUE_PAIR *mac; mac = fr_pair_find_by_num(rvps, PW_EAP_SIM_MAC, 0, TAG_ANY); if(!mac || mac->vp_length != 18) { /* can't check a packet with no AT_MAC attribute */ return 0; } /* get original copy of EAP message, note that it was sanitized * to have a valid length, which we depend upon. */ e = eap_vp2packet(ctx, rvps); if (!e) return 0; /* make copy big enough for everything */ elen = (e->length[0] * 256) + e->length[1]; len = elen + extralen; buffer = talloc_array(ctx, uint8_t, len); if (!buffer) { talloc_free(e); return 0; } memcpy(buffer, e, elen); memcpy(buffer + elen, extra, extralen); /* * now look for the AT_MAC attribute in the copy of the buffer * and make sure that the checksum is zero. * */ { uint8_t *attr; /* first attribute is 8 bytes into the EAP packet. * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved. */ attr = buffer+8; while(attr < (buffer+elen)) { if (attr[0] == (PW_EAP_SIM_MAC - PW_EAP_SIM_BASE)) { /* zero the data portion, after making sure * the size is >=5. Maybe future versions. * will use more bytes, so be liberal. */ if(attr[1] < 5) { ret = 0; goto done; } memset(&attr[4], 0, (attr[1]-1)*4); } /* advance the pointer */ attr += attr[1]*4; } } /* now, HMAC-SHA1 it with the key. */ fr_hmac_sha1(calcmac, buffer, len, key, 16); ret = memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0 ? 1 : 0; done: talloc_free(e); talloc_free(buffer); return(ret); }