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 static const char rcsid[] = "$Id$";
63 static const char *letters = "0123456789ABCDEF";
66 * hex2bin converts hexadecimal strings into binary
68 static int hex2bin (const char *szHex, unsigned char* szBin, int len)
73 for (i = 0; i < len; i++) {
74 if( !(c1 = memchr(letters, toupper((int) szHex[i << 1]), 16)) ||
75 !(c2 = memchr(letters, toupper((int) szHex[(i << 1) + 1]), 16)))
77 szBin[i] = ((c1-letters)<<4) + (c2-letters);
83 * bin2hex creates hexadecimal presentation
86 static void bin2hex (const unsigned char *szBin, char *szHex, int len)
89 for (i = 0; i < len; i++) {
90 szHex[i<<1] = letters[szBin[i] >> 4];
91 szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F];
96 /* Allowable account control bits */
97 #define ACB_DISABLED 0x0001 /* 1 = User account disabled */
98 #define ACB_HOMDIRREQ 0x0002 /* 1 = Home directory required */
99 #define ACB_PWNOTREQ 0x0004 /* 1 = User password not required */
100 #define ACB_TEMPDUP 0x0008 /* 1 = Temporary duplicate account */
101 #define ACB_NORMAL 0x0010 /* 1 = Normal user account */
102 #define ACB_MNS 0x0020 /* 1 = MNS logon user account */
103 #define ACB_DOMTRUST 0x0040 /* 1 = Interdomain trust account */
104 #define ACB_WSTRUST 0x0080 /* 1 = Workstation trust account */
105 #define ACB_SVRTRUST 0x0100 /* 1 = Server trust account */
106 #define ACB_PWNOEXP 0x0200 /* 1 = User password does not expire */
107 #define ACB_AUTOLOCK 0x0400 /* 1 = Account auto locked */
109 static int pdb_decode_acct_ctrl(const char *p)
115 * Check if the account type bits have been encoded after the
116 * NT password (in the form [NDHTUWSLXI]).
119 if (*p != '[') return 0;
121 for (p++; *p && !finished; p++) {
123 case 'N': /* 'N'o password. */
124 acct_ctrl |= ACB_PWNOTREQ;
127 case 'D': /* 'D'isabled. */
128 acct_ctrl |= ACB_DISABLED ;
131 case 'H': /* 'H'omedir required. */
132 acct_ctrl |= ACB_HOMDIRREQ;
135 case 'T': /* 'T'emp account. */
136 acct_ctrl |= ACB_TEMPDUP;
139 case 'U': /* 'U'ser account (normal). */
140 acct_ctrl |= ACB_NORMAL;
143 case 'M': /* 'M'NS logon user account. What is this? */
144 acct_ctrl |= ACB_MNS;
147 case 'W': /* 'W'orkstation account. */
148 acct_ctrl |= ACB_WSTRUST;
151 case 'S': /* 'S'erver account. */
152 acct_ctrl |= ACB_SVRTRUST;
155 case 'L': /* 'L'ocked account. */
156 acct_ctrl |= ACB_AUTOLOCK;
159 case 'X': /* No 'X'piry on password */
160 acct_ctrl |= ACB_PWNOEXP;
163 case 'I': /* 'I'nterdomain trust account. */
164 acct_ctrl |= ACB_DOMTRUST;
167 case ' ': /* ignore spaces */
185 * ntpwdhash converts Unicode password to 16-byte NT hash
188 static void ntpwdhash (char *szHash, const char *szPassword)
190 char szUnicodePass[513];
195 * NT passwords are unicode. Convert plain text password
196 * to unicode by inserting a zero every other byte
198 nPasswordLen = strlen(szPassword);
199 for (i = 0; i < nPasswordLen; i++) {
200 szUnicodePass[i << 1] = szPassword[i];
201 szUnicodePass[(i << 1) + 1] = 0;
204 /* Encrypt Unicode password to a 16-byte MD4 hash */
205 md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
210 * challenge_hash() is used by mschap2() and auth_response()
211 * implements RFC2759 ChallengeHash()
212 * generates 64 bit challenge
214 static void challenge_hash( const char *peer_challenge,
215 const char *auth_challenge,
216 const char *user_name, char *challenge )
222 SHA1Update(&Context, peer_challenge, 16);
223 SHA1Update(&Context, auth_challenge, 16);
224 SHA1Update(&Context, user_name, strlen(user_name));
225 SHA1Final(hash, &Context);
226 memcpy(challenge, hash, 8);
229 static void mschap2(const char *peer_challenge, const char *auth_challenge,
230 const char *user_name, const char *nt_password,
235 challenge_hash(peer_challenge, auth_challenge, user_name,
238 lrad_mschap(nt_password, challenge, response);
242 * auth_response() generates MS-CHAP v2 SUCCESS response
243 * according to RFC 2759 GenerateAuthenticatorResponse()
244 * returns 42-octet response string
246 static void auth_response(const char *username, const char *nt_password,
248 char *peer_challenge, char *auth_challenge,
253 const char magic1[39] =
254 {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
255 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
256 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
257 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
259 const char magic2[41] =
260 {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
261 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
262 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
263 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
270 * Hash password hash into hashhash
273 md4_calc(hashhash, nt_password, 16);
276 SHA1Update(&Context, hashhash, 16);
277 SHA1Update(&Context, ntresponse, 24);
278 SHA1Update(&Context, magic1, 39);
279 SHA1Final(digest, &Context);
280 challenge_hash(peer_challenge, auth_challenge, username, challenge);
282 SHA1Update(&Context, digest, 20);
283 SHA1Update(&Context, challenge, 8);
284 SHA1Update(&Context, magic2, 41);
285 SHA1Final(digest, &Context);
288 * Encode the value of 'Digest' as "S=" followed by
289 * 40 ASCII hexadecimal digits and return it in
290 * AuthenticatorResponse.
292 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
296 bin2hex(digest, response + 2, 20);
299 struct mschap_instance {
301 int require_encryption;
307 static CONF_PARSER module_config[] = {
309 * Cache the password by default.
311 { "use_mppe", PW_TYPE_BOOLEAN,
312 offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
313 { "require_encryption", PW_TYPE_BOOLEAN,
314 offsetof(struct mschap_instance,require_encryption), NULL, "no" },
315 { "require_strong", PW_TYPE_BOOLEAN,
316 offsetof(struct mschap_instance,require_strong), NULL, "no" },
317 { "passwd", PW_TYPE_STRING_PTR,
318 offsetof(struct mschap_instance, passwd_file), NULL, NULL },
319 { "authtype", PW_TYPE_STRING_PTR,
320 offsetof(struct mschap_instance, auth_type), NULL, NULL },
322 { NULL, -1, 0, NULL, NULL } /* end the list */
326 * deinstantiate module, free all memory allocated during
327 * mschap_instantiate()
329 static int mschap_detach(void *instance){
330 #define inst ((struct mschap_instance *)instance)
331 if (inst->passwd_file) free(inst->passwd_file);
332 if (inst->auth_type) free(inst->auth_type);
339 * Create instance for our module. Allocate space for
340 * instance structure and read configuration parameters
342 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
344 struct mschap_instance *inst;
346 inst = *instance = rad_malloc(sizeof(*inst));
350 memset(inst, 0, sizeof(*inst));
352 if (cf_section_parse(conf, inst, module_config) < 0) {
358 * This module used to support SMB Password files, but it
359 * made it too complicated. If the user tries to
360 * configure an SMB Password file, then die, with an
363 if (inst->passwd_file) {
364 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module. Use rlm_passwd module instead");
373 * add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
374 * attribute to reply packet
376 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
377 const char* name, const char* value, int len)
379 VALUE_PAIR *reply_attr;
380 reply_attr = pairmake(name, "", T_OP_EQ);
382 DEBUG(" rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr);
386 reply_attr->strvalue[0] = ident;
387 memcpy(reply_attr->strvalue + 1, value, len);
388 reply_attr->length = len + 1;
389 pairadd(vp, reply_attr);
393 * Add MPPE attributes to the reply.
395 static void mppe_add_reply(VALUE_PAIR** vp,
396 const char* name, const char* value, int len)
398 VALUE_PAIR *reply_attr;
399 reply_attr = pairmake(name, "", T_OP_EQ);
401 DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
405 memcpy(reply_attr->strvalue, value, len);
406 reply_attr->length = len;
407 pairadd(vp, reply_attr);
410 static const uint8_t SHSpad1[40] =
411 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
416 static const uint8_t SHSpad2[40] =
417 { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
418 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
419 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
420 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
422 static const uint8_t magic1[27] =
423 { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
424 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
425 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
427 static const uint8_t magic2[84] =
428 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
429 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
430 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
431 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
432 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
433 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
434 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
435 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
436 0x6b, 0x65, 0x79, 0x2e };
438 static const uint8_t magic3[84] =
439 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
440 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
441 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
442 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
443 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
444 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
445 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
446 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
447 0x6b, 0x65, 0x79, 0x2e };
450 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
457 SHA1Update(&Context,nt_hashhash,16);
458 SHA1Update(&Context,nt_response,24);
459 SHA1Update(&Context,magic1,27);
460 SHA1Final(digest,&Context);
462 memcpy(masterkey,digest,16);
466 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
467 int keylen,int issend)
482 SHA1Update(&Context,masterkey,16);
483 SHA1Update(&Context,SHSpad1,40);
484 SHA1Update(&Context,s,84);
485 SHA1Update(&Context,SHSpad2,40);
486 SHA1Final(digest,&Context);
488 memcpy(sesskey,digest,keylen);
492 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
493 uint8_t *sendkey,uint8_t *recvkey)
495 uint8_t masterkey[16];
497 mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
499 mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
500 mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
504 * Generate MPPE keys.
506 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
507 uint8_t *nt_hash,uint8_t *response,
508 uint8_t *sendkey,uint8_t *recvkey)
512 /* uint8_t salt[2]; */
513 uint8_t nt_hashhash[16];
515 md4_calc(nt_hashhash,nt_hash,16);
517 mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
520 * dictionary.microsoft defines these attributes as
521 * 'encrypt=2'. The functions in src/lib/radius.c will
522 * take care of encrypting/decrypting them as appropriate,
523 * so that we don't have to.
525 memcpy (sendkey, enckey1, 16);
526 memcpy (recvkey, enckey2, 16);
531 * mschap_authorize() - authorize user if we can authenticate
532 * it later. Add Auth-Type attribute if present in module
533 * configuration (usually Auth-Type must be "MS-CHAP")
535 static int mschap_authorize(void * instance, REQUEST *request)
537 #define inst ((struct mschap_instance *)instance)
538 VALUE_PAIR *challenge = NULL, *response = NULL;
540 const char *authtype_name = "MS-CHAP";
542 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
544 return RLM_MODULE_NOOP;
547 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
549 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
552 * Nothing we recognize. Don't do anything.
555 DEBUG2(" rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
556 return RLM_MODULE_NOOP;
560 * Choose MS-CHAP, or whatever else they told us to use.
562 if (inst->auth_type) {
563 authtype_name = inst->auth_type;
566 DEBUG2(" rlm_mschap: Found MS-CHAP attributes. Setting 'Auth-Type = %s'", authtype_name);
569 * Set Auth-Type to MS-CHAP. The authentication code
570 * will take care of turning clear-text passwords into
573 pairdelete(&request->config_items, PW_AUTHTYPE);
574 vp = pairmake("Auth-Type", authtype_name, T_OP_EQ);
575 rad_assert(vp != NULL);
576 pairadd(&request->config_items, vp);
578 return RLM_MODULE_OK;
583 * mschap_authenticate() - authenticate user based on given
584 * attributes and configuration.
585 * We will try to find out password in configuration
586 * or in configured passwd file.
587 * If one is found we will check paraneters given by NAS.
589 * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
591 * PAP: PW_PASSWORD or
592 * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
593 * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
594 * In case of password mismatch or locked account we MAY return
595 * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
596 * If MS-CHAP2 succeeds we MUST return
599 static int mschap_authenticate(void * instance, REQUEST *request)
601 #define inst ((struct mschap_instance *)instance)
602 VALUE_PAIR *challenge = NULL, *response = NULL;
603 VALUE_PAIR *password = NULL;
604 VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
605 VALUE_PAIR *username;
606 VALUE_PAIR *reply_attr;
607 uint8_t calculated[32];
608 uint8_t msch2resp[42];
609 uint8_t mppe_sendkey[34];
610 uint8_t mppe_recvkey[34];
614 * Find the SMB-Account-Ctrl attribute, or the
615 * SMB-Account-Ctrl-Text attribute.
617 smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
619 password = pairfind(request->config_items,
620 PW_SMB_ACCOUNT_CTRL_TEXT);
622 smb_ctrl = pairmake("SMB-Account-CTRL", "0", T_OP_SET);
623 pairadd(&request->config_items, smb_ctrl);
624 smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
629 * We're configured to do MS-CHAP authentication.
630 * and account control information exists. Enforce it.
634 * Password is not required.
636 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
637 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says no password is required.");
638 return RLM_MODULE_OK;
643 * Decide how to get the passwords.
645 password = pairfind(request->config_items, PW_PASSWORD);
648 * We need an LM-Password.
650 lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
655 if ((lm_password->length == 16) ||
656 ((lm_password->length == 32) &&
657 (hex2bin(lm_password->strvalue,
658 lm_password->strvalue, 16) == 16))) {
659 DEBUG2(" rlm_mschap: Found LM-Password");
660 lm_password->length = 16;
663 radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
667 } else if (!password) {
668 DEBUG2(" rlm_mschap: No User-Password configured. Cannot create LM-Password.");
670 } else { /* there is a configured User-Password */
671 lm_password = pairmake("LM-Password", "", T_OP_EQ);
673 radlog(L_ERR, "No memory");
675 lrad_lmpwdhash(password->strvalue,
676 lm_password->strvalue);
677 lm_password->length = 16;
678 pairadd(&request->config_items, lm_password);
683 * We need an NT-Password.
685 nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
687 if ((nt_password->length == 16) ||
688 ((nt_password->length == 32) &&
689 (hex2bin(nt_password->strvalue,
690 nt_password->strvalue, 16) == 16))) {
691 DEBUG2(" rlm_mschap: Found NT-Password");
692 nt_password->length = 16;
695 radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
698 } else if (!password) {
699 DEBUG2(" rlm_mschap: No User-Password configured. Cannot create NT-Password.");
701 } else { /* there is a configured User-Password */
702 nt_password = pairmake("NT-Password", "", T_OP_EQ);
704 radlog(L_ERR, "No memory");
706 ntpwdhash(nt_password->strvalue, password->strvalue);
707 nt_password->length = 16;
708 pairadd(&request->config_items, nt_password);
713 * No NT or LM Passwords, die.
715 if (!lm_password && !nt_password) {
716 DEBUG2(" rlm_mschap: No LM-Password or NT-Password attribute found. Cannot perform MS-CHAP authentication.");
717 return RLM_MODULE_FAIL;
721 * We MAY be asked to take a User-Password attribute from
722 * the packet, and compare it to passwords retrieved from
723 * an SMB Password file.
725 password = pairfind(request->packet->vps, PW_PASSWORD);
728 lrad_lmpwdhash(calculated,
730 if (memcmp(calculated,
731 lm_password->strvalue, 16) == 0) {
732 DEBUG2(" rlm_mschap: User-Password matches LM-Password.");
733 return RLM_MODULE_OK;
735 DEBUG2(" rlm_mschap: FAILED: User-Password does NOT match LM-Password.");
737 } else if (nt_password) {
738 ntpwdhash(calculated, password->strvalue);
739 if (memcmp(calculated,
740 nt_password->strvalue, 16) == 0) {
741 DEBUG2(" rlm_mschap: User-Password matches NT-Password.");
742 return RLM_MODULE_OK;
744 DEBUG2(" rlm_mschap: FAILED: User-Password does NOT match NT-Password.");
747 return RLM_MODULE_REJECT;
748 } /* compare User-Password in packet to configured NT/LM-Password */
750 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
752 DEBUG2(" rlm_mschap: No MS-CHAP-Challenge in the request");
753 return RLM_MODULE_REJECT;
757 * We also require an MS-CHAP-Response.
759 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
761 * MS-CHAP-Response, means MS-CHAPv1
767 * MS-CHAPv1 challenges are 8 octets.
769 if (challenge->length < 8) {
770 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
771 return RLM_MODULE_INVALID;
775 * Responses are 50 octets.
777 if (response->length < 50) {
778 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
779 return RLM_MODULE_INVALID;
783 * We are doing MS-CHAP. Calculate the MS-CHAP
786 if (response->strvalue[1] & 0x01) {
787 DEBUG2(" rlm_mschap: doing MS-CHAPv1 with NT-Password");
788 password = nt_password;
791 DEBUG2(" rlm_mschap: doing MS-CHAPv1 with LM-Password");
792 password = lm_password;
797 * No password configured. Die.
800 DEBUG2(" rlm_mschap: FAILED: No NT/LM-Password");
801 return RLM_MODULE_REJECT;
805 * Calculate the expected response.
807 lrad_mschap(password->strvalue, challenge->strvalue,
809 if (memcmp(response->strvalue + offset,
810 calculated, 24) != 0) {
811 DEBUG(" rlm_mschap: FAILED: MS-CHAP-Response is incorrect");
812 return RLM_MODULE_FAIL;
817 } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
819 * MS-CHAPv2 challenges are 16 octets.
821 if (challenge->length < 16) {
822 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
823 return RLM_MODULE_INVALID;
827 * Responses are 50 octets.
829 if (response->length < 50) {
830 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
831 return RLM_MODULE_INVALID;
835 * We also require a User-Name
837 username = pairfind(request->packet->vps, PW_USER_NAME);
839 radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
840 return RLM_MODULE_INVALID;
844 * We are doing MS-CHAPv2
845 * We need NT hash for it to calculate response
848 DEBUG2(" rlm_mschap: No NT-Password configured. Cannot perform MS-CHAPv2 authentication.");
849 return RLM_MODULE_INVALID;
852 DEBUG2(" rlm_mschap: doing MS-CHAPv2 with NT-Password");
853 mschap2(response->strvalue + 2, challenge->strvalue,
854 username->strvalue, nt_password->strvalue,
856 if (memcmp(response->strvalue + 26, calculated, 24) != 0) {
857 DEBUG2(" rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
858 add_reply(&request->reply->vps, *response->strvalue,
859 "MS-CHAP-Error", "E=691 R=1", 9);
860 return RLM_MODULE_REJECT;
863 auth_response(username->strvalue,
864 nt_password->strvalue, calculated,
865 response->strvalue + 2,
868 add_reply( &request->reply->vps, *response->strvalue,
869 "MS-CHAP2-Success", msch2resp, 42);
872 } else { /* Neither CHAPv1 or CHAPv2 response: die */
873 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
874 return RLM_MODULE_INVALID;
878 * We have a CHAP response, but the account may be
879 * disabled. Reject the user with the same error code
880 * we use when their password is invalid.
884 * Account is disabled.
886 * They're found, but they don't exist, so we
887 * return 'not found'.
889 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
890 ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
891 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
892 add_reply( &request->reply->vps, *response->strvalue,
893 "MS-CHAP-Error", "E=691 R=1", 9);
894 return RLM_MODULE_NOTFOUND;
898 * User is locked out.
900 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
901 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
902 add_reply( &request->reply->vps, *response->strvalue,
903 "MS-CHAP-Error", "E=647 R=0", 9);
904 return RLM_MODULE_USERLOCK;
908 /* now create MPPE attributes */
909 if (inst->use_mppe) {
911 DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
912 memset(mppe_sendkey, 0, 32);
914 memcpy(mppe_sendkey, lm_password->strvalue, 8);
919 * According to RFC 2548 we
920 * should send NT hash. But in
921 * practice it doesn't work.
922 * Instead, we should send nthashhash
924 * This is an error on RFC 2548.
926 md4_calc(mppe_sendkey + 8,
927 nt_password->strvalue, 16);
928 mppe_add_reply(&request->reply->vps,
932 } else if (chap == 2) {
933 DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
934 mppe_chap2_gen_keys128(request->secret,
935 request->packet->vector,
936 nt_password->strvalue,
937 response->strvalue + 26,
938 mppe_sendkey, mppe_recvkey);
940 mppe_add_reply(&request->reply->vps,
943 mppe_add_reply(&request->reply->vps,
948 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
949 (inst->require_encryption)? "0x00000002":"0x00000001",
951 rad_assert(reply_attr != NULL);
952 pairadd(&request->reply->vps, reply_attr);
953 reply_attr = pairmake("MS-MPPE-Encryption-Types",
954 (inst->require_strong)? "0x00000004":"0x00000006",
956 rad_assert(reply_attr != NULL);
957 pairadd(&request->reply->vps, reply_attr);
959 } /* else we weren't asked to use MPPE */
961 return RLM_MODULE_OK;
965 module_t rlm_mschap = {
967 RLM_TYPE_THREAD_SAFE, /* type */
968 NULL, /* initialize */
969 mschap_instantiate, /* instantiation */
971 mschap_authenticate, /* authenticate */
972 mschap_authorize, /* authorize */
973 NULL, /* pre-accounting */
974 NULL, /* accounting */
975 NULL, /* checksimul */
976 NULL, /* pre-proxy */
977 NULL, /* post-proxy */
980 mschap_detach, /* detach */