/* * auth_response() generates MS-CHAP v2 SUCCESS response * according to RFC 2759 GenerateAuthenticatorResponse() * returns 42-octet response string */ static void auth_response(const char *username, const uint8_t *nt_hash_hash, uint8_t *ntresponse, uint8_t *peer_challenge, uint8_t *auth_challenge, char *response) { fr_SHA1_CTX Context; static const uint8_t magic1[39] = {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74}; static const uint8_t magic2[41] = {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E}; static const char hex[16] = "0123456789ABCDEF"; size_t i; uint8_t challenge[8]; uint8_t digest[20]; fr_SHA1Init(&Context); fr_SHA1Update(&Context, nt_hash_hash, 16); fr_SHA1Update(&Context, ntresponse, 24); fr_SHA1Update(&Context, magic1, 39); fr_SHA1Final(digest, &Context); challenge_hash(peer_challenge, auth_challenge, username, challenge); fr_SHA1Init(&Context); fr_SHA1Update(&Context, digest, 20); fr_SHA1Update(&Context, challenge, 8); fr_SHA1Update(&Context, magic2, 41); fr_SHA1Final(digest, &Context); /* * Encode the value of 'Digest' as "S=" followed by * 40 ASCII hexadecimal digits and return it in * AuthenticatorResponse. * For example, * "S=0123456789ABCDEF0123456789ABCDEF01234567" */ response[0] = 'S'; response[1] = '='; /* * The hexadecimal digits [A-F] MUST be uppercase. */ for (i = 0; i < sizeof(digest); i++) { response[2 + (i * 2)] = hex[(digest[i] >> 4) & 0x0f]; response[3 + (i * 2)] = hex[digest[i] & 0x0f]; } }
void generate_nt_response_pwhash(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password_hash, u8 *response) { u8 challenge[8]; challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge); challenge_response(challenge, password_hash, response); }
void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; u8 password_hash[16]; challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge); nt_password_hash(password, password_len, password_hash); challenge_response(challenge, password_hash, response); }
/** * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure */ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge) || nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; }
/** * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 * @password_hash: 16-octet PasswordHash (IN) * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually * encoded as a 42-octet ASCII string (S=hexdump_of_response) * Returns: 0 on success, -1 on failure */ int generate_authenticator_response_pwhash( const u8 *password_hash, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response) { static const u8 magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; static const u8 magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E }; u8 password_hash_hash[16], challenge[8]; const unsigned char *addr1[3]; const size_t len1[3] = { 16, 24, sizeof(magic1) }; const unsigned char *addr2[3]; const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; addr1[0] = password_hash_hash; addr1[1] = nt_response; addr1[2] = magic1; addr2[0] = response; addr2[1] = challenge; addr2[2] = magic2; if (hash_nt_password_hash(password_hash, password_hash_hash)) return -1; if (sha1_vector(3, addr1, len1, response)) return -1; if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; return sha1_vector(3, addr2, len2, response); }
/* * Does dynamic translation of strings. * * Pulls NT-Response, LM-Response, or Challenge from MSCHAP * attributes. */ static size_t mschap_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, RADIUS_ESCAPE_STRING func) { size_t i, data_len; uint8_t *data = NULL; uint8_t buffer[32]; VALUE_PAIR *user_name; VALUE_PAIR *chap_challenge, *response; rlm_mschap_t *inst = instance; response = NULL; func = func; /* -Wunused */ /* * Challenge means MS-CHAPv1 challenge, or * hash of MS-CHAPv2 challenge, and peer challenge. */ if (strncasecmp(fmt, "Challenge", 9) == 0) { chap_challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!chap_challenge) { RDEBUG2("No MS-CHAP-Challenge in the request."); return 0; } /* * MS-CHAP-Challenges are 8 octets, * for MS-CHAPv2 */ if (chap_challenge->length == 8) { RDEBUG2(" mschap1: %02x", chap_challenge->vp_octets[0]); data = chap_challenge->vp_octets; data_len = 8; /* * MS-CHAP-Challenges are 16 octets, * for MS-CHAPv2. */ } else if (chap_challenge->length == 16) { char *username_string; RDEBUG2(" mschap2: %02x", chap_challenge->vp_octets[0]); response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); if (!response) { RDEBUG2("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge."); return 0; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return 0; } user_name = pairfind(request->packet->vps, PW_USER_NAME); if (!user_name) { RDEBUG2("User-Name is required to calculateMS-CHAPv1 Challenge."); return 0; } /* * with_ntdomain_hack moved here, too. */ if ((username_string = strchr(user_name->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 = user_name->vp_strvalue; } } else { username_string = user_name->vp_strvalue; } /* * Get the MS-CHAPv1 challenge * from the MS-CHAPv2 peer challenge, * our challenge, and the user name. */ challenge_hash(response->vp_octets + 2, chap_challenge->vp_octets, username_string, buffer); data = buffer; data_len = 8; } else { RDEBUG2("Invalid MS-CHAP challenge length"); return 0; } /* * Get the MS-CHAPv1 response, or the MS-CHAPv2 * response. */ } else if (strncasecmp(fmt, "NT-Response", 11) == 0) { response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); if (!response) response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); if (!response) { RDEBUG2("No MS-CHAP-Response or MS-CHAP2-Response was found in the request."); return 0; } /* * For MS-CHAPv1, the NT-Response exists only * if the second octet says so. */ if ((response->attribute == PW_MSCHAP_RESPONSE) && ((response->vp_octets[1] & 0x01) == 0)) { RDEBUG2("No NT-Response in MS-CHAP-Response"); return 0; } /* * MS-CHAP-Response and MS-CHAP2-Response have * the NT-Response at the same offset, and are * the same length. */ data = response->vp_octets + 26; data_len = 24; /* * LM-Response is deprecated, and exists only * in MS-CHAPv1, and not often there. */ } else if (strncasecmp(fmt, "LM-Response", 11) == 0) { response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); if (!response) { RDEBUG2("No MS-CHAP-Response was found in the request."); return 0; } /* * For MS-CHAPv1, the NT-Response exists only * if the second octet says so. */ if ((response->vp_octets[1] & 0x01) != 0) { RDEBUG2("No LM-Response in MS-CHAP-Response"); return 0; } data = response->vp_octets + 2; data_len = 24; /* * Pull the NT-Domain out of the User-Name, if it exists. */ } else if (strncasecmp(fmt, "NT-Domain", 9) == 0) { char *p, *q; user_name = pairfind(request->packet->vps, PW_USER_NAME); if (!user_name) { RDEBUG2("No User-Name was found in the request."); return 0; } /* * First check to see if this is a host/ style User-Name * (a la Kerberos host principal) */ if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) { /* * If we're getting a User-Name formatted in this way, * it's likely due to PEAP. The Windows Domain will be * the first domain component following the hostname, * or the machine name itself if only a hostname is supplied */ p = strchr(user_name->vp_strvalue, '.'); if (!p) { RDEBUG2("setting NT-Domain to same as machine name"); strlcpy(out, user_name->vp_strvalue + 5, outlen); } else { p++; /* skip the period */ q = strchr(p, '.'); /* * use the same hack as below * only if another period was found */ if (q) *q = '\0'; strlcpy(out, p, outlen); if (q) *q = '.'; } } else { p = strchr(user_name->vp_strvalue, '\\'); if (!p) { RDEBUG2("No NT-Domain was found in the User-Name."); return 0; } /* * Hack. This is simpler than the alternatives. */ *p = '\0'; strlcpy(out, user_name->vp_strvalue, outlen); *p = '\\'; } return strlen(out); /* * Pull the User-Name out of the User-Name... */ } else if (strncasecmp(fmt, "User-Name", 9) == 0) { char *p; user_name = pairfind(request->packet->vps, PW_USER_NAME); if (!user_name) { RDEBUG2("No User-Name was found in the request."); return 0; } /* * First check to see if this is a host/ style User-Name * (a la Kerberos host principal) */ if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) { /* * If we're getting a User-Name formatted in this way, * it's likely due to PEAP. When authenticating this against * a Domain, Windows will expect the User-Name to be in the * format of hostname$, the SAM version of the name, so we * have to convert it to that here. We do so by stripping * off the first 5 characters (host/), and copying everything * from that point to the first period into a string and appending * a $ to the end. */ p = strchr(user_name->vp_strvalue, '.'); /* * use the same hack as above * only if a period was found */ if (p) *p = '\0'; snprintf(out, outlen, "%s$", user_name->vp_strvalue + 5); if (p) *p = '.'; } else { p = strchr(user_name->vp_strvalue, '\\'); if (p) { p++; /* skip the backslash */ } else { p = user_name->vp_strvalue; /* use the whole User-Name */ } strlcpy(out, p, outlen); } return strlen(out); /* * Return the NT-Hash of the passed string */ } else if (strncasecmp(fmt, "NT-Hash ", 8) == 0) { char *p; p = fmt + 8; /* 7 is the length of 'NT-Hash' */ if ((p == '\0') || (outlen <= 32)) return 0; RDEBUG("rlm_mschap: NT-Hash: %s",p); ntpwdhash(buffer,p); fr_bin2hex(buffer, out, 16); out[32] = '\0'; RDEBUG("rlm_mschap: NT-Hash: Result: %s",out); return 32; /* * Return the LM-Hash of the passed string */ } else if (strncasecmp(fmt, "LM-Hash ", 8) == 0) { char *p; p = fmt + 8; /* 7 is the length of 'LM-Hash' */ if ((p == '\0') || (outlen <= 32)) return 0; RDEBUG("rlm_mschap: LM-Hash: %s",p); smbdes_lmpwdhash(p, buffer); fr_bin2hex(buffer, out, 16); out[32] = '\0'; RDEBUG("rlm_mschap: LM-Hash: Result: %s",out); return 32; } else { RDEBUG2("Unknown expansion string \"%s\"", fmt); return 0; } if (outlen == 0) return 0; /* nowhere to go, don't do anything */ /* * Didn't set anything: this is bad. */ if (!data) { RDEBUG2("Failed to do anything intelligent"); return 0; } /* * Check the output length. */ if (outlen < ((data_len * 2) + 1)) { data_len = (outlen - 1) / 2; } /* * */ for (i = 0; i < data_len; i++) { sprintf(out + (2 * i), "%02x", data[i]); } out[data_len * 2] = '\0'; return data_len * 2; }
/* * 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 }
static void eap_mschapv2_process_response(struct eap_sm *sm, struct eap_mschapv2_data *data, struct wpabuf *respData) { struct eap_mschapv2_hdr *resp; const u8 *pos, *end, *peer_challenge, *nt_response, *name; u8 flags; size_t len, name_len, i; u8 expected[24]; u8 challenge_hash1[8]; const u8 *username, *user; size_t username_len, user_len; int x; char *buf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); if (pos == NULL || len < 1) return; /* Should not happen - frame already validated */ end = pos + len; resp = (struct eap_mschapv2_hdr *) pos; pos = (u8 *) (resp + 1); if (len < sizeof(*resp) + 1 + 49 || resp->op_code != MSCHAPV2_OP_RESPONSE || pos[0] != 49) { wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", respData); data->state = FAILURE; return; } data->resp_mschapv2_id = resp->mschapv2_id; pos++; peer_challenge = pos; pos += 16 + 8; nt_response = pos; pos += 24; flags = *pos++; name = pos; name_len = end - name; if (data->peer_challenge) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " "Peer-Challenge"); peer_challenge = data->peer_challenge; } wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", peer_challenge, 16); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); challenge_hash(peer_challenge, data->auth_challenge, name, name_len, challenge_hash1); wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Challenge Hash", challenge_hash1, 8); wpa_printf(MSG_INFO, "MANA (EAP-FAST) : Username:%s", name); wpa_printf(MSG_INFO, "MANA (EAP-FAST) : Challenge"); printf("MANA (EAP-FAST) : "); for (x=0;x<7;x++) printf("%02x:",challenge_hash1[x]); printf("%02x\n",challenge_hash1[7]); wpa_printf(MSG_INFO, "MANA (EAP-FAST) : Response"); printf("MANA (EAP-FAST) : "); for (x=0;x<23;x++) printf("%02x:",nt_response[x]); printf("%02x\n",nt_response[23]); char *ennode = getenv("KARMANODE"); FILE *f = fopen(ennode, "a"); if (f != NULL) { const char *hdr = "CHAP"; fprintf(f, "%s|%s|", hdr, name); for (x = 0; x < 7; x++) { fprintf(f, "%02x:", challenge_hash1[x]); } fprintf(f, "%02x|", challenge_hash1[7]); for (x = 0; x < 23; x++) { fprintf(f, "%02x:", nt_response[x]); } fprintf(f, "%02x\n", nt_response[23]); fclose(f); } buf = os_malloc(name_len * 4 + 1); if (buf) { printf_encode(buf, name_len * 4 + 1, name, name_len); eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); os_free(buf); } /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = sm->identity; username_len = sm->identity_len; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } user = name; user_len = name_len; for (i = 0; i < user_len; i++) { if (user[i] == '\\') { user_len -= i + 1; user += i + 1; break; } } if (username_len != user_len || os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " "name", username, username_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " "name", user, user_len); data->state = FAILURE; return; } wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", username, username_len); if (sm->user->password_hash) { //res = generate_nt_response_pwhash(data->auth_challenge, generate_nt_response_pwhash(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, expected); } else { //res = generate_nt_response(data->auth_challenge, generate_nt_response(data->auth_challenge, peer_challenge, username, username_len, sm->user->password, sm->user->password_len, expected); } //if (res) { //data->state = FAILURE; //return; //} nt_response = expected; //os_memset((void *)nt_response, 0, 24); //os_memset((void *)expected, 0, 24); //if (os_memcmp_const(nt_response, expected, 24) == 0) { const u8 *pw_hash; u8 pw_hash_buf[16], pw_hash_hash[16]; wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); data->state = SUCCESS_REQ; /* Authenticator response is not really needed yet, but * calculate it here so that peer_challenge and username need * not be saved. */ if (sm->user->password_hash) { pw_hash = sm->user->password; } else { if (nt_password_hash(sm->user->password, sm->user->password_len, pw_hash_buf) < 0) { //data->state = FAILURE; data->state = SUCCESS; //return; } pw_hash = pw_hash_buf; } generate_authenticator_response_pwhash( pw_hash, peer_challenge, data->auth_challenge, username, username_len, nt_response, data->auth_response); hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, nt_response, data->master_key); data->master_key_valid = 1; wpa_hexdump_key(MSG_INFO, "EAP-MSCHAPV2: Derived Master Key", data->master_key, MSCHAPV2_KEY_LEN); //} else { //data->state = SUCCESS; //} }
int main(int argc, char *argv[]) { /* Test vector from RFC2759 example */ u8 *username = "******"; u8 *password = "******"; u8 auth_challenge[] = { 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 }; u8 peer_challenge[] = { 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E }; u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 }; u8 password_hash[] = { 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE }; u8 nt_response[] = { 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF }; u8 password_hash_hash[] = { 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F }; u8 authenticator_response[] = { 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, 0x93, 0x2C, 0xDA, 0x56 }; u8 master_key[] = { 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 }; u8 send_start_key[] = { 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB }; u8 buf[32]; int errors = 0; printf("Testing ms_funcs.c\n"); challenge_hash(peer_challenge, auth_challenge, username, strlen(username), buf); if (memcmp(challenge, buf, sizeof(challenge)) != 0) { printf("challenge_hash failed\n"); errors++; } nt_password_hash(password, strlen(password), buf); if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) { printf("nt_password_hash failed\n"); errors++; } generate_nt_response(auth_challenge, peer_challenge, username, strlen(username), password, strlen(password), buf); if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) { printf("generate_nt_response failed\n"); errors++; } hash_nt_password_hash(password_hash, buf); if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { printf("hash_nt_password_hash failed\n"); errors++; } generate_authenticator_response(password, strlen(password), peer_challenge, auth_challenge, username, strlen(username), nt_response, buf); if (memcmp(authenticator_response, buf, sizeof(authenticator_response)) != 0) { printf("generate_authenticator_response failed\n"); errors++; } get_master_key(password_hash_hash, nt_response, buf); if (memcmp(master_key, buf, sizeof(master_key)) != 0) { printf("get_master_key failed\n"); errors++; } get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1); if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { printf("get_asymetric_start_key failed\n"); errors++; } if (errors) printf("FAILED! %d errors\n", errors); return errors; }