/* * Check password. * * Returns: 0 OK * -1 Password fail * -2 Rejected (Auth-Type = Reject, send Port-Message back) * 1 End check & return, don't reply * * NOTE: NOT the same as the RLM_ values ! */ static int rad_check_password(REQUEST *request) { VALUE_PAIR *auth_type_pair; VALUE_PAIR *cur_config_item; VALUE_PAIR *password_pair; VALUE_PAIR *auth_item; uint8_t my_chap[MAX_STRING_LEN]; int auth_type = -1; int result; int auth_type_count = 0; result = 0; /* * Look for matching check items. We skip the whole lot * if the authentication type is PW_AUTHTYPE_ACCEPT or * PW_AUTHTYPE_REJECT. */ cur_config_item = request->config_items; while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE))) != NULL) { auth_type = auth_type_pair->vp_integer; auth_type_count++; RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, auth_type_pair->vp_integer)); cur_config_item = auth_type_pair->next; if (auth_type == PW_AUTHTYPE_REJECT) { RDEBUG2("Auth-Type = Reject, rejecting user"); return -2; } } if (( auth_type_count > 1) && (debug_flag)) { radlog_request(L_ERR, 0, request, "Warning: Found %d auth-types on request for user '%s'", auth_type_count, request->username->vp_strvalue); } /* * This means we have a proxy reply or an accept * and it wasn't rejected in the above loop. So * that means it is accepted and we do no further * authentication */ if ((auth_type == PW_AUTHTYPE_ACCEPT) #ifdef WITH_PROXY || (request->proxy) #endif ) { RDEBUG2("Auth-Type = Accept, accepting the user"); return 0; } password_pair = pairfind(request->config_items, PW_USER_PASSWORD); if (password_pair && pairfind(request->config_items, PW_CLEARTEXT_PASSWORD)) { pairdelete(&request->config_items, PW_USER_PASSWORD); password_pair = NULL; } if (password_pair) { DICT_ATTR *da; RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); RDEBUG("!!! Replacing User-Password in config items with Cleartext-Password. !!!"); RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); RDEBUG("!!! Please update your configuration so that the \"known good\" !!!"); RDEBUG("!!! clear text password is in Cleartext-Password, and not in User-Password. !!!"); RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); password_pair->attribute = PW_CLEARTEXT_PASSWORD; da = dict_attrbyvalue(PW_CLEARTEXT_PASSWORD); if (!da) { radlog_request(L_ERR, 0, request, "FATAL: You broke the dictionaries. Please use the default dictionaries!"); _exit(1); } password_pair->name = da->name; } /* * Find the "known good" password. * * FIXME: We should get rid of these hacks, and replace * them with a module. */ if ((password_pair = pairfind(request->config_items, PW_CRYPT_PASSWORD)) != NULL) { /* * Re-write Auth-Type, but ONLY if it isn't already * set. */ if (auth_type == -1) auth_type = PW_AUTHTYPE_CRYPT; } else { password_pair = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); } if (auth_type < 0) { if (password_pair) { auth_type = PW_AUTHTYPE_LOCAL; } else { /* * The admin hasn't told us how to * authenticate the user, so we reject them! * * This is fail-safe. */ RDEBUG2("ERROR: No authenticate method (Auth-Type) found for the request: Rejecting the user"); return -2; } } switch(auth_type) { case PW_AUTHTYPE_CRYPT: RDEBUG2("WARNING: Please update your configuration, and remove 'Auth-Type = Crypt'"); RDEBUG2("WARNING: Use the PAP module instead."); /* * Find the password sent by the user. It * SHOULD be there, if it's not * authentication fails. */ auth_item = request->password; if (auth_item == NULL) { RDEBUG2("No User-Password or CHAP-Password attribute in the request"); return -1; } if (password_pair == NULL) { RDEBUG2("No Crypt-Password configured for the user"); rad_authlog("Login incorrect " "(No Crypt-Password configured for the user)", request, 0); return -1; } switch (fr_crypt_check((char *)auth_item->vp_strvalue, (char *)password_pair->vp_strvalue)) { case -1: rad_authlog("Login incorrect " "(system failed to supply an encrypted password for comparison)", request, 0); /* FALL-THROUGH */ case 1: return -1; } break; case PW_AUTHTYPE_LOCAL: RDEBUG2("WARNING: Please update your configuration, and remove 'Auth-Type = Local'"); RDEBUG2("WARNING: Use the PAP or CHAP modules instead."); /* * Find the password sent by the user. It * SHOULD be there, if it's not * authentication fails. */ auth_item = request->password; if (!auth_item) auth_item = pairfind(request->packet->vps, PW_CHAP_PASSWORD); if (!auth_item) { RDEBUG2("No User-Password or CHAP-Password attribute in the request."); RDEBUG2("Cannot perform authentication."); return -1; } /* * Plain text password. */ if (password_pair == NULL) { RDEBUG2("No \"known good\" password was configured for the user."); RDEBUG2("As a result, we cannot authenticate the user."); rad_authlog("Login incorrect " "(No password configured for the user)", request, 0); return -1; } /* * Local password is just plain text. */ if (auth_item->attribute == PW_USER_PASSWORD) { if (strcmp((char *)password_pair->vp_strvalue, (char *)auth_item->vp_strvalue) != 0) { RDEBUG2("User-Password in the request does NOT match \"known good\" password."); return -1; } RDEBUG2("User-Password in the request is correct."); break; } else if (auth_item->attribute != PW_CHAP_PASSWORD) { RDEBUG2("The user did not supply a User-Password or a CHAP-Password attribute"); rad_authlog("Login incorrect " "(no User-Password or CHAP-Password attribute)", request, 0); return -1; } rad_chap_encode(request->packet, my_chap, auth_item->vp_octets[0], password_pair); /* * Compare them */ if (memcmp(my_chap + 1, auth_item->vp_strvalue + 1, CHAP_VALUE_LENGTH) != 0) { RDEBUG2("CHAP-Password is incorrect."); return -1; } RDEBUG2("CHAP-Password is correct."); break; default: /* * See if there is a module that handles * this type, and turn the RLM_ return * status into the values as defined at * the top of this function. */ result = module_authenticate(auth_type, request); switch (result) { /* * An authentication module FAIL * return code, or any return code that * is not expected from authentication, * is the same as an explicit REJECT! */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_UPDATED: case RLM_MODULE_USERLOCK: default: result = -1; break; case RLM_MODULE_OK: result = 0; break; case RLM_MODULE_HANDLED: result = 1; break; } break; } return result; }
/* * Check password. * * Returns: 0 OK * -1 Password fail * -2 Rejected (Auth-Type = Reject, send Port-Message back) * 1 End check & return, don't reply * * NOTE: NOT the same as the RLM_ values ! */ static int rad_check_password(REQUEST *request) { VALUE_PAIR *auth_type_pair; VALUE_PAIR *cur_config_item; int auth_type = -1; int result; int auth_type_count = 0; result = 0; /* * Look for matching check items. We skip the whole lot * if the authentication type is PW_AUTHTYPE_ACCEPT or * PW_AUTHTYPE_REJECT. */ cur_config_item = request->config_items; while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE, 0, TAG_ANY))) != NULL) { auth_type = auth_type_pair->vp_integer; auth_type_count++; RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type)); cur_config_item = auth_type_pair->next; if (auth_type == PW_AUTHTYPE_REJECT) { RDEBUG2("Auth-Type = Reject, rejecting user"); return -2; } } /* * Warn if more than one Auth-Type was found, because only the last * one found will actually be used. */ if (( auth_type_count > 1) && (debug_flag)) { radlog_request(L_ERR, 0, request, "Warning: Found %d auth-types on request for user '%s'", auth_type_count, request->username->vp_strvalue); } /* * This means we have a proxy reply or an accept and it wasn't * rejected in the above loop. So that means it is accepted and we * do no further authentication. */ if ((auth_type == PW_AUTHTYPE_ACCEPT) #ifdef WITH_PROXY || (request->proxy) #endif ) { RDEBUG2("Auth-Type = Accept, accepting the user"); return 0; } /* * Check that Auth-Type has been set, and reject if not. * * Do quick checks to see if Cleartext-Password or Crypt-Password have * been set, and complain if so. */ if (auth_type < 0) { if (pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) { RDEBUG2W("Please update your configuration, and remove 'Auth-Type = Crypt'"); RDEBUG2W("Use the PAP module instead."); } else if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) { RDEBUG2W("Please update your configuration, and remove 'Auth-Type = Local'"); RDEBUG2W("Use the PAP or CHAP modules instead."); } /* * The admin hasn't told us how to * authenticate the user, so we reject them! * * This is fail-safe. */ RDEBUG2E("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject"); return -2; } /* * See if there is a module that handles * this Auth-Type, and turn the RLM_ return * status into the values as defined at * the top of this function. */ result = module_authenticate(auth_type, request); switch (result) { /* * An authentication module FAIL * return code, or any return code that * is not expected from authentication, * is the same as an explicit REJECT! */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_UPDATED: case RLM_MODULE_USERLOCK: default: result = -1; break; case RLM_MODULE_OK: result = 0; break; case RLM_MODULE_HANDLED: result = 1; break; } return result; }
/* * Authenticate a previously sent challenge. */ static int gtc_authenticate(void *type_data, EAP_HANDLER *handler) { VALUE_PAIR *vp; EAP_DS *eap_ds = handler->eap_ds; rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) type_data; REQUEST *request = handler->request; /* * Get the Cleartext-Password for this user. */ rad_assert(handler->request != NULL); rad_assert(handler->stage == AUTHENTICATE); /* * Sanity check the response. We need at least one byte * of data. */ if (eap_ds->response->length <= 4) { radlog(L_ERR, "rlm_eap_gtc: corrupted data"); eap_ds->request->code = PW_EAP_FAILURE; return 0; } #if 0 if ((debug_flag > 2) && fr_log_fp) { int i; for (i = 0; i < eap_ds->response->length - 4; i++) { if ((i & 0x0f) == 0) fprintf(fr_log_fp, "%d: ", i); fprintf(fr_log_fp, "%02x ", eap_ds->response->type.data[i]); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); } } #endif /* * Handle passwords here. */ if (inst->auth_type == PW_AUTHTYPE_LOCAL) { /* * For now, do clear-text password authentication. */ vp = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY); if (!vp) { RDEBUG2E("Cleartext-Password is required for authentication."); eap_ds->request->code = PW_EAP_FAILURE; return 0; } if (eap_ds->response->type.length != vp->length) { RDEBUG2E("Passwords are of different length. %u %u", (unsigned) eap_ds->response->type.length, (unsigned) vp->length); eap_ds->request->code = PW_EAP_FAILURE; return 0; } if (memcmp(eap_ds->response->type.data, vp->vp_strvalue, vp->length) != 0) { RDEBUG2E("Passwords are different"); eap_ds->request->code = PW_EAP_FAILURE; return 0; } /* * EAP packets can be ~64k long maximum, and * we don't like that. */ } else if (eap_ds->response->type.length <= 128) { int rcode; /* * If there was a User-Password in the request, * why the heck are they using EAP-GTC? */ pairdelete(&handler->request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); vp = pairmake("User-Password", "", T_OP_EQ); if (!vp) { radlog(L_ERR, "rlm_eap_gtc: out of memory"); return 0; } vp->length = eap_ds->response->type.length; memcpy(vp->vp_strvalue, eap_ds->response->type.data, vp->length); vp->vp_strvalue[vp->length] = 0; /* * Add the password to the request, and allow * another module to do the work of authenticating it. */ pairadd(&handler->request->packet->vps, vp); handler->request->password = vp; /* * This is a wild & crazy hack. */ rcode = module_authenticate(inst->auth_type, handler->request); if (rcode != RLM_MODULE_OK) { eap_ds->request->code = PW_EAP_FAILURE; return 0; } } else { radlog(L_ERR, "rlm_eap_gtc: Response is too large to understand"); eap_ds->request->code = PW_EAP_FAILURE; return 0; } eap_ds->request->code = PW_EAP_SUCCESS; return 1; }
/* * Authenticate a previously sent challenge. */ static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler) { int rcode, ccode; mschapv2_opaque_t *data; EAP_DS *eap_ds = handler->eap_ds; VALUE_PAIR *challenge, *response, *name; rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg; rad_assert(handler->request != NULL); rad_assert(handler->stage == AUTHENTICATE); data = (mschapv2_opaque_t *) handler->opaque; /* * Sanity check the response. */ if (eap_ds->response->length <= 5) { radlog(L_ERR, "rlm_eap_mschapv2: corrupted data"); return 0; } ccode = eap_ds->response->type.data[0]; switch (data->code) { case PW_EAP_MSCHAPV2_FAILURE: if (ccode == PW_EAP_MSCHAPV2_RESPONSE) { DEBUG2(" rlm_eap_mschapv2: authentication re-try from client after we sent a failure"); break; } /* * if we sent error 648 (password expired) to the client * we might get an MSCHAP-CPW packet here; turn it into a * regular MS-CHAP2-CPW packet and pass it to rlm_mschap * (or proxy it, I guess) */ if (ccode == PW_EAP_MSCHAPV2_CHGPASSWD) { VALUE_PAIR *cpw; int mschap_id = eap_ds->response->type.data[1]; int copied=0,seq=1; DEBUG2(" rlm_eap_mschapv2: password change packet received"); 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); pairadd(&handler->request->packet->vps, challenge); cpw = pairmake("MS-CHAP2-CPW", "", T_OP_EQ); cpw->vp_octets[0] = 7; cpw->vp_octets[1] = mschap_id; memcpy(cpw->vp_octets+2, eap_ds->response->type.data + 520, 66); cpw->length = 68; pairadd(&handler->request->packet->vps, cpw); /* * break the encoded password into VPs (3 of them) */ while (copied < 516) { VALUE_PAIR *nt_enc; int to_copy = 516 - copied; if (to_copy > 243) to_copy = 243; nt_enc = pairmake("MS-CHAP-NT-Enc-PW", "", T_OP_ADD); nt_enc->vp_octets[0] = 6; nt_enc->vp_octets[1] = mschap_id; nt_enc->vp_octets[2] = 0; nt_enc->vp_octets[3] = seq++; memcpy(nt_enc->vp_octets + 4, eap_ds->response->type.data + 4 + copied, to_copy); copied += to_copy; nt_enc->length = 4 + to_copy; pairadd(&handler->request->packet->vps, nt_enc); } DEBUG2(" rlm_eap_mschapv2: built change password packet"); debug_pair_list(handler->request->packet->vps); /* * jump to "authentication" */ goto packet_ready; } /* * we sent a failure and are expecting a failure back */ if (ccode != PW_EAP_MSCHAPV2_FAILURE) { radlog(L_ERR, "rlm_eap_mschapv2: Sent FAILURE expecting FAILURE but got %d", ccode); return 0; } failure: handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; eap_ds->request->code = PW_EAP_FAILURE; return 1; case PW_EAP_MSCHAPV2_SUCCESS: /* * we sent a success to the client; some clients send a * success back as-per the RFC, some send an ACK. Permit * both, I guess... */ switch (ccode) { case PW_EAP_MSCHAPV2_SUCCESS: eap_ds->request->code = PW_EAP_SUCCESS; pairadd(&handler->request->reply->vps, data->mppe_keys); data->mppe_keys = NULL; /* fall through... */ case PW_EAP_MSCHAPV2_ACK: #ifdef WITH_PROXY /* * It's a success. Don't proxy it. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; #endif pairadd(&handler->request->reply->vps, data->reply); data->reply = NULL; return 1; } radlog(L_ERR, "rlm_eap_mschapv2: Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode); return 0; case PW_EAP_MSCHAPV2_CHALLENGE: if (ccode == PW_EAP_MSCHAPV2_FAILURE) goto failure; /* * we sent a challenge, expecting a response */ if (ccode != PW_EAP_MSCHAPV2_RESPONSE) { radlog(L_ERR, "rlm_eap_mschapv2: Sent CHALLENGE expecting RESPONSE but got %d", ccode); return 0; } /* authentication happens below */ break; default: /* should never happen */ radlog(L_ERR, "rlm_eap_mschapv2: unknown state %d", data->code); return 0; } /* * Ensure that we have at least enough data * to do the following checks. * * EAP header (4), EAP type, MS-CHAP opcode, * MS-CHAP ident, MS-CHAP data length (2), * MS-CHAP value length. */ if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) { radlog(L_ERR, "rlm_eap_mschapv2: Response is too short"); return 0; } /* * The 'value_size' is the size of the response, * which is supposed to be the response (48 * bytes) plus 1 byte of flags at the end. */ 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; } /* * The MS-Length field is 5 + value_size + length * of name, which is put after the response. */ 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; } /* * We now know that the user has sent us a response * to the challenge. Let's try to authenticate it. * * We do this by taking the challenge from 'data', * the response from the EAP packet, and creating VALUE_PAIR's * to pass to the 'mschap' module. This is a little wonky, * but it works. */ 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; } /* * MS-Length - MS-Value - 5. */ 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'; /* * Add the pairs to the request, and call the 'mschap' * module. */ pairadd(&handler->request->packet->vps, challenge); pairadd(&handler->request->packet->vps, response); pairadd(&handler->request->packet->vps, name); packet_ready: #ifdef WITH_PROXY /* * If this options is set, then we do NOT authenticate the * user here. Instead, now that we've added the MS-CHAP * attributes to the request, we STOP, and let the outer * tunnel code handle it. * * This means that the outer tunnel code will DELETE the * EAP attributes, and proxy the MS-CHAP attributes to a * home server. */ if (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) { char *username = NULL; eap_tunnel_data_t *tunnel; DEBUG2("rlm_eap_mschapv2: cancelling authentication and letting it be proxied"); /* * Set up the callbacks for the tunnel */ tunnel = rad_malloc(sizeof(*tunnel)); memset(tunnel, 0, sizeof(*tunnel)); tunnel->tls_session = arg; tunnel->callback = mschap_postproxy; /* * Associate the callback with the request. */ rcode = request_data_add(handler->request, handler->request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK, tunnel, free); rad_assert(rcode == 0); /* * The State attribute is NOT supposed to * go into the proxied packet, it will confuse * other RADIUS servers, and they will discard * the request. * * The PEAP module will take care of adding * the State attribute back, before passing * the handler & request back into the tunnel. */ pairdelete(&handler->request->packet->vps, PW_STATE, 0, TAG_ANY); /* * Fix the User-Name when proxying, to strip off * the NT Domain, if we're told to, and a User-Name * exists, and there's a \\, meaning an NT-Domain * in the user name, THEN discard the user name. */ if (inst->with_ntdomain_hack && ((challenge = pairfind(handler->request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) && ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) { /* * Wipe out the NT domain. * * FIXME: Put it into MS-CHAP-Domain? */ username++; /* skip the \\ */ memmove(challenge->vp_strvalue, username, strlen(username) + 1); /* include \0 */ challenge->length = strlen(challenge->vp_strvalue); } /* * Remember that in the post-proxy stage, we've got * to do the work below, AFTER the call to MS-CHAP * authentication... */ return 1; } #endif /* * This is a wild & crazy hack. */ rcode = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->request); /* * Delete MPPE keys & encryption policy. We don't * want these here. */ fix_mppe_keys(handler, data); /* * Take the response from the mschap module, and * return success or failure, depending on the result. */ response = NULL; if (rcode == RLM_MODULE_OK) { pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY); data->code = PW_EAP_MSCHAPV2_SUCCESS; } else if (inst->send_error) { pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY); if (response) { int n,err,retry; char buf[34]; DEBUG2(" MSCHAP-Error: %s", response->vp_strvalue); /* * Pxarse the new challenge out of the * MS-CHAP-Error, so that if the client * issues a re-try, we will know which * challenge value that they used. */ n = sscanf(response->vp_strvalue, "%*cE=%d R=%d C=%32s", &err, &retry, &buf[0]); if (n == 3) { DEBUG2(" Found new challenge from MS-CHAP-Error: err=%d retry=%d challenge=%s", err, retry, buf); fr_hex2bin(buf, data->challenge, 16); } else { DEBUG2(" Could not parse new challenge from MS-CHAP-Error: %d", n); } } data->code = PW_EAP_MSCHAPV2_FAILURE; } else { eap_ds->request->code = PW_EAP_FAILURE; return 1; } /* * No response, die. */ if (!response) { radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found."); return 0; } /* * Compose the response (whatever it is), * and return it to the over-lying EAP module. */ eapmschapv2_compose(handler, response); pairfree(&response); return 1; }
/* * Check password. * * Returns: 0 OK * -1 Password fail * -2 Rejected (Auth-Type = Reject, send Port-Message back) * 1 End check & return, don't reply * * NOTE: NOT the same as the RLM_ values ! */ static int rad_check_password(REQUEST *request) { VALUE_PAIR *auth_type_pair; VALUE_PAIR *cur_config_item; VALUE_PAIR *password_pair; VALUE_PAIR *auth_item; DICT_VALUE *da; char string[MAX_STRING_LEN]; int auth_type = -1; int result; int auth_type_count = 0; result = 0; /* * Look for matching check items. We skip the whole lot * if the authentication type is PW_AUTHTYPE_ACCEPT or * PW_AUTHTYPE_REJECT. */ cur_config_item = request->config_items; while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE))) != NULL) { auth_type = auth_type_pair->lvalue; auth_type_count++; DEBUG2(" rad_check_password: Found Auth-Type %s", auth_type_pair->strvalue); cur_config_item = auth_type_pair->next; if (auth_type == PW_AUTHTYPE_REJECT) { DEBUG2(" rad_check_password: Auth-Type = Reject, rejecting user"); return -2; } } if (( auth_type_count > 1) && (debug_flag)) { radlog(L_ERR, "Warning: Found %d auth-types on request for user '%s'", auth_type_count, request->username->strvalue); } /* * This means we have a proxy reply or an accept * and it wasn't rejected in the above loop. So * that means it is accepted and we do no further * authentication */ if ((auth_type == PW_AUTHTYPE_ACCEPT) || (request->proxy)) { DEBUG2(" rad_check_password: Auth-Type = Accept, accepting the user"); return 0; } /* * Find the password from the users file. */ if ((password_pair = pairfind(request->config_items, PW_CRYPT_PASSWORD)) != NULL) { /* * Re-write Auth-Type, but ONLY if it isn't already * set. */ if (auth_type == -1) auth_type = PW_AUTHTYPE_CRYPT; } else { password_pair = pairfind(request->config_items, PW_PASSWORD); } if (auth_type < 0) { if (password_pair) { auth_type = PW_AUTHTYPE_LOCAL; } else { /* * The admin hasn't told us how to * authenticate the user, so we reject them! * * This is fail-safe. */ DEBUG2("auth: No authenticate method (Auth-Type) configuration found for the request: Rejecting the user"); return -2; } } switch(auth_type) { case PW_AUTHTYPE_CRYPT: /* * Find the password sent by the user. It * SHOULD be there, if it's not * authentication fails. */ auth_item = request->password; if (auth_item == NULL) { DEBUG2("auth: No User-Password or CHAP-Password attribute in the request"); return -1; } DEBUG2("auth: type Crypt"); if (password_pair == NULL) { DEBUG2("No Crypt-Password configured for the user"); rad_authlog("Login incorrect " "(No Crypt-Password configured for the user)", request, 0); return -1; } switch (lrad_crypt_check((char *)auth_item->strvalue, (char *)password_pair->strvalue)) { case -1: rad_authlog("Login incorrect " "(system failed to supply an encrypted password for comparison)", request, 0); case 1: return -1; } break; case PW_AUTHTYPE_LOCAL: DEBUG2("auth: type Local"); /* * Find the password sent by the user. It * SHOULD be there, if it's not * authentication fails. */ auth_item = request->password; if (auth_item == NULL) { DEBUG2("auth: No User-Password or CHAP-Password attribute in the request"); return -1; } /* * Plain text password. */ if (password_pair == NULL) { DEBUG2("auth: No password configured for the user"); rad_authlog("Login incorrect " "(No password configured for the user)", request, 0); return -1; } /* * Local password is just plain text. */ if (auth_item->attribute == PW_PASSWORD) { if (strcmp((char *)password_pair->strvalue, (char *)auth_item->strvalue) != 0) { DEBUG2("auth: user supplied User-Password does NOT match local User-Password"); return -1; } DEBUG2("auth: user supplied User-Password matches local User-Password"); break; } else if (auth_item->attribute != PW_CHAP_PASSWORD) { DEBUG2("The user did not supply a User-Password or a CHAP-Password attribute"); rad_authlog("Login incorrect " "(no User-Password or CHAP-Password attribute)", request, 0); return -1; } rad_chap_encode(request->packet, string, auth_item->strvalue[0], password_pair); /* * Compare them */ if (memcmp(string + 1, auth_item->strvalue + 1, CHAP_VALUE_LENGTH) != 0) { DEBUG2("auth: user supplied CHAP-Password does NOT match local User-Password"); return -1; } DEBUG2("auth: user supplied CHAP-Password matches local User-Password"); break; default: da = dict_valbyattr(PW_AUTH_TYPE, auth_type); DEBUG2("auth: type \"%s\"", da->name); /* * See if there is a module that handles * this type, and turn the RLM_ return * status into the values as defined at * the top of this function. */ result = module_authenticate(auth_type, request); switch (result) { /* * An authentication module FAIL * return code, or any return code that * is not expected from authentication, * is the same as an explicit REJECT! */ case RLM_MODULE_FAIL: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: case RLM_MODULE_INVALID: case RLM_MODULE_NOTFOUND: case RLM_MODULE_NOOP: case RLM_MODULE_UPDATED: result = -1; break; case RLM_MODULE_OK: result = 0; break; case RLM_MODULE_HANDLED: result = 1; break; } break; } return result; }