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;
303 int with_ntdomain_hack;
308 static CONF_PARSER module_config[] = {
310 * Cache the password by default.
312 { "use_mppe", PW_TYPE_BOOLEAN,
313 offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
314 { "require_encryption", PW_TYPE_BOOLEAN,
315 offsetof(struct mschap_instance,require_encryption), NULL, "no" },
316 { "require_strong", PW_TYPE_BOOLEAN,
317 offsetof(struct mschap_instance,require_strong), NULL, "no" },
318 { "with_ntdomain_hack", PW_TYPE_BOOLEAN,
319 offsetof(struct mschap_instance,with_ntdomain_hack), NULL, "no" },
320 { "passwd", PW_TYPE_STRING_PTR,
321 offsetof(struct mschap_instance, passwd_file), NULL, NULL },
322 { "authtype", PW_TYPE_STRING_PTR,
323 offsetof(struct mschap_instance, auth_type), NULL, NULL },
325 { NULL, -1, 0, NULL, NULL } /* end the list */
329 * deinstantiate module, free all memory allocated during
330 * mschap_instantiate()
332 static int mschap_detach(void *instance){
333 #define inst ((struct mschap_instance *)instance)
334 if (inst->passwd_file) free(inst->passwd_file);
335 if (inst->auth_type) free(inst->auth_type);
342 * Create instance for our module. Allocate space for
343 * instance structure and read configuration parameters
345 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
347 struct mschap_instance *inst;
349 inst = *instance = rad_malloc(sizeof(*inst));
353 memset(inst, 0, sizeof(*inst));
355 if (cf_section_parse(conf, inst, module_config) < 0) {
361 * This module used to support SMB Password files, but it
362 * made it too complicated. If the user tries to
363 * configure an SMB Password file, then die, with an
366 if (inst->passwd_file) {
367 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module. Use rlm_passwd module instead");
376 * add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
377 * attribute to reply packet
379 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
380 const char* name, const char* value, int len)
382 VALUE_PAIR *reply_attr;
383 reply_attr = pairmake(name, "", T_OP_EQ);
385 DEBUG(" rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr);
389 reply_attr->strvalue[0] = ident;
390 memcpy(reply_attr->strvalue + 1, value, len);
391 reply_attr->length = len + 1;
392 pairadd(vp, reply_attr);
396 * Add MPPE attributes to the reply.
398 static void mppe_add_reply(VALUE_PAIR** vp,
399 const char* name, const char* value, int len)
401 VALUE_PAIR *reply_attr;
402 reply_attr = pairmake(name, "", T_OP_EQ);
404 DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
408 memcpy(reply_attr->strvalue, value, len);
409 reply_attr->length = len;
410 pairadd(vp, reply_attr);
413 static const uint8_t SHSpad1[40] =
414 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
416 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
417 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
419 static const uint8_t SHSpad2[40] =
420 { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
421 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
422 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
423 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
425 static const uint8_t magic1[27] =
426 { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
427 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
428 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
430 static const uint8_t magic2[84] =
431 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
432 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
433 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
434 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
435 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
436 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
437 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
438 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
439 0x6b, 0x65, 0x79, 0x2e };
441 static const uint8_t magic3[84] =
442 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
443 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
444 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
445 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
446 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
447 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
448 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
449 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
450 0x6b, 0x65, 0x79, 0x2e };
453 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
460 SHA1Update(&Context,nt_hashhash,16);
461 SHA1Update(&Context,nt_response,24);
462 SHA1Update(&Context,magic1,27);
463 SHA1Final(digest,&Context);
465 memcpy(masterkey,digest,16);
469 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
470 int keylen,int issend)
485 SHA1Update(&Context,masterkey,16);
486 SHA1Update(&Context,SHSpad1,40);
487 SHA1Update(&Context,s,84);
488 SHA1Update(&Context,SHSpad2,40);
489 SHA1Final(digest,&Context);
491 memcpy(sesskey,digest,keylen);
495 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
496 uint8_t *sendkey,uint8_t *recvkey)
498 uint8_t masterkey[16];
500 mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
502 mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
503 mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
507 * Generate MPPE keys.
509 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
510 uint8_t *nt_hash,uint8_t *response,
511 uint8_t *sendkey,uint8_t *recvkey)
515 /* uint8_t salt[2]; */
516 uint8_t nt_hashhash[16];
518 md4_calc(nt_hashhash,nt_hash,16);
520 mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
523 * dictionary.microsoft defines these attributes as
524 * 'encrypt=2'. The functions in src/lib/radius.c will
525 * take care of encrypting/decrypting them as appropriate,
526 * so that we don't have to.
528 memcpy (sendkey, enckey1, 16);
529 memcpy (recvkey, enckey2, 16);
534 * mschap_authorize() - authorize user if we can authenticate
535 * it later. Add Auth-Type attribute if present in module
536 * configuration (usually Auth-Type must be "MS-CHAP")
538 static int mschap_authorize(void * instance, REQUEST *request)
540 #define inst ((struct mschap_instance *)instance)
541 VALUE_PAIR *challenge = NULL, *response = NULL;
543 const char *authtype_name = "MS-CHAP";
545 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
547 return RLM_MODULE_NOOP;
550 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
552 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
555 * Nothing we recognize. Don't do anything.
558 DEBUG2(" rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
559 return RLM_MODULE_NOOP;
563 * Choose MS-CHAP, or whatever else they told us to use.
565 if (inst->auth_type) {
566 authtype_name = inst->auth_type;
569 DEBUG2(" rlm_mschap: Found MS-CHAP attributes. Setting 'Auth-Type = %s'", authtype_name);
572 * Set Auth-Type to MS-CHAP. The authentication code
573 * will take care of turning clear-text passwords into
576 pairdelete(&request->config_items, PW_AUTHTYPE);
577 vp = pairmake("Auth-Type", authtype_name, T_OP_EQ);
578 rad_assert(vp != NULL);
579 pairadd(&request->config_items, vp);
581 return RLM_MODULE_OK;
586 * mschap_authenticate() - authenticate user based on given
587 * attributes and configuration.
588 * We will try to find out password in configuration
589 * or in configured passwd file.
590 * If one is found we will check paraneters given by NAS.
592 * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
594 * PAP: PW_PASSWORD or
595 * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
596 * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
597 * In case of password mismatch or locked account we MAY return
598 * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
599 * If MS-CHAP2 succeeds we MUST return
602 static int mschap_authenticate(void * instance, REQUEST *request)
604 #define inst ((struct mschap_instance *)instance)
605 VALUE_PAIR *challenge = NULL, *response = NULL;
606 VALUE_PAIR *password = NULL;
607 VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
608 VALUE_PAIR *username;
609 VALUE_PAIR *reply_attr;
610 uint8_t calculated[32];
611 uint8_t msch2resp[42];
612 uint8_t mppe_sendkey[34];
613 uint8_t mppe_recvkey[34];
614 char *username_string;
618 * Find the SMB-Account-Ctrl attribute, or the
619 * SMB-Account-Ctrl-Text attribute.
621 smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
623 password = pairfind(request->config_items,
624 PW_SMB_ACCOUNT_CTRL_TEXT);
626 smb_ctrl = pairmake("SMB-Account-CTRL", "0", T_OP_SET);
627 pairadd(&request->config_items, smb_ctrl);
628 smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
633 * We're configured to do MS-CHAP authentication.
634 * and account control information exists. Enforce it.
638 * Password is not required.
640 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
641 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says no password is required.");
642 return RLM_MODULE_OK;
647 * Decide how to get the passwords.
649 password = pairfind(request->config_items, PW_PASSWORD);
652 * We need an LM-Password.
654 lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
659 if ((lm_password->length == 16) ||
660 ((lm_password->length == 32) &&
661 (hex2bin(lm_password->strvalue,
662 lm_password->strvalue, 16) == 16))) {
663 DEBUG2(" rlm_mschap: Found LM-Password");
664 lm_password->length = 16;
667 radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
671 } else if (!password) {
672 DEBUG2(" rlm_mschap: No User-Password configured. Cannot create LM-Password.");
674 } else { /* there is a configured User-Password */
675 lm_password = pairmake("LM-Password", "", T_OP_EQ);
677 radlog(L_ERR, "No memory");
679 lrad_lmpwdhash(password->strvalue,
680 lm_password->strvalue);
681 lm_password->length = 16;
682 pairadd(&request->config_items, lm_password);
687 * We need an NT-Password.
689 nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
691 if ((nt_password->length == 16) ||
692 ((nt_password->length == 32) &&
693 (hex2bin(nt_password->strvalue,
694 nt_password->strvalue, 16) == 16))) {
695 DEBUG2(" rlm_mschap: Found NT-Password");
696 nt_password->length = 16;
699 radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
702 } else if (!password) {
703 DEBUG2(" rlm_mschap: No User-Password configured. Cannot create NT-Password.");
705 } else { /* there is a configured User-Password */
706 nt_password = pairmake("NT-Password", "", T_OP_EQ);
708 radlog(L_ERR, "No memory");
710 ntpwdhash(nt_password->strvalue, password->strvalue);
711 nt_password->length = 16;
712 pairadd(&request->config_items, nt_password);
717 * No NT or LM Passwords, die.
719 if (!lm_password && !nt_password) {
720 DEBUG2(" rlm_mschap: No LM-Password or NT-Password attribute found. Cannot perform MS-CHAP authentication.");
721 return RLM_MODULE_FAIL;
725 * We MAY be asked to take a User-Password attribute from
726 * the packet, and compare it to passwords retrieved from
727 * an SMB Password file.
729 password = pairfind(request->packet->vps, PW_PASSWORD);
732 lrad_lmpwdhash(calculated,
734 if (memcmp(calculated,
735 lm_password->strvalue, 16) == 0) {
736 DEBUG2(" rlm_mschap: User-Password matches LM-Password.");
737 return RLM_MODULE_OK;
739 DEBUG2(" rlm_mschap: FAILED: User-Password does NOT match LM-Password.");
741 } else if (nt_password) {
742 ntpwdhash(calculated, password->strvalue);
743 if (memcmp(calculated,
744 nt_password->strvalue, 16) == 0) {
745 DEBUG2(" rlm_mschap: User-Password matches NT-Password.");
746 return RLM_MODULE_OK;
748 DEBUG2(" rlm_mschap: FAILED: User-Password does NOT match NT-Password.");
751 return RLM_MODULE_REJECT;
752 } /* compare User-Password in packet to configured NT/LM-Password */
754 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
756 DEBUG2(" rlm_mschap: No MS-CHAP-Challenge in the request");
757 return RLM_MODULE_REJECT;
761 * We also require an MS-CHAP-Response.
763 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
765 * MS-CHAP-Response, means MS-CHAPv1
771 * MS-CHAPv1 challenges are 8 octets.
773 if (challenge->length < 8) {
774 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
775 return RLM_MODULE_INVALID;
779 * Responses are 50 octets.
781 if (response->length < 50) {
782 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
783 return RLM_MODULE_INVALID;
787 * We are doing MS-CHAP. Calculate the MS-CHAP
790 if (response->strvalue[1] & 0x01) {
791 DEBUG2(" rlm_mschap: doing MS-CHAPv1 with NT-Password");
792 password = nt_password;
795 DEBUG2(" rlm_mschap: doing MS-CHAPv1 with LM-Password");
796 password = lm_password;
801 * No password configured. Die.
804 DEBUG2(" rlm_mschap: FAILED: No NT/LM-Password");
805 return RLM_MODULE_REJECT;
809 * Calculate the expected response.
811 lrad_mschap(password->strvalue, challenge->strvalue,
813 if (memcmp(response->strvalue + offset,
814 calculated, 24) != 0) {
815 DEBUG(" rlm_mschap: FAILED: MS-CHAP-Response is incorrect");
816 return RLM_MODULE_FAIL;
821 } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
823 * MS-CHAPv2 challenges are 16 octets.
825 if (challenge->length < 16) {
826 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
827 return RLM_MODULE_INVALID;
831 * Responses are 50 octets.
833 if (response->length < 50) {
834 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
835 return RLM_MODULE_INVALID;
839 * We also require a User-Name
841 username = pairfind(request->packet->vps, PW_USER_NAME);
843 radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
844 return RLM_MODULE_INVALID;
849 *with_ntdomain_hack moved here
851 if((username_string = strchr(username->strvalue, '\\')) != NULL) {
852 if(inst->with_ntdomain_hack) {
855 DEBUG2(" rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
856 username_string = username->strvalue;
859 username_string = username->strvalue;
863 * We are doing MS-CHAPv2
864 * We need NT hash for it to calculate response
867 DEBUG2(" rlm_mschap: No NT-Password configured. Cannot perform MS-CHAPv2 authentication.");
868 return RLM_MODULE_INVALID;
871 DEBUG2(" rlm_mschap: doing MS-CHAPv2 with NT-Password");
872 mschap2(response->strvalue + 2, challenge->strvalue,
873 username_string, nt_password->strvalue,
875 if (memcmp(response->strvalue + 26, calculated, 24) != 0) {
876 DEBUG2(" rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
877 add_reply(&request->reply->vps, *response->strvalue,
878 "MS-CHAP-Error", "E=691 R=1", 9);
879 return RLM_MODULE_REJECT;
882 auth_response(username_string,
883 nt_password->strvalue, calculated,
884 response->strvalue + 2,
887 add_reply( &request->reply->vps, *response->strvalue,
888 "MS-CHAP2-Success", msch2resp, 42);
891 } else { /* Neither CHAPv1 or CHAPv2 response: die */
892 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
893 return RLM_MODULE_INVALID;
897 * We have a CHAP response, but the account may be
898 * disabled. Reject the user with the same error code
899 * we use when their password is invalid.
903 * Account is disabled.
905 * They're found, but they don't exist, so we
906 * return 'not found'.
908 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
909 ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
910 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
911 add_reply( &request->reply->vps, *response->strvalue,
912 "MS-CHAP-Error", "E=691 R=1", 9);
913 return RLM_MODULE_NOTFOUND;
917 * User is locked out.
919 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
920 DEBUG2(" rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
921 add_reply( &request->reply->vps, *response->strvalue,
922 "MS-CHAP-Error", "E=647 R=0", 9);
923 return RLM_MODULE_USERLOCK;
927 /* now create MPPE attributes */
928 if (inst->use_mppe) {
930 DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
931 memset(mppe_sendkey, 0, 32);
933 memcpy(mppe_sendkey, lm_password->strvalue, 8);
938 * According to RFC 2548 we
939 * should send NT hash. But in
940 * practice it doesn't work.
941 * Instead, we should send nthashhash
943 * This is an error on RFC 2548.
945 md4_calc(mppe_sendkey + 8,
946 nt_password->strvalue, 16);
947 mppe_add_reply(&request->reply->vps,
951 } else if (chap == 2) {
952 DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
953 mppe_chap2_gen_keys128(request->secret,
954 request->packet->vector,
955 nt_password->strvalue,
956 response->strvalue + 26,
957 mppe_sendkey, mppe_recvkey);
959 mppe_add_reply(&request->reply->vps,
962 mppe_add_reply(&request->reply->vps,
967 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
968 (inst->require_encryption)? "0x00000002":"0x00000001",
970 rad_assert(reply_attr != NULL);
971 pairadd(&request->reply->vps, reply_attr);
972 reply_attr = pairmake("MS-MPPE-Encryption-Types",
973 (inst->require_strong)? "0x00000004":"0x00000006",
975 rad_assert(reply_attr != NULL);
976 pairadd(&request->reply->vps, reply_attr);
978 } /* else we weren't asked to use MPPE */
980 return RLM_MODULE_OK;
984 module_t rlm_mschap = {
986 RLM_TYPE_THREAD_SAFE, /* type */
987 NULL, /* initialize */
988 mschap_instantiate, /* instantiation */
990 mschap_authenticate, /* authenticate */
991 mschap_authorize, /* authorize */
992 NULL, /* pre-accounting */
993 NULL, /* accounting */
994 NULL, /* checksimul */
995 NULL, /* pre-proxy */
996 NULL, /* post-proxy */
999 mschap_detach, /* detach */