rlm_eap_mschapv2.c [plain text]
#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/autoconf.h>
#include <stdio.h>
#include <stdlib.h>
#include "eap_mschapv2.h"
#include <freeradius-devel/rad_assert.h>
typedef struct rlm_eap_mschapv2_t {
int with_ntdomain_hack;
} rlm_eap_mschapv2_t;
static CONF_PARSER module_config[] = {
{ "with_ntdomain_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_eap_mschapv2_t,with_ntdomain_hack), NULL, "no" },
{ NULL, -1, 0, NULL, NULL }
};
static void fix_mppe_keys(EAP_HANDLER *handler, mschapv2_opaque_t *data)
{
pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 7));
pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 8));
pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 16));
pairmove2(&data->mppe_keys, &handler->request->reply->vps, ((311 << 16) | 17));
}
static void free_data(void *ptr)
{
mschapv2_opaque_t *data = ptr;
pairfree(&data->mppe_keys);
free(data);
}
static int mschapv2_detach(void *arg)
{
rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
free(inst);
return 0;
}
static int mschapv2_attach(CONF_SECTION *cs, void **instance)
{
rlm_eap_mschapv2_t *inst;
inst = malloc(sizeof(*inst));
if (!inst) {
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return -1;
}
memset(inst, 0, sizeof(*inst));
if (cf_section_parse(cs, inst, module_config) < 0) {
mschapv2_detach(inst);
return -1;
}
*instance = inst;
return 0;
}
static int eapmschapv2_compose(EAP_HANDLER *handler, VALUE_PAIR *reply)
{
uint8_t *ptr;
int16_t length;
mschapv2_header_t *hdr;
EAP_DS *eap_ds = handler->eap_ds;
eap_ds->request->code = PW_EAP_REQUEST;
eap_ds->request->type.type = PW_EAP_MSCHAPV2;
switch (reply->attribute) {
case PW_MSCHAP_CHALLENGE:
length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(handler->identity);
eap_ds->request->type.data = malloc(length);
if (eap_ds->request->type.data == NULL) {
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return 0;
}
eap_ds->request->type.length = length;
ptr = eap_ds->request->type.data;
hdr = (mschapv2_header_t *) ptr;
hdr->opcode = PW_EAP_MSCHAPV2_CHALLENGE;
hdr->mschapv2_id = eap_ds->response->id + 1;
length = htons(length);
memcpy(hdr->ms_length, &length, sizeof(uint16_t));
hdr->value_size = MSCHAPV2_CHALLENGE_LEN;
ptr += MSCHAPV2_HEADER_LEN;
memcpy(ptr, reply->vp_strvalue, reply->length);
memcpy((ptr + reply->length), handler->identity, strlen(handler->identity));
break;
case PW_MSCHAP2_SUCCESS:
DEBUG2("MSCHAP Success\n");
length = 46;
eap_ds->request->type.data = malloc(length);
if (eap_ds->request->type.data == NULL) {
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return 0;
}
memset(eap_ds->request->type.data, 0, length);
eap_ds->request->type.length = length;
eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_SUCCESS;
eap_ds->request->type.data[1] = eap_ds->response->id;
length = htons(length);
memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t));
memcpy((eap_ds->request->type.data + 4), reply->vp_strvalue + 1, 42);
break;
case PW_MSCHAP_ERROR:
DEBUG2("MSCHAP Failure\n");
length = 4 + MSCHAPV2_FAILURE_MESSAGE_LEN;
eap_ds->request->type.data = malloc(length);
if (eap_ds->request->type.data == NULL) {
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return 0;
}
memset(eap_ds->request->type.data, 0, length);
eap_ds->request->type.length = length;
eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_FAILURE;
eap_ds->request->type.data[1] = eap_ds->response->id;
length = htons(length);
memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t));
memcpy((eap_ds->request->type.data + 4), MSCHAPV2_FAILURE_MESSAGE, MSCHAPV2_FAILURE_MESSAGE_LEN);
break;
default:
radlog(L_ERR, "rlm_eap_mschapv2: Internal sanity check failed");
return 0;
break;
}
return 1;
}
static int mschapv2_initiate(void *type_data, EAP_HANDLER *handler)
{
int i;
VALUE_PAIR *challenge;
mschapv2_opaque_t *data;
type_data = type_data;
challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
if (!challenge) {
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return 0;
}
challenge->length = MSCHAPV2_CHALLENGE_LEN;
for (i = 0; i < MSCHAPV2_CHALLENGE_LEN; i++) {
challenge->vp_strvalue[i] = fr_rand();
}
DEBUG2("rlm_eap_mschapv2: Issuing Challenge");
data = malloc(sizeof(mschapv2_opaque_t));
rad_assert(data != NULL);
data->code = PW_EAP_MSCHAPV2_CHALLENGE;
memcpy(data->challenge, challenge->vp_strvalue, MSCHAPV2_CHALLENGE_LEN);
data->mppe_keys = NULL;
handler->opaque = data;
handler->free_opaque = free_data;
eapmschapv2_compose(handler, challenge);
pairfree(&challenge);
#ifdef WITH_PROXY
handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
#endif
handler->stage = AUTHENTICATE;
return 1;
}
#ifdef WITH_PROXY
static int mschap_postproxy(EAP_HANDLER *handler, void *tunnel_data)
{
VALUE_PAIR *response = NULL;
mschapv2_opaque_t *data;
data = (mschapv2_opaque_t *) handler->opaque;
rad_assert(data != NULL);
tunnel_data = tunnel_data;
DEBUG2(" rlm_eap_mschapv2: Passing reply from proxy back into the tunnel %p %d.",
handler->request, handler->request->reply->code);
switch (handler->request->reply->code) {
case PW_AUTHENTICATION_ACK:
DEBUG(" rlm_eap_mschapv2: Authentication succeeded.");
pairmove2(&response,
&handler->request->reply->vps,
PW_MSCHAP2_SUCCESS);
break;
default:
case PW_AUTHENTICATION_REJECT:
DEBUG(" rlm_eap_mschapv2: Authentication did not succeed.");
return 0;
}
if (!response) {
radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found.");
return 0;
}
handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
eapmschapv2_compose(handler, response);
data->code = PW_EAP_MSCHAPV2_SUCCESS;
fix_mppe_keys(handler, data);
handler->request->reply->code = PW_ACCESS_CHALLENGE;
pairfree(&response);
return 1;
}
#endif
static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler)
{
int rcode;
mschapv2_opaque_t *data;
EAP_DS *eap_ds = handler->eap_ds;
VALUE_PAIR *challenge, *response, *name;
rad_assert(handler->request != NULL);
rad_assert(handler->stage == AUTHENTICATE);
data = (mschapv2_opaque_t *) handler->opaque;
if (eap_ds->response->length <= 4) {
radlog(L_ERR, "rlm_eap_mschapv2: corrupted data");
return 0;
}
switch (eap_ds->response->type.data[0]) {
case PW_EAP_MSCHAPV2_ACK:
if (data->code != PW_EAP_MSCHAPV2_SUCCESS) {
radlog(L_ERR, "rlm_eap_mschapv2: Unexpected ACK received");
return 0;
}
handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
return 1;
break;
case PW_EAP_MSCHAPV2_RESPONSE:
if (data->code != PW_EAP_MSCHAPV2_CHALLENGE) {
radlog(L_ERR, "rlm_eap_mschapv2: Unexpected response received");
return 0;
}
if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) {
radlog(L_ERR, "rlm_eap_mschapv2: Response is too short");
return 0;
}
if (eap_ds->response->type.data[4] != 49) {
radlog(L_ERR, "rlm_eap_mschapv2: Response is of incorrect length %d", eap_ds->response->type.data[4]);
return 0;
}
if (((eap_ds->response->type.data[2] << 8) |
eap_ds->response->type.data[3]) < (5 + 49)) {
radlog(L_ERR, "rlm_eap_mschapv2: Response contains contradictory length %d %d",
(eap_ds->response->type.data[2] << 8) |
eap_ds->response->type.data[3], 5 + 49);
return 0;
}
break;
case PW_EAP_MSCHAPV2_SUCCESS:
if (data->code != PW_EAP_MSCHAPV2_SUCCESS) {
radlog(L_ERR, "rlm_eap_mschapv2: Unexpected success received");
return 0;
}
#ifdef WITH_PROXY
handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
#endif
eap_ds->request->code = PW_EAP_SUCCESS;
pairadd(&handler->request->reply->vps, data->mppe_keys);
data->mppe_keys = NULL;
return 1;
break;
default:
radlog(L_ERR, "rlm_eap_mschapv2: Invalid response type %d",
eap_ds->response->type.data[0]);
return 0;
}
challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ);
if (!challenge) {
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return 0;
}
challenge->length = MSCHAPV2_CHALLENGE_LEN;
memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN);
response = pairmake("MS-CHAP2-Response", "0x00", T_OP_EQ);
if (!response) {
pairfree(&challenge);
radlog(L_ERR, "rlm_eap_mschapv2: out of memory");
return 0;
}
response->length = MSCHAPV2_RESPONSE_LEN;
memcpy(response->vp_strvalue + 2, &eap_ds->response->type.data[5],
MSCHAPV2_RESPONSE_LEN - 2);
response->vp_strvalue[0] = eap_ds->response->type.data[1];
response->vp_strvalue[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];
name = pairmake("NTLM-User-Name", "", T_OP_EQ);
if (!name) {
pairfree(&challenge);
pairfree(&response);
radlog(L_ERR, "rlm_eap_mschapv2: Failed creating NTLM-User-Name: %s", fr_strerror());
return 0;
}
name->length = (((eap_ds->response->type.data[2] << 8) |
eap_ds->response->type.data[3]) -
eap_ds->response->type.data[4] - 5);
if (name->length >= sizeof(name->vp_strvalue)) {
name->length = sizeof(name->vp_strvalue) - 1;
}
memcpy(name->vp_strvalue,
&eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
name->length);
name->vp_strvalue[name->length] = '\0';
pairadd(&handler->request->packet->vps, challenge);
pairadd(&handler->request->packet->vps, response);
pairadd(&handler->request->packet->vps, name);
#ifdef WITH_PROXY
if (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
char *username = NULL;
eap_tunnel_data_t *tunnel;
rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
tunnel = rad_malloc(sizeof(*tunnel));
memset(tunnel, 0, sizeof(*tunnel));
tunnel->tls_session = arg;
tunnel->callback = mschap_postproxy;
rcode = request_data_add(handler->request,
handler->request->proxy,
REQUEST_DATA_EAP_TUNNEL_CALLBACK,
tunnel, free);
rad_assert(rcode == 0);
pairdelete(&handler->request->packet->vps, PW_STATE);
if (inst->with_ntdomain_hack &&
((challenge = pairfind(handler->request->packet->vps,
PW_USER_NAME)) != NULL) &&
((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) {
username++;
memmove(challenge->vp_strvalue,
username,
strlen(username) + 1);
challenge->length = strlen(challenge->vp_strvalue);
}
return 1;
}
#endif
rcode = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->request);
fix_mppe_keys(handler, data);
response = NULL;
if (rcode == RLM_MODULE_OK) {
pairmove2(&response, &handler->request->reply->vps,
PW_MSCHAP2_SUCCESS);
data->code = PW_EAP_MSCHAPV2_SUCCESS;
} else {
eap_ds->request->code = PW_EAP_FAILURE;
return 1;
#if 0
pairmove2(&handler->request->reply->vps, &response
PW_MSCHAP_ERROR);
data->code = PW_EAP_MSCHAPV2_FAILURE;
#endif
}
if (!response) {
radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found.");
return 0;
}
eapmschapv2_compose(handler, response);
pairfree(&response);
return 1;
}
EAP_TYPE rlm_eap_mschapv2 = {
"eap_mschapv2",
mschapv2_attach,
mschapv2_initiate,
NULL,
mschapv2_authenticate,
mschapv2_detach
};