📄 eap.c
字号:
return RLM_MODULE_HANDLED; } /* Should never enter here */ radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code); request->reply->code = PW_AUTHENTICATION_REJECT; reply->code = PW_EAP_FAILURE; rcode = RLM_MODULE_REJECT; break; } return rcode;}/* * Radius criteria, EAP-Message is invalid without Message-Authenticator * For EAP_START, send Access-Challenge with EAP Identity request. */int eap_start(rlm_eap_t *inst, REQUEST *request){ VALUE_PAIR *vp, *proxy; VALUE_PAIR *eap_msg; eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE); if (eap_msg == NULL) { RDEBUG2("No EAP-Message, not doing EAP"); return EAP_NOOP; } /* * Look for EAP-Type = None (FreeRADIUS specific attribute) * this allows you to NOT do EAP for some users. */ vp = pairfind(request->packet->vps, PW_EAP_TYPE); if (vp && vp->vp_integer == 0) { RDEBUG2("Found EAP-Message, but EAP-Type = None, so we're not doing EAP."); return EAP_NOOP; } /* * http://www.freeradius.org/rfc/rfc2869.html#EAP-Message * * Checks for Message-Authenticator are handled by rad_recv(). */ /* * Check for a Proxy-To-Realm. Don't get excited over LOCAL * realms (sigh). */ proxy = pairfind(request->config_items, PW_PROXY_TO_REALM); if (proxy) { REALM *realm; /* * If it's a LOCAL realm, then we're not proxying * to it. */ realm = realm_find(proxy->vp_strvalue); if (realm && (realm->auth_pool == NULL)) { proxy = NULL; } } /* * Check the length before de-referencing the contents. * * Lengths of zero are required by the RFC for EAP-Start, * but we've never seen them in practice. * * Lengths of two are what we see in practice as * EAP-Starts. */ if ((eap_msg->length == 0) || (eap_msg->length == 2)) { EAP_DS *eap_ds; EAP_HANDLER handler; /* * It's a valid EAP-Start, but the request * was marked as being proxied. So we don't * do EAP, as the home server will do it. */ if (proxy) { do_proxy: RDEBUG2("Request is supposed to be proxied to Realm %s. Not doing EAP.", proxy->vp_strvalue); return EAP_NOOP; } RDEBUG2("Got EAP_START message"); if ((eap_ds = eap_ds_alloc()) == NULL) { RDEBUG2("EAP Start failed in allocation"); return EAP_FAIL; } /* * It's an EAP-Start packet. Tell them to stop wasting * our time, and give us an EAP-Identity packet. * * Hmm... we should probably check the contents of the * EAP-Start packet for something... */ eap_ds->request->code = PW_EAP_REQUEST; eap_ds->request->type.type = PW_EAP_IDENTITY; /* * We don't have a handler, but eap_compose needs one, * (for various reasons), so we fake it out here. */ memset(&handler, 0, sizeof(handler)); handler.request = request; handler.eap_ds = eap_ds; eap_compose(&handler); eap_ds_free(&eap_ds); return EAP_FOUND; } /* end of handling EAP-Start */ /* * The EAP packet header is 4 bytes, plus one byte of * EAP sub-type. Short packets are discarded, unless * we're proxying. */ if (eap_msg->length < (EAP_HEADER_LEN + 1)) { if (proxy) goto do_proxy; RDEBUG2("Ignoring EAP-Message which is too short to be meaningful."); return EAP_FAIL; } /* * Create an EAP-Type containing the EAP-type * from the packet. */ vp = paircreate(PW_EAP_TYPE, PW_TYPE_INTEGER); if (vp) { vp->vp_integer = eap_msg->vp_octets[4]; pairadd(&(request->packet->vps), vp); } /* * If the request was marked to be proxied, do it now. * This is done after checking for a valid length * (which may not be good), and after adding the EAP-Type * attribute. This lets other modules selectively cancel * proxying based on EAP-Type. */ if (proxy) goto do_proxy; /* * From now on, we're supposed to be handling the * EAP packet. We better understand it... */ /* * We're allowed only a few codes. Request, Response, * Success, or Failure. */ if ((eap_msg->vp_octets[0] == 0) || (eap_msg->vp_octets[0] > PW_EAP_MAX_CODES)) { RDEBUG2("Unknown EAP packet"); } else { RDEBUG2("EAP packet type %s id %d length %d", eap_codes[eap_msg->vp_octets[0]], eap_msg->vp_octets[1], eap_msg->length); } /* * We handle request and responses. The only other defined * codes are success and fail. The client SHOULD NOT be * sending success/fail packets to us, as it doesn't make * sense. */ if ((eap_msg->vp_octets[0] != PW_EAP_REQUEST) && (eap_msg->vp_octets[0] != PW_EAP_RESPONSE)) { RDEBUG2("Ignoring EAP packet which we don't know how to handle."); return EAP_FAIL; } /* * We've been told to ignore unknown EAP types, AND it's * an unknown type. Return "NOOP", which will cause the * eap_authorize() to return NOOP. * * EAP-Identity, Notification, and NAK are all handled * internally, so they never have handlers. */ if ((eap_msg->vp_octets[4] >= PW_EAP_MD5) && inst->ignore_unknown_eap_types && ((eap_msg->vp_octets[4] == 0) || (eap_msg->vp_octets[4] > PW_EAP_MAX_TYPES) || (inst->types[eap_msg->vp_octets[4]] == NULL))) { RDEBUG2(" Ignoring Unknown EAP type"); return EAP_NOOP; } /* * They're NAKing the EAP type we wanted to use, and * asking for one which we don't support. * * NAK is code + id + length1 + length + NAK * + requested EAP type(s). * * We know at this point that we can't handle the * request. We could either return an EAP-Fail here, but * it's not too critical. * * By returning "noop", we can ensure that authorize() * returns NOOP, and another module may choose to proxy * the request. */ if ((eap_msg->vp_octets[4] == PW_EAP_NAK) && (eap_msg->length >= (EAP_HEADER_LEN + 2)) && inst->ignore_unknown_eap_types && ((eap_msg->vp_octets[5] == 0) || (eap_msg->vp_octets[5] > PW_EAP_MAX_TYPES) || (inst->types[eap_msg->vp_octets[5]] == NULL))) { RDEBUG2("Ignoring NAK with request for unknown EAP type"); return EAP_NOOP; } if ((eap_msg->vp_octets[4] == PW_EAP_TTLS) || (eap_msg->vp_octets[4] == PW_EAP_PEAP)) { RDEBUG2("Continuing tunnel setup."); return EAP_OK; } /* * Later EAP messages are longer than the 'start' * message, so if everything is OK, this function returns * 'no start found', so that the rest of the EAP code can * use the State attribute to match this EAP-Message to * an ongoing conversation. */ RDEBUG2("No EAP Start, assuming it's an on-going EAP conversation"); return EAP_NOTFOUND;}/* * compose EAP FAILURE packet in EAP-Message */void eap_fail(EAP_HANDLER *handler){ /* * Delete any previous replies. */ pairdelete(&handler->request->reply->vps, PW_EAP_MESSAGE); pairdelete(&handler->request->reply->vps, PW_STATE); eap_packet_free(&handler->eap_ds->request); handler->eap_ds->request = eap_packet_alloc(); handler->eap_ds->request->code = PW_EAP_FAILURE; eap_compose(handler);}/* * compose EAP SUCCESS packet in EAP-Message */void eap_success(EAP_HANDLER *handler){ handler->eap_ds->request->code = PW_EAP_SUCCESS; eap_compose(handler);}/* * Basic EAP packet verfications & validations */static int eap_validation(REQUEST *request, eap_packet_t *eap_packet){ uint16_t len; memcpy(&len, eap_packet->length, sizeof(uint16_t)); len = ntohs(len); /* * High level EAP packet checks */ if ((len <= EAP_HEADER_LEN) || ((eap_packet->code != PW_EAP_RESPONSE) && (eap_packet->code != PW_EAP_REQUEST)) || (eap_packet->data[0] <= 0) || (eap_packet->data[0] > PW_EAP_MAX_TYPES)) { radlog_request(L_AUTH, 0, request, "Badly formatted EAP Message: Ignoring the packet"); return EAP_INVALID; } /* we don't expect notification, but we send it */ if (eap_packet->data[0] == PW_EAP_NOTIFICATION) { radlog_request(L_AUTH, 0, request, "Got NOTIFICATION, " "Ignoring the packet"); return EAP_INVALID; } return EAP_VALID;}/* * Get the user Identity only from EAP-Identity packets */static char *eap_identity(REQUEST *request, eap_packet_t *eap_packet){ int size; uint16_t len; char *identity; if ((eap_packet == NULL) || (eap_packet->code != PW_EAP_RESPONSE) || (eap_packet->data[0] != PW_EAP_IDENTITY)) { return NULL; } memcpy(&len, eap_packet->length, sizeof(uint16_t)); len = ntohs(len); if ((len <= 5) || (eap_packet->data[1] == 0x00)) { RDEBUG("UserIdentity Unknown "); return NULL; } size = len - 5; identity = rad_malloc(size + 1); memcpy(identity, &eap_packet->data[1], size); identity[size] = '\0'; return identity;}/* * Create our Request-Response data structure with the eap packet */static EAP_DS *eap_buildds(eap_packet_t **eap_packet_p){ EAP_DS *eap_ds = NULL; eap_packet_t *eap_packet = *eap_packet_p; int typelen; uint16_t len; if ((eap_ds = eap_ds_alloc()) == NULL) { return NULL; } eap_ds->response->packet = (unsigned char *)eap_packet; eap_ds->response->code = eap_packet->code; eap_ds->response->id = eap_packet->id; eap_ds->response->type.type = eap_packet->data[0]; memcpy(&len, eap_packet->length, sizeof(uint16_t)); len = ntohs(len); eap_ds->response->length = len; /* * We've eaten the eap packet into the eap_ds. */ *eap_packet_p = NULL; /* * First 5 bytes in eap, are code + id + length(2) + type. * * The rest is type-specific data. We skip type while * getting typedata from data. */ typelen = len - 5/*code + id + length + type */; if (typelen > 0) { /* * Since the packet contains the complete * eap_packet, typedata will be a ptr in packet * to its typedata */ eap_ds->response->type.data = eap_ds->response->packet + 5/*code+id+length+type*/; eap_ds->response->type.length = typelen; } else { eap_ds->response->type.length = 0; eap_ds->response->type.data = NULL; } return eap_ds;}/* * If identity response then create a fresh handler & fill the identity * else handler MUST be in our list, get that. * This handler creation cannot fail * * username contains REQUEST->username which might have been stripped. * identity contains the one sent in EAP-Identity response */EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p, REQUEST *request){ EAP_HANDLER *handler = NULL; eap_packet_t *eap_packet = *eap_packet_p; VALUE_PAIR *vp; /* * Ensure it's a valid EAP-Request, or EAP-Response. */ if (eap_validation(request, eap_packet) == EAP_INVALID) { free(*eap_packet_p); *eap_packet_p = NULL; return NULL; } /* * EAP_HANDLER MUST be found in the list if it is not * EAP-Identity response */ if (eap_packet->data[0] != PW_EAP_IDENTITY) { handler = eaplist_find(inst, request, eap_packet); if (handler == NULL) { /* Either send EAP_Identity or EAP-Fail */ RDEBUG("Either EAP-request timed out OR" " EAP-response to an unknown EAP-request"); free(*eap_packet_p); *eap_packet_p = NULL; return NULL; } /* * Even more paranoia. Without this, some weird * clients could do crazy things. * * It's ok to send EAP sub-type NAK in response * to a request for a particular type, but it's NOT * OK to blindly return data for another type. */ if ((eap_packet->data[0] != PW_EAP_NAK) && (eap_packet->data[0] != handler->eap_type)) { RDEBUG("Response appears to match, but EAP type is wrong."); free(*eap_packet_p); *eap_packet_p = NULL; return NULL; } vp = pairfind(request->packet->vps, PW_USER_NAME); if (!vp) { /* * NAS did not set the User-Name * attribute, so we set it here and * prepend it to the beginning of the * request vps so that autz's work * correctly */ RDEBUG2("Broken NAS did not set User-Name, setting from EAP Identity"); vp = pairmake("User-Name", handler->identity, T_OP_EQ); if (vp == NULL) { RDEBUG("Out of memory"); free(*eap_packet_p); *eap_packet_p = NULL; return NULL; } vp->next = request->packet->vps; request->packet->vps = vp; } else { /* * A little more paranoia. If the NAS * *did* set the User-Name, and it doesn't * match the identity, (i.e. If they * change their User-Name part way through * the EAP transaction), then reject the * request as the NAS is doing something * funny. */ if (strncmp(handler->identity, vp->vp_strvalue, MAX_STRING_LEN) != 0) { RDEBUG("Identity does not match User-Name. Authentication failed."); free(*eap_packet_p); *eap_packet_p = NULL; return NULL; } } } else { /* packet was EAP identity */ handler = eap_handler_alloc(); if (handler == NULL) { RDEBUG("Out of memory."); free(*eap_packet_p); *eap_packet_p = NULL; return NULL; } /* * All fields in the handler are set to zero. */ handler->identity = eap_identity(request, eap_packet); if (handler->identity == NULL) { RDEBUG("Identity Unknown, authentication failed"); free(*eap_packet_p); *eap_packet_p = NULL; eap_handler_free(handler); return NULL; } vp = pairfind(request->packet->vps, PW_USER_NAME); if (!vp) { /* * NAS did not set the User-Name * attribute, so we set it here and * prepend it to the beginning of the * request vps so that autz's work * correctly */ RDEBUG2("WARNING NAS did not set User-Name. Setting it locally from EAP Identity"); vp = pairmake("User-Name", handler->identity, T_OP_EQ); if (vp == NULL) { RDEBUG("Out of memory"); free(*eap_packet_p); *eap_packet_p = NULL; eap_handler_free(handler); return NULL; } vp->next = request->packet->vps; request->packet->vps = vp; } else { /* * Paranoia. If the NAS *did* set the * User-Name, and it doesn't match the * identity, the NAS is doing something * funny, so reject the request. */ if (strncmp(handler->identity, vp->vp_strvalue, MAX_STRING_LEN) != 0) { RDEBUG("Identity does not match User-Name, setting from EAP Identity."); free(*eap_packet_p); *eap_packet_p = NULL; eap_handler_free(handler); return NULL; } } } handler->eap_ds = eap_buildds(eap_packet_p); if (handler->eap_ds == NULL) { free(*eap_packet_p); *eap_packet_p = NULL; eap_handler_free(handler); return NULL; } handler->timestamp = request->timestamp; handler->request = request; /* LEAP needs this */ return handler;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -