/* * Authenticate a previously sent challenge. */ static int md5_authenticate(UNUSED void *arg, EAP_HANDLER *handler) { MD5_PACKET *packet; MD5_PACKET *reply; VALUE_PAIR *password; /* * Get the Cleartext-Password for this user. */ rad_assert(handler->request != NULL); rad_assert(handler->stage == AUTHENTICATE); password = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD); if (password == NULL) { DEBUG2("rlm_eap_md5: Cleartext-Password is required for EAP-MD5 authentication"); return 0; } /* * Extract the EAP-MD5 packet. */ if (!(packet = eapmd5_extract(handler->eap_ds))) return 0; /* * Create a reply, and initialize it. */ reply = eapmd5_alloc(); if (!reply) { eapmd5_free(&packet); return 0; } reply->id = handler->eap_ds->request->id; reply->length = 0; /* * Verify the received packet against the previous packet * (i.e. challenge) which we sent out. */ if (eapmd5_verify(packet, password, handler->opaque)) { reply->code = PW_MD5_SUCCESS; } else { reply->code = PW_MD5_FAILURE; } /* * Compose the EAP-MD5 packet out of the data structure, * and free it. */ eapmd5_compose(handler->eap_ds, reply); eapmd5_free(&packet); return 1; }
/* * Compose the portions of the reply packet specific to the * EAP-MD5 protocol, in the EAP reply typedata */ int eapmd5_compose(EAP_DS *eap_ds, MD5_PACKET *reply) { uint8_t *ptr; unsigned short name_len; /* * We really only send Challenge (EAP-Identity), * and EAP-Success, and EAP-Failure. */ if (reply->code < 3) { eap_ds->request->type.type = PW_EAP_MD5; rad_assert(reply->length > 0); eap_ds->request->type.data = malloc(reply->length); if (eap_ds->request->type.data == NULL) { eapmd5_free(&reply); radlog(L_ERR, "rlm_eap_md5: out of memory"); return 0; } ptr = eap_ds->request->type.data; *ptr++ = (uint8_t)(reply->value_size & 0xFF); memcpy(ptr, reply->value, reply->value_size); /* Just the Challenge length */ eap_ds->request->type.length = reply->value_size + 1; /* * Return the name, if necessary. * * Don't see why this is *ever* necessary... */ name_len = reply->length - (reply->value_size + 1); if (name_len && reply->name) { ptr += reply->value_size; memcpy(ptr, reply->name, name_len); /* Challenge length + Name length */ eap_ds->request->type.length += name_len; } } else { eap_ds->request->type.length = 0; /* TODO: In future we might add message here wrt rfc1994 */ } eap_ds->request->code = reply->code; eapmd5_free(&reply); return 1; }
/* * If an EAP MD5 request needs to be initiated then * create such a packet. */ MD5_PACKET *eapmd5_initiate(EAP_DS *eap_ds) { MD5_PACKET *reply; reply = eapmd5_alloc(); if (reply == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); return NULL; } reply->code = PW_MD5_CHALLENGE; reply->length = MD5_HEADER_LEN + 1/*value_size*/ + MD5_LEN; reply->value_size = MD5_LEN; reply->value = malloc(reply->value_size); if (reply->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&reply); return NULL; } eapmd5_challenge(reply->value, reply->value_size); return reply; }
/* * We expect only RESPONSE for which CHALLENGE, SUCCESS or FAILURE is sent back */ MD5_PACKET *eapmd5_extract(EAP_DS *eap_ds) { md5_packet_t *data; MD5_PACKET *packet; unsigned short name_len; if (!eap_ds || !eap_ds->response || (eap_ds->response->code != PW_MD5_RESPONSE) || eap_ds->response->type.type != PW_EAP_MD5 || !eap_ds->response->type.data || (eap_ds->response->length < MD5_HEADER_LEN) || (eap_ds->response->type.data[0] <= 0) ) { radlog(L_ERR, "rlm_eap_md5: corrupted data"); return NULL; } packet = eapmd5_alloc(); if (!packet) return NULL; /* * Code, id & length for MD5 & EAP are same * but md5_length = eap_length - 1(Type = 1 octet) */ packet->code = eap_ds->response->code; packet->id = eap_ds->response->id; packet->length = eap_ds->response->length - 1; packet->value_size = 0; packet->value = NULL; packet->name = NULL; data = (md5_packet_t *)eap_ds->response->type.data; packet->value_size = data->value_size; if (packet->value_size < 1) { radlog(L_ERR, "rlm_eap_md5: Value size is too small"); eapmd5_free(&packet); return NULL; } packet->value = malloc(packet->value_size); if (packet->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&packet); return NULL; } memcpy(packet->value, data->value_name, packet->value_size); /* * Name is optional and is present after Value, but we need to check for it */ name_len = packet->length - (packet->value_size + 5); if (name_len) { packet->name = malloc(name_len+1); if (!packet->name) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&packet); return NULL; } memset(packet->name, 0, name_len+1); memcpy(packet->name, data->value_name+packet->value_size, name_len); } return packet; }
/* * Identify whether the response that you got is either the * response to the challenge that we sent or a new one. * If it is a response to the request then issue success/failure * else issue a challenge */ MD5_PACKET *eapmd5_process(MD5_PACKET *packet, int id, VALUE_PAIR *username, VALUE_PAIR* password, md5_packet_t *request) { unsigned char output[MAX_STRING_LEN]; MD5_PACKET *reply; if (!username || !password || !packet) return NULL; reply = eapmd5_alloc(); if (!reply) return NULL; memset(output, 0, MAX_STRING_LEN); reply->id = id; if (request) { /* verify and issue Success/failure */ if (eapmd5_verify(packet, password, request) == 0) { radlog(L_INFO, "rlm_eap_md5: Challenge failed"); reply->code = PW_MD5_FAILURE; } else { reply->code = PW_MD5_SUCCESS; } } else { /* * Previous request not found. * Probably it is timed out. * So send another challenge. * TODO: Later Send these challenges for the configurable * number of times for each user & stop. */ /* * Ensure that the challenge is always of the correct * length. i.e. Don't take value size from data * supplied by the client. */ if (reply->value_size != MD5_LEN) { free(reply->value); reply->value_size = MD5_LEN; reply->value = malloc(reply->value_size); } eapmd5_challenge(reply->value, reply->value_size); reply->code = PW_MD5_CHALLENGE; radlog(L_INFO, "rlm_eap_md5: Previous request not found"); radlog(L_INFO, "rlm_eap_md5: Issuing Challenge to the user - %s", (char *)username->strvalue); } /* fill reply packet */ if (reply->code == PW_MD5_CHALLENGE) { reply->value_size = packet->value_size; reply->value = malloc(reply->value_size); if (reply->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&reply); return NULL; } memcpy(reply->value, output, reply->value_size); reply->length = packet->length; } else { reply->length = MD5_HEADER_LEN; } return reply; }
/* * Initiate the EAP-MD5 session by sending a challenge to the peer. */ static int md5_initiate(void *type_data, EAP_HANDLER *handler) { int i; MD5_PACKET *reply; type_data = type_data; /* -Wunused */ /* * Allocate an EAP-MD5 packet. */ reply = eapmd5_alloc(); if (reply == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); return 0; } /* * Fill it with data. */ reply->code = PW_MD5_CHALLENGE; reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */ reply->value_size = MD5_CHALLENGE_LEN; /* * Allocate user data. */ reply->value = malloc(reply->value_size); if (reply->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&reply); return 0; } /* * Get a random challenge. */ for (i = 0; i < reply->value_size; i++) { reply->value[i] = fr_rand(); } DEBUG2("rlm_eap_md5: Issuing Challenge"); /* * Keep track of the challenge. */ handler->opaque = malloc(reply->value_size); rad_assert(handler->opaque != NULL); memcpy(handler->opaque, reply->value, reply->value_size); handler->free_opaque = free; /* * Compose the EAP-MD5 packet out of the data structure, * and free it. */ eapmd5_compose(handler->eap_ds, reply); /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = AUTHENTICATE; return 1; }
/* * We expect only RESPONSE for which SUCCESS or FAILURE is sent back */ MD5_PACKET *eapmd5_extract(EAP_DS *eap_ds) { md5_packet_t *data; MD5_PACKET *packet; unsigned short name_len; /* * We need a response, of type EAP-MD5, with at least * one byte of type data (EAP-MD5) following the 4-byte * EAP-Packet header. */ if (!eap_ds || !eap_ds->response || (eap_ds->response->code != PW_MD5_RESPONSE) || eap_ds->response->type.type != PW_EAP_MD5 || !eap_ds->response->type.data || (eap_ds->response->length <= MD5_HEADER_LEN) || (eap_ds->response->type.data[0] <= 0)) { radlog(L_ERR, "rlm_eap_md5: corrupted data"); return NULL; } packet = eapmd5_alloc(); if (!packet) return NULL; /* * Code & id for MD5 & EAP are same * * but md5_length = length of the EAP-MD5 data, which * doesn't include the EAP header, or the octet saying * EAP-MD5. */ packet->code = eap_ds->response->code; packet->id = eap_ds->response->id; packet->length = eap_ds->response->length - (MD5_HEADER_LEN + 1); /* * Sanity check the EAP-MD5 packet sent to us * by the client. */ data = (md5_packet_t *)eap_ds->response->type.data; /* * Already checked the size above. */ packet->value_size = data->value_size; /* * Allocate room for the data, and copy over the data. */ packet->value = malloc(packet->value_size); if (packet->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&packet); return NULL; } memcpy(packet->value, data->value_name, packet->value_size); /* * Name is optional and is present after Value, but we * need to check for it, as eapmd5_compose() */ name_len = packet->length - (packet->value_size + 1); if (name_len) { packet->name = malloc(name_len + 1); if (!packet->name) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&packet); return NULL; } memcpy(packet->name, data->value_name + packet->value_size, name_len); packet->name[name_len] = 0; } return packet; }