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=645abb1efaa21c465062f462df5fc218f572a99b;hpb=d20c92703a0aaeea2b5dce449300b7726362c3f1;p=freeradius.git diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index 645abb1..62d45ac 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -20,46 +20,26 @@ * 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 RCSID("$Id$") -#include - -#include -#include -#include -#include - #include #include - -#include #include +#include +#include + +#include +#include "mschap.h" #include "smbdes.h" +#ifdef __APPLE__ +extern int od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair); +#endif + /* Allowable account control bits */ #define ACB_DISABLED 0x0001 /* 1 = User account disabled */ #define ACB_HOMDIRREQ 0x0002 /* 1 = Home directory required */ @@ -148,113 +128,18 @@ static int pdb_decode_acct_ctrl(const char *p) } -/* - * ntpwdhash converts Unicode password to 16-byte NT hash - * with MD4 - */ -static void ntpwdhash (unsigned char *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 */ - md4_calc(szHash, 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 ) -{ - SHA1_CTX Context; - uint8_t hash[20]; - - SHA1Init(&Context); - SHA1Update(&Context, peer_challenge, 16); - SHA1Update(&Context, auth_challenge, 16); - SHA1Update(&Context, user_name, strlen(user_name)); - 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, - char *peer_challenge, char *auth_challenge, - char *response) -{ - SHA1_CTX Context; - 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}; - - 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}; - - char challenge[8]; - uint8_t digest[20]; - - SHA1Init(&Context); - SHA1Update(&Context, nt_hash_hash, 16); - SHA1Update(&Context, ntresponse, 24); - SHA1Update(&Context, magic1, 39); - SHA1Final(digest, &Context); - challenge_hash(peer_challenge, auth_challenge, username, challenge); - SHA1Init(&Context); - SHA1Update(&Context, digest, 20); - SHA1Update(&Context, challenge, 8); - SHA1Update(&Context, magic2, 41); - 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] = '='; - lrad_bin2hex(digest, response + 2, 20); -} - - typedef struct rlm_mschap_t { int use_mppe; int require_encryption; int require_strong; int with_ntdomain_hack; /* this should be in another module */ char *passwd_file; - char *xlat_name; + const char *xlat_name; char *ntlm_auth; - char *auth_type; + const char *auth_type; +#ifdef __APPLE__ + int open_directory; +#endif } rlm_mschap_t; @@ -264,7 +149,7 @@ typedef struct rlm_mschap_t { * Pulls NT-Response, LM-Response, or Challenge from MSCHAP * attributes. */ -static int mschap_xlat(void *instance, REQUEST *request, +static size_t mschap_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, RADIUS_ESCAPE_STRING func) { @@ -275,7 +160,7 @@ static int mschap_xlat(void *instance, REQUEST *request, VALUE_PAIR *chap_challenge, *response; rlm_mschap_t *inst = instance; - chap_challenge = response = NULL; + response = NULL; func = func; /* -Wunused */ @@ -283,11 +168,12 @@ static int mschap_xlat(void *instance, REQUEST *request, * Challenge means MS-CHAPv1 challenge, or * hash of MS-CHAPv2 challenge, and peer challenge. */ - if (strcasecmp(fmt, "Challenge") == 0) { + if (strncasecmp(fmt, "Challenge", 9) == 0) { chap_challenge = pairfind(request->packet->vps, - PW_MSCHAP_CHALLENGE); + PW_MSCHAP_CHALLENGE, + VENDORPEC_MICROSOFT); if (!chap_challenge) { - DEBUG2(" rlm_mschap: No MS-CHAP-Challenge in the request."); + RDEBUG2("No MS-CHAP-Challenge in the request."); return 0; } @@ -296,7 +182,7 @@ static int mschap_xlat(void *instance, REQUEST *request, * for MS-CHAPv2 */ if (chap_challenge->length == 8) { - DEBUG2(" mschap1: %02x", + RDEBUG2(" mschap1: %02x", chap_challenge->vp_octets[0]); data = chap_challenge->vp_octets; data_len = 8; @@ -306,43 +192,73 @@ static int mschap_xlat(void *instance, REQUEST *request, * for MS-CHAPv2. */ } else if (chap_challenge->length == 16) { + VALUE_PAIR *name_attr, *response_name; char *username_string; - DEBUG2(" mschap2: %02x", chap_challenge->vp_octets[0]); + RDEBUG2(" mschap2: %02x", chap_challenge->vp_octets[0]); response = pairfind(request->packet->vps, - PW_MSCHAP2_RESPONSE); + PW_MSCHAP2_RESPONSE, + VENDORPEC_MICROSOFT); if (!response) { - DEBUG2(" rlm_mschap: MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge."); + 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) { - radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format."); + radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return 0; } user_name = pairfind(request->packet->vps, - PW_USER_NAME); + PW_USER_NAME, 0); if (!user_name) { - DEBUG2(" rlm_mschap: 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 { - DEBUG2(" rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); - username_string = user_name->vp_strvalue; + RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); + 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); } /* @@ -350,27 +266,30 @@ static int 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; data_len = 8; } else { - DEBUG2(" rlm_mschap: Invalid MS-CHAP challenge length"); + RDEBUG2("Invalid MS-CHAP challenge length"); return 0; } - + /* * Get the MS-CHAPv1 response, or the MS-CHAPv2 * response. */ - } else if (strcasecmp(fmt, "NT-Response") == 0) { + } 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) { - DEBUG2(" rlm_mschap: No MS-CHAP-Response or MS-CHAP2-Response was found in the request."); + RDEBUG2("No MS-CHAP-Response or MS-CHAP2-Response was found in the request."); return 0; } @@ -380,7 +299,7 @@ static int mschap_xlat(void *instance, REQUEST *request, */ if ((response->attribute == PW_MSCHAP_RESPONSE) && ((response->vp_octets[1] & 0x01) == 0)) { - DEBUG2(" rlm_mschap: No NT-Response in MS-CHAP-Response"); + RDEBUG2("No NT-Response in MS-CHAP-Response"); return 0; } @@ -391,16 +310,16 @@ static int mschap_xlat(void *instance, REQUEST *request, */ data = response->vp_octets + 26; data_len = 24; - + /* * LM-Response is deprecated, and exists only * in MS-CHAPv1, and not often there. */ - } else if (strcasecmp(fmt, "LM-Response") == 0) { + } else if (strncasecmp(fmt, "LM-Response", 11) == 0) { response = pairfind(request->packet->vps, - PW_MSCHAP_RESPONSE); + PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT); if (!response) { - DEBUG2(" rlm_mschap: No MS-CHAP-Response was found in the request."); + RDEBUG2("No MS-CHAP-Response was found in the request."); return 0; } @@ -409,7 +328,7 @@ static int mschap_xlat(void *instance, REQUEST *request, * if the second octet says so. */ if ((response->vp_octets[1] & 0x01) != 0) { - DEBUG2(" rlm_mschap: No LM-Response in MS-CHAP-Response"); + RDEBUG2("No LM-Response in MS-CHAP-Response"); return 0; } data = response->vp_octets + 2; @@ -418,15 +337,15 @@ static int mschap_xlat(void *instance, REQUEST *request, /* * Pull the NT-Domain out of the User-Name, if it exists. */ - } else if (strcasecmp(fmt, "NT-Domain") == 0) { + } 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) { - DEBUG2(" rlm_mschap: No User-Name was found in the request."); + 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) @@ -440,7 +359,7 @@ static int mschap_xlat(void *instance, REQUEST *request, */ p = strchr(user_name->vp_strvalue, '.'); if (!p) { - DEBUG2(" rlm_mschap: setting NT-Domain to same as machine name"); + RDEBUG2("setting NT-Domain to same as machine name"); strlcpy(out, user_name->vp_strvalue + 5, outlen); } else { p++; /* skip the period */ @@ -456,7 +375,7 @@ static int mschap_xlat(void *instance, REQUEST *request, } else { p = strchr(user_name->vp_strvalue, '\\'); if (!p) { - DEBUG2(" rlm_mschap: No NT-Domain was found in the User-Name."); + RDEBUG2("No NT-Domain was found in the User-Name."); return 0; } @@ -473,15 +392,15 @@ static int mschap_xlat(void *instance, REQUEST *request, /* * Pull the User-Name out of the User-Name... */ - } else if (strcasecmp(fmt, "User-Name") == 0) { + } 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) { - DEBUG2(" rlm_mschap: No User-Name was found in the request."); + 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) @@ -522,16 +441,25 @@ static int 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; - DEBUG("rlm_mschap: NT-Hash: %s",p); - ntpwdhash(buffer,p); - lrad_bin2hex(buffer, out, 16); + 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'; - DEBUG("rlm_mschap: NT-Hash: Result: %s",out); + RDEBUG("NT-Hash of %s = %s", buf2, out); return 32; /* @@ -539,19 +467,27 @@ static int 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; - - DEBUG("rlm_mschap: LM-Hash: %s",p); - smbdes_lmpwdhash(p,buffer); - lrad_bin2hex(buffer, out, 16); + + 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'; - DEBUG("rlm_mschap: LM-Hash: Result: %s",out); + RDEBUG("LM-Hash of %s = %s", buf2, out); return 32; } else { - DEBUG2(" rlm_mschap: Unknown expansion string \"%s\"", + RDEBUG2("Unknown expansion string \"%s\"", fmt); return 0; } @@ -562,7 +498,7 @@ static int mschap_xlat(void *instance, REQUEST *request, * Didn't set anything: this is bad. */ if (!data) { - DEBUG2(" rlm_mschap: Failed to do anything intelligent"); + RDEBUG2("Failed to do anything intelligent"); return 0; } @@ -574,13 +510,13 @@ static int mschap_xlat(void *instance, REQUEST *request, } /* - * + * */ for (i = 0; i < data_len; i++) { sprintf(out + (2 * i), "%02x", data[i]); } out[data_len * 2] = '\0'; - + return data_len * 2; } @@ -601,6 +537,10 @@ static const CONF_PARSER module_config[] = { offsetof(rlm_mschap_t, passwd_file), NULL, NULL }, { "ntlm_auth", PW_TYPE_STRING_PTR, offsetof(rlm_mschap_t, ntlm_auth), NULL, NULL }, +#ifdef __APPLE__ + { "use_open_directory", PW_TYPE_BOOLEAN, + offsetof(rlm_mschap_t,open_directory), NULL, "yes" }, +#endif { NULL, -1, 0, NULL, NULL } /* end the list */ }; @@ -626,7 +566,6 @@ static int mschap_detach(void *instance){ */ static int mschap_instantiate(CONF_SECTION *conf, void **instance) { - const char *xlat_name; rlm_mschap_t *inst; inst = *instance = rad_malloc(sizeof(*inst)); @@ -655,21 +594,18 @@ static int mschap_instantiate(CONF_SECTION *conf, void **instance) /* * Create the dynamic translation. */ - if (cf_section_name1(conf)) - xlat_register(cf_section_name1(conf),mschap_xlat, inst); - - if ((xlat_name = cf_section_name2(conf)) != NULL) - xlat_register(xlat_name, mschap_xlat, inst); - if (xlat_name == NULL) - xlat_name = cf_section_name1(conf); - if (xlat_name) - inst->xlat_name = strdup(xlat_name); + inst->xlat_name = cf_section_name2(conf); + if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf); + inst->xlat_name = strdup(inst->xlat_name); + xlat_register(inst->xlat_name, mschap_xlat, inst); /* * 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; } return 0; @@ -679,13 +615,13 @@ static int mschap_instantiate(CONF_SECTION *conf, void **instance) * add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error * attribute to reply packet */ -static void add_reply(VALUE_PAIR** vp, unsigned char ident, +void mschap_add_reply(REQUEST *request, VALUE_PAIR** vp, unsigned char ident, const char* name, const char* value, int len) { VALUE_PAIR *reply_attr; reply_attr = pairmake(name, "", T_OP_EQ); if (!reply_attr) { - DEBUG(" rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr); + RDEBUG("Failed to create attribute %s: %s\n", name, fr_strerror()); return; } @@ -698,19 +634,18 @@ static void add_reply(VALUE_PAIR** vp, unsigned char ident, /* * Add MPPE attributes to the reply. */ -static void mppe_add_reply(VALUE_PAIR **vp, - const char* name, const char* value, int len) +static void mppe_add_reply(REQUEST *request, + const char* name, const uint8_t * value, int len) { - VALUE_PAIR *reply_attr; - reply_attr = pairmake(name, "", T_OP_EQ); - if (!reply_attr) { - DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr); + VALUE_PAIR *vp; + vp = radius_pairmake(request, &request->reply->vps, name, "", T_OP_EQ); + if (!vp) { + RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror()); return; } - memcpy(reply_attr->vp_octets, value, len); - reply_attr->length = len; - pairadd(vp, reply_attr); + memcpy(vp->vp_octets, value, len); + vp->length = len; } @@ -724,35 +659,9 @@ static void mppe_add_reply(VALUE_PAIR **vp, static int do_mschap(rlm_mschap_t *inst, REQUEST *request, VALUE_PAIR *password, uint8_t *challenge, uint8_t *response, - uint8_t *nthashhash) + uint8_t *nthashhash, int do_ntlm_auth) { - int do_ntlm_auth = 0; uint8_t calculated[24]; - VALUE_PAIR *vp = NULL; - - /* - * If we have ntlm_auth configured, use it unless told - * otherwise - */ - if (inst->ntlm_auth) do_ntlm_auth = 1; - - /* - * If we have an ntlm_auth configuration, then we may - * want to use it. - */ - vp = pairfind(request->config_items, - PW_MS_CHAP_USE_NTLM_AUTH); - if (vp) do_ntlm_auth = vp->lvalue; - - /* - * No ntlm_auth configured, attribute to tell us to - * use it exists, and we're told to use it. We don't - * know what to do... - */ - if (!inst->ntlm_auth && do_ntlm_auth) { - DEBUG2(" rlm_mschap: Asked to use ntlm_auth, but it was not configured in the mschap{} section."); - return -1; - } /* * Do normal authentication. @@ -762,22 +671,22 @@ static int do_mschap(rlm_mschap_t *inst, * No password: can't do authentication. */ if (!password) { - DEBUG2(" rlm_mschap: FAILED: No NT/LM-Password. Cannot perform authentication."); + RDEBUG2("FAILED: No NT/LM-Password. Cannot perform authentication."); return -1; } - + smbdes_mschap(password->vp_strvalue, challenge, calculated); if (memcmp(response, calculated, 24) != 0) { return -1; } - + /* * If the password exists, and is an NT-Password, * then calculate the hash of the NT hash. Doing this * here minimizes work for later. */ if (password && (password->attribute == PW_NT_PASSWORD)) { - md4_calc(nthashhash, password->vp_strvalue, 16); + fr_md4_calc(nthashhash, password->vp_octets, 16); } else { memset(nthashhash, 0, 16); } @@ -788,14 +697,31 @@ static int do_mschap(rlm_mschap_t *inst, memset(nthashhash, 0, 16); /* - * Run the program, and expect that we get 16 + * Run the program, and expect that we get 16 */ result = radius_exec_program(inst->ntlm_auth, request, TRUE, /* wait */ buffer, sizeof(buffer), NULL, NULL, 1); if (result != 0) { - DEBUG2(" rlm_mschap: External script failed."); + 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; } @@ -806,7 +732,7 @@ static int do_mschap(rlm_mschap_t *inst, * NT_KEY: 000102030405060708090a0b0c0d0e0f */ if (memcmp(buffer, "NT_KEY: ", 8) != 0) { - DEBUG2(" rlm_mschap: Invalid output from ntlm_auth: expecting NT_KEY"); + RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY"); return -1; } @@ -815,15 +741,15 @@ static int do_mschap(rlm_mschap_t *inst, * with an LF at the end. */ if (strlen(buffer + 8) < 32) { - DEBUG2(" rlm_mschap: Invalid output from ntlm_auth: NT_KEY has unexpected length"); + RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length"); return -1; } /* * Update the NT hash hash, from the NT key. */ - if (lrad_hex2bin(buffer + 8, nthashhash, 16) != 16) { - DEBUG2(" rlm_mschap: Invalid output from ntlm_auth: NT_KEY has non-hex values"); + if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) { + RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values"); return -1; } } @@ -879,13 +805,13 @@ static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response, uint8_t *masterkey) { uint8_t digest[20]; - SHA1_CTX Context; + fr_SHA1_CTX Context; - SHA1Init(&Context); - SHA1Update(&Context,nt_hashhash,16); - SHA1Update(&Context,nt_response,24); - SHA1Update(&Context,magic1,27); - SHA1Final(digest,&Context); + fr_SHA1Init(&Context); + fr_SHA1Update(&Context,nt_hashhash,16); + fr_SHA1Update(&Context,nt_response,24); + fr_SHA1Update(&Context,magic1,27); + fr_SHA1Final(digest,&Context); memcpy(masterkey,digest,16); } @@ -896,7 +822,7 @@ static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey, { uint8_t digest[20]; const uint8_t *s; - SHA1_CTX Context; + fr_SHA1_CTX Context; memset(digest,0,20); @@ -906,12 +832,12 @@ static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey, s = magic2; } - SHA1Init(&Context); - SHA1Update(&Context,masterkey,16); - SHA1Update(&Context,SHSpad1,40); - SHA1Update(&Context,s,84); - SHA1Update(&Context,SHSpad2,40); - SHA1Final(digest,&Context); + fr_SHA1Init(&Context); + fr_SHA1Update(&Context,masterkey,16); + fr_SHA1Update(&Context,SHSpad1,40); + fr_SHA1Update(&Context,s,84); + fr_SHA1Update(&Context,SHSpad2,40); + fr_SHA1Final(digest,&Context); memcpy(sesskey,digest,keylen); } @@ -959,36 +885,46 @@ static int mschap_authorize(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL, *response = NULL; - VALUE_PAIR *vp; - 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. */ if (!response) { - DEBUG2(" rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response."); + RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP-Response."); return RLM_MODULE_NOOP; } - DEBUG2(" rlm_mschap: Found MS-CHAP attributes. Setting 'Auth-Type = %s'", inst->xlat_name); + if (pairfind(request->config_items, PW_AUTH_TYPE, 0)) { + RDEBUG2("WARNING: Auth-Type already set. Not setting to MS-CHAP"); + return RLM_MODULE_NOOP; + } + + RDEBUG2("Found MS-CHAP attributes. Setting 'Auth-Type = %s'", inst->xlat_name); /* * Set Auth-Type to MS-CHAP. The authentication code * will take care of turning clear-text passwords into * NT/LM passwords. */ - vp = pairmake("Auth-Type", inst->auth_type, T_OP_EQ); - if (!vp) return RLM_MODULE_FAIL; - pairmove(&request->config_items, &vp); - pairfree(&vp); /* may be NULL */ + if (!radius_pairmake(request, &request->config_items, + "Auth-Type", inst->auth_type, T_OP_EQ)) { + return RLM_MODULE_FAIL; + } return RLM_MODULE_OK; #undef inst @@ -1019,24 +955,44 @@ static int mschap_authenticate(void * instance, REQUEST *request) VALUE_PAIR *password = NULL; VALUE_PAIR *lm_password, *nt_password, *smb_ctrl; VALUE_PAIR *username; - VALUE_PAIR *reply_attr; uint8_t nthashhash[16]; - uint8_t msch2resp[42]; + 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, 0); + 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); + 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 = pairmake("SMB-Account-CTRL", "0", T_OP_SET); - pairadd(&request->config_items, smb_ctrl); - smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->vp_strvalue); + 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); + } } } @@ -1048,8 +1004,8 @@ static int mschap_authenticate(void * instance, REQUEST *request) /* * Password is not required. */ - if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) { - DEBUG2(" rlm_mschap: SMB-Account-Ctrl says no password is required."); + if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) { + RDEBUG2("SMB-Account-Ctrl says no password is required."); return RLM_MODULE_OK; } } @@ -1057,85 +1013,89 @@ 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. */ if ((lm_password->length == 16) || ((lm_password->length == 32) && - (lrad_hex2bin(lm_password->vp_strvalue, - lm_password->vp_strvalue, 16) == 16))) { - DEBUG2(" rlm_mschap: Found LM-Password"); + (fr_hex2bin(lm_password->vp_strvalue, + lm_password->vp_octets, 16) == 16))) { + RDEBUG2("Found LM-Password"); lm_password->length = 16; } else { - radlog(L_ERR, "rlm_mschap: Invalid LM-Password"); + radlog_request(L_ERR, 0, request, "Invalid LM-Password"); lm_password = NULL; } } else if (!password) { - DEBUG2(" rlm_mschap: No Cleartext-Password configured. Cannot create LM-Password."); + if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password."); } else { /* there is a configured Cleartext-Password */ - lm_password = pairmake("LM-Password", "", T_OP_EQ); + lm_password = radius_pairmake(request, &request->config_items, + "LM-Password", "", T_OP_EQ); if (!lm_password) { - radlog(L_ERR, "No memory"); + radlog_request(L_ERR, 0, request, "No memory"); } else { smbdes_lmpwdhash(password->vp_strvalue, - lm_password->vp_strvalue); + lm_password->vp_octets); lm_password->length = 16; - pairadd(&request->config_items, lm_password); } } /* * 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) && - (lrad_hex2bin(nt_password->vp_strvalue, - nt_password->vp_strvalue, 16) == 16))) { - DEBUG2(" rlm_mschap: Found NT-Password"); + (fr_hex2bin(nt_password->vp_strvalue, + nt_password->vp_octets, 16) == 16))) { + RDEBUG2("Found NT-Password"); nt_password->length = 16; } else { - radlog(L_ERR, "rlm_mschap: Invalid NT-Password"); + radlog_request(L_ERR, 0, request, "Invalid NT-Password"); nt_password = NULL; } } else if (!password) { - DEBUG2(" rlm_mschap: No Cleartext-Password configured. Cannot create NT-Password."); + if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password."); } else { /* there is a configured Cleartext-Password */ - nt_password = pairmake("NT-Password", "", T_OP_EQ); + nt_password = radius_pairmake(request, &request->config_items, + "NT-Password", "", T_OP_EQ); if (!nt_password) { - radlog(L_ERR, "No memory"); + radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { - ntpwdhash(nt_password->vp_strvalue, + mschap_ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; - pairadd(&request->config_items, nt_password); } } - challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); + challenge = pairfind(request->packet->vps, + PW_MSCHAP_CHALLENGE, + VENDORPEC_MICROSOFT); if (!challenge) { - DEBUG2(" rlm_mschap: 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 @@ -1147,7 +1107,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) * MS-CHAPv1 challenges are 8 octets. */ if (challenge->length < 8) { - radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format."); + radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } @@ -1155,7 +1115,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) * Responses are 50 octets. */ if (response->length < 50) { - radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format."); + radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } @@ -1164,11 +1124,11 @@ static int mschap_authenticate(void * instance, REQUEST *request) * response */ if (response->vp_octets[1] & 0x01) { - DEBUG2(" rlm_mschap: Told to do MS-CHAPv1 with NT-Password"); + RDEBUG2("Told to do MS-CHAPv1 with NT-Password"); password = nt_password; offset = 26; } else { - DEBUG2(" rlm_mschap: Told to do MS-CHAPv1 with LM-Password"); + RDEBUG2("Told to do MS-CHAPv1 with LM-Password"); password = lm_password; offset = 2; } @@ -1177,23 +1137,26 @@ static int mschap_authenticate(void * instance, REQUEST *request) * Do the MS-CHAP authentication. */ if (do_mschap(inst, request, password, challenge->vp_octets, - response->vp_octets + offset, nthashhash) < 0) { - DEBUG2(" rlm_mschap: MS-CHAP-Response is incorrect."); - add_reply(&request->reply->vps, *response->vp_octets, - "MS-CHAP-Error", "E=691 R=1", 9); + 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) { + } 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. */ if (challenge->length < 16) { - radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format."); + radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } @@ -1201,34 +1164,73 @@ static int mschap_authenticate(void * instance, REQUEST *request) * Responses are 50 octets. */ if (response->length < 50) { - radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format."); + 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); + username = pairfind(request->packet->vps, PW_USER_NAME, 0); if (!username) { - radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2"); + 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 { - DEBUG2(" rlm_mschap: 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__ + /* + * 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. @@ -1236,40 +1238,38 @@ 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 */ - - DEBUG2(" rlm_mschap: Told to do MS-CHAPv2 for %s with NT-Password", + + 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) < 0) { - DEBUG2(" rlm_mschap: FAILED: MS-CHAP2-Response is incorrect"); - add_reply(&request->reply->vps, *response->vp_octets, - "MS-CHAP-Error", "E=691 R=1", 9); + 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 */ + 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 */ - add_reply( &request->reply->vps, *response->vp_octets, - "MS-CHAP2-Success", msch2resp, 42); + 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(L_AUTH, "rlm_mschap: 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; } @@ -1285,21 +1285,23 @@ static int mschap_authenticate(void * instance, REQUEST *request) * They're found, but they don't exist, so we * return 'not found'. */ - if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) || - ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) { - DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account."); - add_reply( &request->reply->vps, *response->vp_octets, - "MS-CHAP-Error", "E=691 R=1", 9); + 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->lvalue & ACB_AUTOLOCK) != 0) { - DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is locked out."); - add_reply( &request->reply->vps, *response->vp_octets, - "MS-CHAP-Error", "E=647 R=0", 9); + 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; } } @@ -1310,7 +1312,7 @@ static int mschap_authenticate(void * instance, REQUEST *request) uint8_t mppe_recvkey[34]; if (chap == 1){ - DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys"); + RDEBUG2("adding MS-CHAPv1 MPPE keys"); memset(mppe_sendkey, 0, 32); if (lm_password) { memcpy(mppe_sendkey, lm_password->vp_octets, 8); @@ -1330,34 +1332,31 @@ static int mschap_authenticate(void * instance, REQUEST *request) */ memcpy(mppe_sendkey + 8, nthashhash, 16); - mppe_add_reply(&request->reply->vps, + mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32); } else if (chap == 2) { - DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys"); + RDEBUG2("adding MS-CHAPv2 MPPE keys"); mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey); - - mppe_add_reply(&request->reply->vps, + + mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16); - mppe_add_reply(&request->reply->vps, + mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16); } - reply_attr = pairmake("MS-MPPE-Encryption-Policy", - (inst->require_encryption)? "0x00000002":"0x00000001", - T_OP_EQ); - rad_assert(reply_attr != NULL); - pairadd(&request->reply->vps, reply_attr); - reply_attr = pairmake("MS-MPPE-Encryption-Types", - (inst->require_strong)? "0x00000004":"0x00000006", - T_OP_EQ); - rad_assert(reply_attr != NULL); - pairadd(&request->reply->vps, reply_attr); - + 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;