6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000,2001 The FreeRADIUS server project
25 * mschap.c MS-CHAP module
27 * This implements MS-CHAP, as described in RFC 2548
29 * http://www.freeradius.org/rfc/rfc2548.txt
34 * If you have any questions on NTLM (Samba) passwords
35 * support, LM authentication and MS-CHAP v2 support
38 * Vladimir Dubrovin vlad@sandy.ru
40 * ZARAZA 3APA3A@security.nnov.ru
43 /* MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
46 #include "libradius.h"
59 #include "rad_assert.h"
61 #define PW_MSCHAP_RESPONSE ((311 << 16) | 1)
62 #define PW_MSCHAP_CHALLENGE ((311 << 16) | 11)
63 #define PW_MSCHAP2_RESPONSE ((311 << 16) | 25)
65 static const char *letters = "0123456789ABCDEF";
68 * hex2bin converts hexadecimal strings into binary
70 static int hex2bin (const char *szHex, unsigned char* szBin, int len)
75 for (i = 0; i < len; i++) {
76 if( !(c1 = memchr(letters, toupper((int) szHex[i << 1]), 16)) ||
77 !(c2 = memchr(letters, toupper((int) szHex[(i << 1) + 1]), 16)))
79 szBin[i] = ((c1-letters)<<4) + (c2-letters);
85 * bin2hex creates hexadecimal presentation
88 static void bin2hex (const unsigned char *szBin, char *szHex, int len)
91 for (i = 0; i < len; i++) {
92 szHex[i<<1] = letters[szBin[i] >> 4];
93 szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F];
98 /* Allowable account control bits */
99 #define ACB_DISABLED 0x0001 /* 1 = User account disabled */
100 #define ACB_HOMDIRREQ 0x0002 /* 1 = Home directory required */
101 #define ACB_PWNOTREQ 0x0004 /* 1 = User password not required */
102 #define ACB_TEMPDUP 0x0008 /* 1 = Temporary duplicate account */
103 #define ACB_NORMAL 0x0010 /* 1 = Normal user account */
104 #define ACB_MNS 0x0020 /* 1 = MNS logon user account */
105 #define ACB_DOMTRUST 0x0040 /* 1 = Interdomain trust account */
106 #define ACB_WSTRUST 0x0080 /* 1 = Workstation trust account */
107 #define ACB_SVRTRUST 0x0100 /* 1 = Server trust account */
108 #define ACB_PWNOEXP 0x0200 /* 1 = User password does not expire */
109 #define ACB_AUTOLOCK 0x0400 /* 1 = Account auto locked */
111 static int pdb_decode_acct_ctrl(const char *p)
117 * Check if the account type bits have been encoded after the
118 * NT password (in the form [NDHTUWSLXI]).
121 if (*p != '[') return 0;
123 for (p++; *p && !finished; p++)
127 case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
128 case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
129 case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
130 case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
131 case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
132 case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
133 case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
134 case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
135 case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
136 case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
137 case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
143 default: { finished = 1; }
152 * ntpwdhash converts Unicode password to 16-byte NT hash
155 static void ntpwdhash (char *szHash, const char *szPassword)
157 char szUnicodePass[513];
162 * NT passwords are unicode. Convert plain text password
163 * to unicode by inserting a zero every other byte
165 nPasswordLen = strlen(szPassword);
166 for (i = 0; i < nPasswordLen; i++) {
167 szUnicodePass[i << 1] = szPassword[i];
168 szUnicodePass[(i << 1) + 1] = 0;
171 /* Encrypt Unicode password to a 16-byte MD4 hash */
172 md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
177 * challenge_hash() is used by mschap2() and auth_response()
178 * implements RFC2759 ChallengeHash()
179 * generates 64 bit challenge
181 static void challenge_hash( const char *peer_challenge,
182 const char *auth_challenge,
183 const char *user_name, char *challenge )
189 SHA1Update(&Context, peer_challenge, 16);
190 SHA1Update(&Context, auth_challenge, 16);
191 SHA1Update(&Context, user_name, strlen(user_name));
192 SHA1Final(hash, &Context);
193 memcpy(challenge, hash, 8);
196 static void mschap2(const char *peer_challenge, const char *auth_challenge,
197 const char *user_name, const char *nt_password,
202 challenge_hash(peer_challenge, auth_challenge, user_name,
205 lrad_mschap(nt_password, challenge, response);
209 * auth_response() generates MS-CHAP v2 SUCCESS response
210 * according to RFC 2759 GenerateAuthenticatorResponse()
211 * returns 42-octet response string
213 static void auth_response(const char *username, const char *nt_password,
215 char *peer_challenge, char *auth_challenge,
220 const char magic1[39] =
221 {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
222 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
223 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
224 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
226 const char magic2[41] =
227 {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
228 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
229 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
230 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
237 * Hash password hash into hashhash
240 md4_calc(hashhash, nt_password, 16);
243 SHA1Update(&Context, hashhash, 16);
244 SHA1Update(&Context, ntresponse, 24);
245 SHA1Update(&Context, magic1, 39);
246 SHA1Final(digest, &Context);
247 challenge_hash(peer_challenge, auth_challenge, username, challenge);
249 SHA1Update(&Context, digest, 20);
250 SHA1Update(&Context, challenge, 8);
251 SHA1Update(&Context, magic2, 41);
252 SHA1Final(digest, &Context);
255 * Encode the value of 'Digest' as "S=" followed by
256 * 40 ASCII hexadecimal digits and return it in
257 * AuthenticatorResponse.
259 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
263 bin2hex(digest, response + 2, 20);
266 struct mschap_instance {
268 int require_encryption;
274 static CONF_PARSER module_config[] = {
276 * Cache the password by default.
278 { "use_mppe", PW_TYPE_BOOLEAN,
279 offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
280 { "require_encryption", PW_TYPE_BOOLEAN,
281 offsetof(struct mschap_instance,require_encryption), NULL, "no" },
282 { "require_strong", PW_TYPE_BOOLEAN,
283 offsetof(struct mschap_instance,require_strong), NULL, "no" },
284 { "passwd", PW_TYPE_STRING_PTR,
285 offsetof(struct mschap_instance, passwd_file), NULL, NULL },
286 { "authtype", PW_TYPE_STRING_PTR,
287 offsetof(struct mschap_instance, auth_type), NULL, NULL },
289 { NULL, -1, 0, NULL, NULL } /* end the list */
293 * deinstantiate module, free all memory allocated during
294 * mschap_instantiate()
296 static int mschap_detach(void *instance){
297 #define inst ((struct mschap_instance *)instance)
298 if (inst->passwd_file) free(inst->passwd_file);
299 if (inst->auth_type) free(inst->auth_type);
306 * Create instance for our module. Allocate space for
307 * instance structure and read configuration parameters
309 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
311 struct mschap_instance *inst;
313 inst = *instance = rad_malloc(sizeof(struct mschap_instance));
314 if (cf_section_parse(conf, inst, module_config) < 0) {
320 * This module used to support SMB Password files, but it
321 * made it too complicated. If the user tries to
322 * configure an SMB Password file, then die, with an
325 if (inst->passwd_file) {
326 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module. Use rlm_passwd module instead");
335 * add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
336 * attribute to reply packet
338 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
339 const char* name, const char* value, int len)
341 VALUE_PAIR *reply_attr;
342 reply_attr = pairmake(name, "", T_OP_EQ);
344 DEBUG(" rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr);
348 reply_attr->strvalue[0] = ident;
349 memcpy(reply_attr->strvalue + 1, value, len);
350 reply_attr->length = len + 1;
351 pairadd(vp, reply_attr);
355 * Add MPPE attributes to the reply.
357 static void mppe_add_reply(VALUE_PAIR** vp,
358 const char* name, const char* value, int len)
360 VALUE_PAIR *reply_attr;
361 reply_attr = pairmake(name, "", T_OP_EQ);
363 DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
367 memcpy(reply_attr->strvalue, value, len);
368 reply_attr->length = len;
369 pairadd(vp, reply_attr);
372 static const uint8_t SHSpad1[40] =
373 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
378 static const uint8_t SHSpad2[40] =
379 { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
380 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
381 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
382 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
384 static const uint8_t magic1[27] =
385 { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
386 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
387 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
389 static const uint8_t magic2[84] =
390 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
391 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
392 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
393 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
394 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
395 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
396 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
397 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
398 0x6b, 0x65, 0x79, 0x2e };
400 static const uint8_t magic3[84] =
401 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
402 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
403 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
404 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
405 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
406 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
407 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
408 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
409 0x6b, 0x65, 0x79, 0x2e };
412 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
419 SHA1Update(&Context,nt_hashhash,16);
420 SHA1Update(&Context,nt_response,24);
421 SHA1Update(&Context,magic1,27);
422 SHA1Final(digest,&Context);
424 memcpy(masterkey,digest,16);
428 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
429 int keylen,int issend)
444 SHA1Update(&Context,masterkey,16);
445 SHA1Update(&Context,SHSpad1,40);
446 SHA1Update(&Context,s,84);
447 SHA1Update(&Context,SHSpad2,40);
448 SHA1Final(digest,&Context);
450 memcpy(sesskey,digest,keylen);
454 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
455 uint8_t *sendkey,uint8_t *recvkey)
457 uint8_t masterkey[16];
459 mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
461 mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
462 mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
466 * Generate MPPE keys.
468 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
469 uint8_t *nt_hash,uint8_t *response,
470 uint8_t *sendkey,uint8_t *recvkey)
474 /* uint8_t salt[2]; */
475 uint8_t nt_hashhash[16];
477 md4_calc(nt_hashhash,nt_hash,16);
479 mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
482 * dictionary.microsoft defines these attributes as
483 * 'encrypt=2'. The functions in src/lib/radius.c will
484 * take care of encrypting/decrypting them as appropriate,
485 * so that we don't have to.
487 memcpy (sendkey, enckey1, 16);
488 memcpy (recvkey, enckey2, 16);
493 * mschap_authorize() - authorize user if we can authenticate
494 * it later. Add Auth-Type attribute if present in module
495 * configuration (usually Auth-Type must be "MS-CHAP")
497 static int mschap_authorize(void * instance, REQUEST *request)
499 #define inst ((struct mschap_instance *)instance)
500 VALUE_PAIR *challenge = NULL, *response = NULL;
502 const char *authtype_name = "MS-CHAP";
504 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
506 return RLM_MODULE_NOOP;
509 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
511 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
514 * Nothing we recognize. Don't do anything.
517 DEBUG2(" rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
518 return RLM_MODULE_NOOP;
522 * Choose MS-CHAP, or whatever else they told us to use.
524 if (inst->auth_type) {
525 authtype_name = inst->auth_type;
528 DEBUG2(" rlm_mschap: Found MS-CHAP attributes. Setting 'Auth-Type := %s'", authtype_name);
531 * Set Auth-Type to MS-CHAP. The authentication code
532 * will take care of turning clear-text passwords into
535 pairdelete(&request->config_items, PW_AUTHTYPE);
536 vp = pairmake("Auth-Type", authtype_name, T_OP_SET);
537 rad_assert(vp != NULL);
538 pairadd(&request->config_items, vp);
540 return RLM_MODULE_OK;
545 * mschap_authenticate() - authenticate user based on given
546 * attributes and configuration.
547 * We will try to find out password in configuration
548 * or in configured passwd file.
549 * If one is found we will check paraneters given by NAS.
551 * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
553 * PAP: PW_PASSWORD or
554 * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
555 * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
556 * In case of password mismatch or locked account we MAY return
557 * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
558 * If MS-CHAP2 succeeds we MUST return
561 static int mschap_authenticate(void * instance, REQUEST *request)
563 #define inst ((struct mschap_instance *)instance)
564 VALUE_PAIR *challenge = NULL, *response = NULL;
565 VALUE_PAIR *password = NULL;
566 VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
567 VALUE_PAIR *reply_attr;
568 uint8_t calculated[32];
569 uint8_t msch2resp[42];
570 uint8_t mppe_sendkey[34];
571 uint8_t mppe_recvkey[34];
575 * Find the SMB-Account-Ctrl attribute, or the
576 * SMB-Account-Ctrl-Text attribute.
578 smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
580 password = pairfind(request->config_items,
581 PW_SMB_ACCOUNT_CTRL_TEXT);
583 smb_ctrl = pairmake("SMB-Account-CTRL", "", T_OP_SET);
584 pairadd(&request->config_items, smb_ctrl);
585 smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
590 * We're configured to do MS-CHAP authentication.
591 * and account control information exists. Enforce it.
595 * Password is not required.
597 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
598 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says no password is required.");
599 return RLM_MODULE_OK;
604 * Decide how to get the passwords.
606 password = pairfind(request->config_items, PW_PASSWORD);
609 * We need an LM-Password.
611 lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
616 if ((lm_password->length == 16) ||
617 ((lm_password->length == 32) &&
618 (hex2bin(lm_password->strvalue,
619 lm_password->strvalue, 16) == 16))) {
620 DEBUG2(" rlm_mschap: Found LM-Password");
621 lm_password->length = 16;
624 radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
628 } else if (!password) {
629 DEBUG2(" rlm_mschap: No User-Password configured. Cannot create LM-Password.");
631 } else { /* there is a configured User-Password */
632 lm_password = pairmake("LM-Password", "", T_OP_EQ);
634 radlog(L_ERR, "No memory");
636 lrad_lmpwdhash(password->strvalue,
637 lm_password->strvalue);
638 lm_password->length = 16;
639 pairadd(&request->config_items, lm_password);
644 * We need an NT-Password.
646 nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
648 if ((nt_password->length == 16) ||
649 ((nt_password->length == 32) &&
650 (hex2bin(nt_password->strvalue,
651 nt_password->strvalue, 16) == 16))) {
652 DEBUG2(" rlm_mschap: Found NT-Password");
653 nt_password->length = 16;
656 radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
659 } else if (!password) {
660 DEBUG2(" rlm_mschap: No User-Password configured. Cannot create NT-Password.");
662 } else { /* there is a configured User-Password */
663 nt_password = pairmake("NT-Password", "", T_OP_EQ);
665 radlog(L_ERR, "No memory");
667 ntpwdhash(nt_password->strvalue, password->strvalue);
668 nt_password->length = 16;
669 pairadd(&request->config_items, nt_password);
674 * No NT or LM Passwords, die.
676 if (!lm_password && !nt_password) {
677 DEBUG2(" rlm_mschap: No LM-Password or NT-Password attribute found. Cannot perform MS-CHAP authentication.");
678 return RLM_MODULE_FAIL;
682 * We MAY be asked to take a User-Password attribute from
683 * the packet, and compare it to passwords retrieved from
684 * an SMB Password file.
686 password = pairfind(request->packet->vps, PW_PASSWORD);
689 lrad_lmpwdhash(calculated,
691 if (memcmp(calculated,
692 lm_password->strvalue, 16) == 0) {
693 DEBUG2(" rlm_mschap: User-Password matches LM-Password.");
694 return RLM_MODULE_OK;
696 DEBUG2(" rlm_mschap: FAILED: User-Password does NOT match LM-Password.");
698 } else if (nt_password) {
699 ntpwdhash(calculated, password->strvalue);
700 if (memcmp(calculated,
701 nt_password->strvalue, 16) == 0) {
702 DEBUG2(" rlm_mschap: User-Password matches NT-Password.");
703 return RLM_MODULE_OK;
705 DEBUG2(" rlm_mschap: FAILED: User-Password does NOT match NT-Password.");
708 return RLM_MODULE_REJECT;
709 } /* compare User-Password in packet to configured NT/LM-Password */
711 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
713 DEBUG2(" rlm_mschap: No MS-CHAP-Challenge in the request");
714 return RLM_MODULE_REJECT;
718 * We also require an MS-CHAP-Response.
720 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
722 * MS-CHAP-Response, means MS-CHAPv1
728 * MS-CHAPv1 challenges are 8 octets.
730 if (challenge->length < 8) {
731 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
732 return RLM_MODULE_INVALID;
736 * Responses are 50 octets.
738 if (response->length < 50) {
739 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
740 return RLM_MODULE_INVALID;
744 * We are doing MS-CHAP. Calculate the MS-CHAP
747 if (response->strvalue[1] & 0x01) {
748 DEBUG2(" rlm_mschap: doing MS-CHAPv1 with NT-Password");
749 password = nt_password;
752 DEBUG2(" rlm_mschap: doing MS-CHAPv1 with LM-Password");
753 password = lm_password;
758 * No password configured. Die.
761 DEBUG2(" rlm_mschap: FAILED: No NT/LM-Password");
762 return RLM_MODULE_REJECT;
766 * Calculate the expected response.
768 lrad_mschap(password->strvalue, challenge->strvalue,
770 if (memcmp(response->strvalue + offset,
771 calculated, 24) != 0) {
772 DEBUG(" rlm_mschap: FAILED: MS-CHAP-Response is incorrect");
773 return RLM_MODULE_FAIL;
778 } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
780 * MS-CHAPv2 challenges are 16 octets.
782 if (challenge->length < 16) {
783 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
784 return RLM_MODULE_INVALID;
788 * Responses are 50 octets.
790 if (response->length < 50) {
791 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
792 return RLM_MODULE_INVALID;
796 * We also require a User-Name
798 if (!request->username) {
799 radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
800 return RLM_MODULE_INVALID;
804 * We are doing MS-CHAPv2
805 * We need NT hash for it to calculate response
808 DEBUG2(" rlm_mschap: No NT-Password configured. Cannot perform MS-CHAPv2 authentication.");
809 return RLM_MODULE_INVALID;
812 DEBUG2(" rlm_mschap: doing MS-CHAPv2 with NT-Password");
813 mschap2(response->strvalue + 2, challenge->strvalue,
814 request->username->strvalue, nt_password->strvalue,
816 if (memcmp(response->strvalue + 26, calculated, 24) != 0) {
817 DEBUG2(" rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
818 add_reply(&request->reply->vps, *response->strvalue,
819 "MS-CHAP-Error", "E=691 R=1", 9);
820 return RLM_MODULE_REJECT;
823 auth_response(request->username->strvalue,
824 nt_password->strvalue, calculated,
825 response->strvalue + 2,
828 add_reply( &request->reply->vps, *response->strvalue,
829 "MS-CHAP2-Success", msch2resp, 42);
832 } else { /* Neither CHAPv1 or CHAPv2 response: die */
833 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
834 return RLM_MODULE_INVALID;
838 * We have a CHAP response, but the account may be
839 * disabled. Reject the user with the same error code
840 * we use when their password is invalid.
844 * Account is disabled.
846 * They're found, but they don't exist, so we
847 * return 'not found'.
849 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
850 ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
851 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
852 add_reply( &request->reply->vps, *response->strvalue,
853 "MS-CHAP-Error", "E=691 R=1", 9);
854 return RLM_MODULE_NOTFOUND;
858 * User is locked out.
860 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
861 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
862 add_reply( &request->reply->vps, *response->strvalue,
863 "MS-CHAP-Error", "E=647 R=0", 9);
864 return RLM_MODULE_USERLOCK;
868 /* now create MPPE attributes */
869 if (inst->use_mppe) {
871 DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
872 memset(mppe_sendkey, 0, 32);
874 memcpy(mppe_sendkey, lm_password->strvalue, 8);
879 * According to RFC 2548 we
880 * should send NT hash. But in
881 * practice it doesn't work.
882 * Instead, we should send nthashhash
884 * This is an error on RFC 2548.
886 md4_calc(mppe_sendkey + 8,
887 nt_password->strvalue, 16);
888 mppe_add_reply(&request->reply->vps,
892 } else if (chap == 2) {
893 DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
894 mppe_chap2_gen_keys128(request->secret,
895 request->packet->vector,
896 nt_password->strvalue,
897 response->strvalue + 26,
898 mppe_sendkey, mppe_recvkey);
900 mppe_add_reply(&request->reply->vps,
903 mppe_add_reply(&request->reply->vps,
908 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
909 (inst->require_encryption)? "0x00000002":"0x00000001",
911 rad_assert(reply_attr != NULL);
912 pairadd(&request->reply->vps, reply_attr);
913 reply_attr = pairmake("MS-MPPE-Encryption-Types",
914 (inst->require_strong)? "0x00000004":"0x00000006",
916 rad_assert(reply_attr != NULL);
917 pairadd(&request->reply->vps, reply_attr);
919 } /* else we weren't asked to use MPPE */
921 return RLM_MODULE_OK;
925 module_t rlm_mschap = {
927 RLM_TYPE_THREAD_SAFE, /* type */
928 NULL, /* initialize */
929 mschap_instantiate, /* instantiation */
931 mschap_authenticate, /* authenticate */
932 mschap_authorize, /* authorize */
933 NULL, /* pre-accounting */
934 NULL, /* accounting */
935 NULL, /* checksimul */
936 NULL, /* pre-proxy */
937 NULL, /* post-proxy */
940 mschap_detach, /* detach */