/* * mschap_authenticate() - authenticate user based on given * attributes and configuration. * We will try to find out password in configuration * or in configured passwd file. * If one is found we will check paraneters given by NAS. * * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have * one of: * PAP: PW_USER_PASSWORD or * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE * In case of password mismatch or locked account we MAY return * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2 * If MS-CHAP2 succeeds we MUST return * PW_MSCHAP2_SUCCESS */ static int mschap_authenticate(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL; VALUE_PAIR *response = NULL; VALUE_PAIR *password = NULL; VALUE_PAIR *lm_password, *nt_password, *smb_ctrl; VALUE_PAIR *username; uint8_t nthashhash[16]; char msch2resp[42]; char *username_string; int chap = 0; int do_ntlm_auth; /* * If we have ntlm_auth configured, use it unless told * otherwise */ do_ntlm_auth = (inst->ntlm_auth != NULL); /* * If we have an ntlm_auth configuration, then we may * want to suppress it. */ if (do_ntlm_auth) { VALUE_PAIR *vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH); if (vp) do_ntlm_auth = vp->vp_integer; } /* * Find the SMB-Account-Ctrl attribute, or the * SMB-Account-Ctrl-Text attribute. */ smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL); if (!smb_ctrl) { password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT); if (password) { smb_ctrl = radius_pairmake(request, &request->config_items, "SMB-Account-CTRL", "0", T_OP_SET); if (smb_ctrl) { smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue); } } } /* * We're configured to do MS-CHAP authentication. * and account control information exists. Enforce it. */ if (smb_ctrl) { /* * Password is not required. */ if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) { RDEBUG2("SMB-Account-Ctrl says no password is required."); return RLM_MODULE_OK; } } /* * Decide how to get the passwords. */ password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); /* * We need an LM-Password. */ lm_password = pairfind(request->config_items, PW_LM_PASSWORD); if (lm_password) { /* * Allow raw octets. */ if ((lm_password->length == 16) || ((lm_password->length == 32) && (fr_hex2bin(lm_password->vp_strvalue, lm_password->vp_octets, 16) == 16))) { RDEBUG2("Found LM-Password"); lm_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid LM-Password"); lm_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password."); } else { /* there is a configured Cleartext-Password */ lm_password = radius_pairmake(request, &request->config_items, "LM-Password", "", T_OP_EQ); if (!lm_password) { radlog_request(L_ERR, 0, request, "No memory"); } else { smbdes_lmpwdhash(password->vp_strvalue, lm_password->vp_octets); lm_password->length = 16; } } /* * We need an NT-Password. */ nt_password = pairfind(request->config_items, PW_NT_PASSWORD); if (nt_password) { if ((nt_password->length == 16) || ((nt_password->length == 32) && (fr_hex2bin(nt_password->vp_strvalue, nt_password->vp_octets, 16) == 16))) { RDEBUG2("Found NT-Password"); nt_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid NT-Password"); nt_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password."); } else { /* there is a configured Cleartext-Password */ nt_password = radius_pairmake(request, &request->config_items, "NT-Password", "", T_OP_EQ); if (!nt_password) { radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; } } challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!challenge) { RDEBUG2("No MS-CHAP-Challenge in the request"); return RLM_MODULE_REJECT; } /* * We also require an MS-CHAP-Response. */ response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); /* * MS-CHAP-Response, means MS-CHAPv1 */ if (response) { int offset; /* * MS-CHAPv1 challenges are 8 octets. */ if (challenge->length < 8) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We are doing MS-CHAP. Calculate the MS-CHAP * response */ if (response->vp_octets[1] & 0x01) { RDEBUG2("Told to do MS-CHAPv1 with NT-Password"); password = nt_password; offset = 26; } else { RDEBUG2("Told to do MS-CHAPv1 with LM-Password"); password = lm_password; offset = 2; } /* * Do the MS-CHAP authentication. */ if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("MS-CHAP-Response is incorrect."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_REJECT; } chap = 1; } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) { uint8_t mschapv1_challenge[16]; /* * MS-CHAPv2 challenges are 16 octets. */ if (challenge->length < 16) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We also require a User-Name */ username = pairfind(request->packet->vps, PW_USER_NAME); if (!username) { radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2"); return RLM_MODULE_INVALID; } /* * with_ntdomain_hack moved here */ if ((username_string = strchr(username->vp_strvalue, '\\')) != NULL) { if (inst->with_ntdomain_hack) { username_string++; } else { RDEBUG2(" NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); username_string = username->vp_strvalue; } } else { username_string = username->vp_strvalue; } #ifdef __APPLE__ /* * No "known good" NT-Password attribute. Try to do * OpenDirectory authentication. * * If OD determines the user is an AD user it will return noop, which * indicates the auth process should continue directly to AD. * Otherwise OD will determine auth success/fail. */ if (!nt_password && inst->open_directory) { RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication."); int odStatus = od_mschap_auth(request, challenge, username); if (odStatus != RLM_MODULE_NOOP) { return odStatus; } } #endif /* * The old "mschapv2" function has been moved to * here. * * MS-CHAPv2 takes some additional data to create an * MS-CHAPv1 challenge, and then does MS-CHAPv1. */ challenge_hash(response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ username_string, /* user name */ mschapv1_challenge); /* resulting challenge */ RDEBUG2("Told to do MS-CHAPv2 for %s with NT-Password", username_string); if (do_mschap(inst, request, nt_password, mschapv1_challenge, response->vp_octets + 26, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("FAILED: MS-CHAP2-Response is incorrect"); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_REJECT; } /* * Get the NT-hash-hash, if necessary */ if (nt_password) { } auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ msch2resp); /* calculated MPPE key */ mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP2-Success", msch2resp, 42); chap = 2; } else { /* Neither CHAPv1 or CHAPv2 response: die */ radlog_request(L_AUTH, 0, request, "No MS-CHAP response found"); return RLM_MODULE_INVALID; } /* * We have a CHAP response, but the account may be * disabled. Reject the user with the same error code * we use when their password is invalid. */ if (smb_ctrl) { /* * Account is disabled. * * They're found, but they don't exist, so we * return 'not found'. */ if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) || ((smb_ctrl->vp_integer & ACB_NORMAL) == 0)) { RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal account."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_NOTFOUND; } /* * User is locked out. */ if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) { RDEBUG2("SMB-Account-Ctrl says that the account is locked out."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=647 R=0", 9); return RLM_MODULE_USERLOCK; } } /* now create MPPE attributes */ if (inst->use_mppe) { uint8_t mppe_sendkey[34]; uint8_t mppe_recvkey[34]; if (chap == 1){ RDEBUG2("adding MS-CHAPv1 MPPE keys"); memset(mppe_sendkey, 0, 32); if (lm_password) { memcpy(mppe_sendkey, lm_password->vp_octets, 8); } /* * According to RFC 2548 we * should send NT hash. But in * practice it doesn't work. * Instead, we should send nthashhash * * This is an error on RFC 2548. */ /* * do_mschap cares to zero nthashhash if NT hash * is not available. */ memcpy(mppe_sendkey + 8, nthashhash, 16); mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32); } else if (chap == 2) { RDEBUG2("adding MS-CHAPv2 MPPE keys"); mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey); mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16); mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16); } radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Policy", (inst->require_encryption)? "0x00000002":"0x00000001", T_OP_EQ); radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Types", (inst->require_strong)? "0x00000004":"0x00000006", T_OP_EQ); } /* else we weren't asked to use MPPE */ return RLM_MODULE_OK; #undef inst }
/* * mschap_authenticate() - authenticate user based on given * attributes and configuration. * We will try to find out password in configuration * or in configured passwd file. * If one is found we will check paraneters given by NAS. * * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have * one of: * PAP: PW_USER_PASSWORD or * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE * In case of password mismatch or locked account we MAY return * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2 * If MS-CHAP2 succeeds we MUST return * PW_MSCHAP2_SUCCESS */ static int mschap_authenticate(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL; VALUE_PAIR *response = NULL; VALUE_PAIR *password = NULL; VALUE_PAIR *lm_password, *nt_password, *smb_ctrl; VALUE_PAIR *username; uint8_t nthashhash[16]; char msch2resp[42]; char *username_string; int chap = 0; int do_ntlm_auth; /* * If we have ntlm_auth configured, use it unless told * otherwise */ do_ntlm_auth = (inst->ntlm_auth != NULL); /* * If we have an ntlm_auth configuration, then we may * want to suppress it. */ if (do_ntlm_auth) { VALUE_PAIR *vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH); if (vp) do_ntlm_auth = vp->vp_integer; } /* * Find the SMB-Account-Ctrl attribute, or the * SMB-Account-Ctrl-Text attribute. */ smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL); if (!smb_ctrl) { password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT); if (password) { smb_ctrl = radius_pairmake(request, &request->config_items, "SMB-Account-CTRL", "0", T_OP_SET); if (smb_ctrl) { smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue); } } } /* * We're configured to do MS-CHAP authentication. * and account control information exists. Enforce it. */ if (smb_ctrl) { /* * Password is not required. */ if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) { RDEBUG2("SMB-Account-Ctrl says no password is required."); return RLM_MODULE_OK; } } /* * Decide how to get the passwords. */ password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); /* * We need an LM-Password. */ lm_password = pairfind(request->config_items, PW_LM_PASSWORD); if (lm_password) { /* * Allow raw octets. */ if ((lm_password->length == 16) || ((lm_password->length == 32) && (fr_hex2bin(lm_password->vp_strvalue, lm_password->vp_octets, 16) == 16))) { RDEBUG2("Found LM-Password"); lm_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid LM-Password"); lm_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password."); } else { /* there is a configured Cleartext-Password */ lm_password = radius_pairmake(request, &request->config_items, "LM-Password", "", T_OP_EQ); if (!lm_password) { radlog_request(L_ERR, 0, request, "No memory"); } else { smbdes_lmpwdhash(password->vp_strvalue, lm_password->vp_octets); lm_password->length = 16; } } /* * We need an NT-Password. */ nt_password = pairfind(request->config_items, PW_NT_PASSWORD); if (nt_password) { if ((nt_password->length == 16) || ((nt_password->length == 32) && (fr_hex2bin(nt_password->vp_strvalue, nt_password->vp_octets, 16) == 16))) { RDEBUG2("Found NT-Password"); nt_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid NT-Password"); nt_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password."); } else { /* there is a configured Cleartext-Password */ nt_password = radius_pairmake(request, &request->config_items, "NT-Password", "", T_OP_EQ); if (!nt_password) { radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { mschap_ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; } } challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!challenge) { RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_REJECT; } /* * We also require an MS-CHAP-Response. */ response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); /* * MS-CHAP-Response, means MS-CHAPv1 */ if (response) { int offset; /* * MS-CHAPv1 challenges are 8 octets. */ if (challenge->length < 8) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We are doing MS-CHAP. Calculate the MS-CHAP * response */ if (response->vp_octets[1] & 0x01) { RDEBUG2("Client is using MS-CHAPv1 with NT-Password"); password = nt_password; offset = 26; } else { RDEBUG2("Client is using MS-CHAPv1 with LM-Password"); password = lm_password; offset = 2; } /* * Do the MS-CHAP authentication. */ if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("MS-CHAP-Response is incorrect."); goto do_error; } chap = 1; } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) { uint8_t mschapv1_challenge[16]; VALUE_PAIR *name_attr, *response_name; /* * MS-CHAPv2 challenges are 16 octets. */ if (challenge->length < 16) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We also require a User-Name */ username = pairfind(request->packet->vps, PW_USER_NAME); if (!username) { radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2"); return RLM_MODULE_INVALID; } /* * Check for MS-CHAP-User-Name and if found, use it * to construct the MSCHAPv1 challenge. This is * set by rlm_eap_mschap to the MS-CHAP Response * packet Name field. * * We prefer this to the User-Name in the * packet. */ response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME); if (response_name) { name_attr = response_name; } else { name_attr = username; } /* * with_ntdomain_hack moved here, too. */ if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) { if (inst->with_ntdomain_hack) { username_string++; } else { RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); username_string = name_attr->vp_strvalue; } } else { username_string = name_attr->vp_strvalue; } if (response_name && ((username->length != response_name->length) || (strncasecmp(username->vp_strvalue, response_name->vp_strvalue, username->length) != 0))) { RDEBUG("ERROR: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", username->vp_strvalue, response_name->vp_strvalue); return RLM_MODULE_REJECT; } /* * The old "mschapv2" function has been moved to * here. * * MS-CHAPv2 takes some additional data to create an * MS-CHAPv1 challenge, and then does MS-CHAPv1. */ RDEBUG2("Creating challenge hash with username: %s", username_string); mschap_challenge_hash(response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ username_string, /* user name */ mschapv1_challenge); /* resulting challenge */ RDEBUG2("Client is using MS-CHAPv2 for %s, we need NT-Password", username_string); #ifdef __APPLE__ if (inst->open_directory) { return do_od_mschap(request, response, challenge, username_string); } #endif if (do_mschap(inst, request, nt_password, mschapv1_challenge, response->vp_octets + 26, nthashhash, do_ntlm_auth) < 0) { int i; char buffer[128]; RDEBUG2("FAILED: MS-CHAP2-Response is incorrect"); do_error: snprintf(buffer, sizeof(buffer), "E=691 R=%d", inst->allow_retry); if (inst->retry_msg) { snprintf(buffer + 9, sizeof(buffer) - 9, " C="); for (i = 0; i < 16; i++) { snprintf(buffer + 12 + i*2, sizeof(buffer) - 12 - i*2, "%02x", fr_rand() & 0xff); } snprintf(buffer + 44, sizeof(buffer) - 44, " V=3 M=%s", inst->retry_msg); } mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", buffer, strlen(buffer)); return RLM_MODULE_REJECT; } mschap_auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ msch2resp); /* calculated MPPE key */ mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP2-Success", msch2resp, 42); chap = 2; } else { /* Neither CHAPv1 or CHAPv2 response: die */ RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_INVALID; } /* * We have a CHAP response, but the account may be * disabled. Reject the user with the same error code * we use when their password is invalid. */ if (smb_ctrl) { /* * Account is disabled. * * They're found, but they don't exist, so we * return 'not found'. */ if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) || ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) { RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal or workstatin trust account."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_NOTFOUND; } /* * User is locked out. */ if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) { RDEBUG2("SMB-Account-Ctrl says that the account is locked out."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=647 R=0", 9); return RLM_MODULE_USERLOCK; } } /* now create MPPE attributes */ if (inst->use_mppe) { uint8_t mppe_sendkey[34]; uint8_t mppe_recvkey[34]; if (chap == 1){ RDEBUG2("adding MS-CHAPv1 MPPE keys"); memset(mppe_sendkey, 0, 32); if (lm_password) { memcpy(mppe_sendkey, lm_password->vp_octets, 8); } /* * According to RFC 2548 we * should send NT hash. But in * practice it doesn't work. * Instead, we should send nthashhash * * This is an error on RFC 2548. */ /* * do_mschap cares to zero nthashhash if NT hash * is not available. */ memcpy(mppe_sendkey + 8, nthashhash, 16); mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32); } else if (chap == 2) { RDEBUG2("adding MS-CHAPv2 MPPE keys"); mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey); mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16); mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16); } radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Policy", (inst->require_encryption)? "0x00000002":"0x00000001", T_OP_EQ); radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Types", (inst->require_strong)? "0x00000004":"0x00000006", T_OP_EQ); } /* else we weren't asked to use MPPE */ return RLM_MODULE_OK; #undef inst }