X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Frlm_mschap%2Frlm_mschap.c;h=62d45ac7d6ea0eca0d63480caa80ac2b51966d4b;hb=2fa3d061ecd55e12a7fcb1ba7b7684dc5161ef84;hp=9f2db5b63d7a232d072b1e7791a5bb8c5a8a6b53;hpb=ffcceff39a733b5baf8e5a17627a96ca157dee85;p=freeradius.git diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index 9f2db5b..62d45ac 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -20,26 +20,6 @@ * Copyright 2000,2001,2006 The FreeRADIUS server project */ - -/* - * mschap.c MS-CHAP module - * - * This implements MS-CHAP, as described in RFC 2548 - * - * http://www.freeradius.org/rfc/rfc2548.txt - * - */ - -/* - * If you have any questions on NTLM (Samba) passwords - * support, LM authentication and MS-CHAP v2 support - * please contact - * - * Vladimir Dubrovin vlad@sandy.ru - * aka - * ZARAZA 3APA3A@security.nnov.ru - */ - /* MPPE support from Takahiro Wagatsuma */ #include @@ -53,6 +33,7 @@ RCSID("$Id$") #include +#include "mschap.h" #include "smbdes.h" #ifdef __APPLE__ @@ -147,115 +128,6 @@ static int pdb_decode_acct_ctrl(const char *p) } -/* - * ntpwdhash converts Unicode password to 16-byte NT hash - * with MD4 - */ -static void ntpwdhash (uint8_t *szHash, const char *szPassword) -{ - char szUnicodePass[513]; - int nPasswordLen; - int i; - - /* - * NT passwords are unicode. Convert plain text password - * to unicode by inserting a zero every other byte - */ - nPasswordLen = strlen(szPassword); - for (i = 0; i < nPasswordLen; i++) { - szUnicodePass[i << 1] = szPassword[i]; - szUnicodePass[(i << 1) + 1] = 0; - } - - /* Encrypt Unicode password to a 16-byte MD4 hash */ - fr_md4_calc(szHash, (uint8_t *) szUnicodePass, (nPasswordLen<<1) ); -} - - -/* - * challenge_hash() is used by mschap2() and auth_response() - * implements RFC2759 ChallengeHash() - * generates 64 bit challenge - */ -static void challenge_hash( const uint8_t *peer_challenge, - const uint8_t *auth_challenge, - const char *user_name, uint8_t *challenge ) -{ - fr_SHA1_CTX Context; - uint8_t hash[20]; - - fr_SHA1Init(&Context); - fr_SHA1Update(&Context, peer_challenge, 16); - fr_SHA1Update(&Context, auth_challenge, 16); - fr_SHA1Update(&Context, (const uint8_t *) user_name, - strlen(user_name)); - fr_SHA1Final(hash, &Context); - memcpy(challenge, hash, 8); -} - -/* - * 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]; - } -} - - typedef struct rlm_mschap_t { int use_mppe; int require_encryption; @@ -298,7 +170,8 @@ static size_t mschap_xlat(void *instance, REQUEST *request, */ if (strncasecmp(fmt, "Challenge", 9) == 0) { chap_challenge = pairfind(request->packet->vps, - PW_MSCHAP_CHALLENGE); + PW_MSCHAP_CHALLENGE, + VENDORPEC_MICROSOFT); if (!chap_challenge) { RDEBUG2("No MS-CHAP-Challenge in the request."); return 0; @@ -319,17 +192,25 @@ static size_t mschap_xlat(void *instance, REQUEST *request, * for MS-CHAPv2. */ } else if (chap_challenge->length == 16) { + VALUE_PAIR *name_attr, *response_name; char *username_string; RDEBUG2(" mschap2: %02x", chap_challenge->vp_octets[0]); response = pairfind(request->packet->vps, - PW_MSCHAP2_RESPONSE); + PW_MSCHAP2_RESPONSE, + VENDORPEC_MICROSOFT); if (!response) { RDEBUG2("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge."); return 0; } /* + * FIXME: Much of this is copied from + * below. We should put it into a + * separate function. + */ + + /* * Responses are 50 octets. */ if (response->length < 50) { @@ -338,24 +219,46 @@ static size_t mschap_xlat(void *instance, REQUEST *request, } user_name = pairfind(request->packet->vps, - PW_USER_NAME); + PW_USER_NAME, 0); if (!user_name) { - RDEBUG2("User-Name is required to calculateMS-CHAPv1 Challenge."); + RDEBUG2("User-Name is required to calculate MS-CHAPv1 Challenge."); return 0; } + /* + * 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, 0); + if (response_name) { + name_attr = response_name; + } else { + name_attr = user_name; + } + /* * with_ntdomain_hack moved here, too. */ - if ((username_string = strchr(user_name->vp_strvalue, '\\')) != NULL) { + 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 = user_name->vp_strvalue; + username_string = name_attr->vp_strvalue; } } else { - username_string = user_name->vp_strvalue; + username_string = name_attr->vp_strvalue; + } + + if (response_name && + ((user_name->length != response_name->length) || + (strncasecmp(user_name->vp_strvalue, response_name->vp_strvalue, user_name->length) != 0))) { + RDEBUG("WARNING: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", user_name->vp_strvalue, response_name->vp_strvalue); } /* @@ -363,7 +266,9 @@ static size_t mschap_xlat(void *instance, REQUEST *request, * from the MS-CHAPv2 peer challenge, * our challenge, and the user name. */ - challenge_hash(response->vp_octets + 2, + RDEBUG2("Creating challenge hash with username: %s", + username_string); + mschap_challenge_hash(response->vp_octets + 2, chap_challenge->vp_octets, username_string, buffer); data = buffer; @@ -379,9 +284,10 @@ static size_t mschap_xlat(void *instance, REQUEST *request, */ } else if (strncasecmp(fmt, "NT-Response", 11) == 0) { response = pairfind(request->packet->vps, - PW_MSCHAP_RESPONSE); + PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT); if (!response) response = pairfind(request->packet->vps, - PW_MSCHAP2_RESPONSE); + PW_MSCHAP2_RESPONSE, + VENDORPEC_MICROSOFT); if (!response) { RDEBUG2("No MS-CHAP-Response or MS-CHAP2-Response was found in the request."); return 0; @@ -411,7 +317,7 @@ static size_t mschap_xlat(void *instance, REQUEST *request, */ } else if (strncasecmp(fmt, "LM-Response", 11) == 0) { response = pairfind(request->packet->vps, - PW_MSCHAP_RESPONSE); + PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT); if (!response) { RDEBUG2("No MS-CHAP-Response was found in the request."); return 0; @@ -434,7 +340,7 @@ static size_t mschap_xlat(void *instance, REQUEST *request, } else if (strncasecmp(fmt, "NT-Domain", 9) == 0) { char *p, *q; - user_name = pairfind(request->packet->vps, PW_USER_NAME); + user_name = pairfind(request->packet->vps, PW_USER_NAME, 0); if (!user_name) { RDEBUG2("No User-Name was found in the request."); return 0; @@ -489,7 +395,7 @@ static size_t mschap_xlat(void *instance, REQUEST *request, } else if (strncasecmp(fmt, "User-Name", 9) == 0) { char *p; - user_name = pairfind(request->packet->vps, PW_USER_NAME); + user_name = pairfind(request->packet->vps, PW_USER_NAME, 0); if (!user_name) { RDEBUG2("No User-Name was found in the request."); return 0; @@ -535,16 +441,25 @@ static size_t mschap_xlat(void *instance, REQUEST *request, */ } else if (strncasecmp(fmt, "NT-Hash ", 8) == 0) { char *p; + char buf2[1024]; 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); + + while (isspace(*p)) p++; + + if (!radius_xlat(buf2, sizeof(buf2),p,request,NULL)) { + RDEBUG("xlat failed"); + *buffer = '\0'; + return 0; + } + + mschap_ntpwdhash(buffer,buf2); fr_bin2hex(buffer, out, 16); out[32] = '\0'; - RDEBUG("rlm_mschap: NT-Hash: Result: %s",out); + RDEBUG("NT-Hash of %s = %s", buf2, out); return 32; /* @@ -552,16 +467,24 @@ static size_t mschap_xlat(void *instance, REQUEST *request, */ } else if (strncasecmp(fmt, "LM-Hash ", 8) == 0) { char *p; + char buf2[1024]; 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); + while (isspace(*p)) p++; + + if (!radius_xlat(buf2, sizeof(buf2),p,request,NULL)) { + RDEBUG("xlat failed"); + *buffer = '\0'; + return 0; + } + + smbdes_lmpwdhash(buf2, buffer); fr_bin2hex(buffer, out, 16); out[32] = '\0'; - RDEBUG("rlm_mschap: LM-Hash: Result: %s",out); + RDEBUG("LM-Hash of %s = %s", buf2, out); return 32; } else { RDEBUG2("Unknown expansion string \"%s\"", @@ -679,7 +602,7 @@ static int mschap_instantiate(CONF_SECTION *conf, void **instance) /* * For backwards compatibility */ - if (!dict_valbyname(PW_AUTH_TYPE, inst->xlat_name)) { + if (!dict_valbyname(PW_AUTH_TYPE, 0, inst->xlat_name)) { inst->auth_type = "MS-CHAP"; } else { inst->auth_type = inst->xlat_name; @@ -781,7 +704,24 @@ static int do_mschap(rlm_mschap_t *inst, buffer, sizeof(buffer), NULL, NULL, 1); if (result != 0) { + char *p; + VALUE_PAIR *vp = NULL; + RDEBUG2("External script failed."); + + vp = pairmake("Module-Failure-Message", "", T_OP_EQ); + if (!vp) { + radlog_request(L_ERR, 0, request, "No memory to allocate Module-Failure-Message"); + return RLM_MODULE_FAIL; + } + + p = strchr(buffer, '\n'); + if (p) *p = '\0'; + snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), + "%s: External script says %s", + inst->xlat_name, buffer); + vp->length = strlen(vp->vp_strvalue); + pairadd(&request->request->vps, vp); return -1; } @@ -946,14 +886,20 @@ static int mschap_authorize(void * instance, REQUEST *request) #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL, *response = NULL; - challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); + challenge = pairfind(request->packet->vps, + PW_MSCHAP_CHALLENGE, + VENDORPEC_MICROSOFT); if (!challenge) { return RLM_MODULE_NOOP; } - response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); + response = pairfind(request->packet->vps, + PW_MSCHAP_RESPONSE, + VENDORPEC_MICROSOFT); if (!response) - response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); + response = pairfind(request->packet->vps, + PW_MSCHAP2_RESPONSE, + VENDORPEC_MICROSOFT); /* * Nothing we recognize. Don't do anything. @@ -963,8 +909,8 @@ static int mschap_authorize(void * instance, REQUEST *request) return RLM_MODULE_NOOP; } - if (pairfind(request->config_items, PW_AUTH_TYPE)) { - RDEBUG2("Found existing Auth-Type. Not changing it."); + if (pairfind(request->config_items, PW_AUTH_TYPE, 0)) { + RDEBUG2("WARNING: Auth-Type already set. Not setting to MS-CHAP"); return RLM_MODULE_NOOP; } @@ -1019,7 +965,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) * If we have ntlm_auth configured, use it unless told * otherwise */ - do_ntlm_auth = inst->ntlm_auth; + do_ntlm_auth = (inst->ntlm_auth != NULL); /* * If we have an ntlm_auth configuration, then we may @@ -1027,7 +973,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) */ if (do_ntlm_auth) { VALUE_PAIR *vp = pairfind(request->config_items, - PW_MS_CHAP_USE_NTLM_AUTH); + PW_MS_CHAP_USE_NTLM_AUTH, 0); if (vp) do_ntlm_auth = vp->vp_integer; } @@ -1035,10 +981,10 @@ static int mschap_authenticate(void * instance, REQUEST *request) * Find the SMB-Account-Ctrl attribute, or the * SMB-Account-Ctrl-Text attribute. */ - smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL); + smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL, 0); if (!smb_ctrl) { password = pairfind(request->config_items, - PW_SMB_ACCOUNT_CTRL_TEXT); + PW_SMB_ACCOUNT_CTRL_TEXT, 0); if (password) { smb_ctrl = radius_pairmake(request, &request->config_items, @@ -1067,12 +1013,12 @@ static int mschap_authenticate(void * instance, REQUEST *request) /* * Decide how to get the passwords. */ - password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); + password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0); /* * We need an LM-Password. */ - lm_password = pairfind(request->config_items, PW_LM_PASSWORD); + lm_password = pairfind(request->config_items, PW_LM_PASSWORD, 0); if (lm_password) { /* * Allow raw octets. @@ -1107,7 +1053,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) /* * We need an NT-Password. */ - nt_password = pairfind(request->config_items, PW_NT_PASSWORD); + nt_password = pairfind(request->config_items, PW_NT_PASSWORD, 0); if (nt_password) { if ((nt_password->length == 16) || ((nt_password->length == 32) && @@ -1130,22 +1076,26 @@ static int mschap_authenticate(void * instance, REQUEST *request) radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { - ntpwdhash(nt_password->vp_octets, + mschap_ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; } } - challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); + challenge = pairfind(request->packet->vps, + PW_MSCHAP_CHALLENGE, + VENDORPEC_MICROSOFT); if (!challenge) { - RDEBUG2("No MS-CHAP-Challenge in the request"); + 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); + response = pairfind(request->packet->vps, + PW_MSCHAP_RESPONSE, + VENDORPEC_MICROSOFT); /* * MS-CHAP-Response, means MS-CHAPv1 @@ -1198,8 +1148,9 @@ static int mschap_authenticate(void * instance, REQUEST *request) chap = 1; - } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) { + } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT)) != NULL) { uint8_t mschapv1_challenge[16]; + VALUE_PAIR *name_attr, *response_name; /* * MS-CHAPv2 challenges are 16 octets. @@ -1220,25 +1171,47 @@ static int mschap_authenticate(void * instance, REQUEST *request) /* * We also require a User-Name */ - username = pairfind(request->packet->vps, PW_USER_NAME); + username = pairfind(request->packet->vps, PW_USER_NAME, 0); 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 + * 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. */ - if ((username_string = strchr(username->vp_strvalue, '\\')) != NULL) { - if (inst->with_ntdomain_hack) { - username_string++; + response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME, 0); + 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 = username->vp_strvalue; + RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); + username_string = name_attr->vp_strvalue; } } else { - username_string = username->vp_strvalue; + 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; } #ifdef __APPLE__ @@ -1265,7 +1238,9 @@ static int mschap_authenticate(void * instance, REQUEST *request) * 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 */ + 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 */ @@ -1283,13 +1258,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) return RLM_MODULE_REJECT; } - /* - * Get the NT-hash-hash, if necessary - */ - if (nt_password) { - } - - auth_response(username_string, /* without the domain */ + mschap_auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ response->vp_octets + 2, /* peer challenge */ @@ -1300,7 +1269,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) chap = 2; } else { /* Neither CHAPv1 or CHAPv2 response: die */ - radlog_request(L_AUTH, 0, request, "No MS-CHAP response found"); + RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_INVALID; }