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 * Jay Miller jaymiller@socket.net
29 * This implements MS-CHAP, as described in RFC 2548
31 * http://www.freeradius.org/rfc/rfc2548.txt
36 * If you have any questions on NTLM (Samba) passwords
37 * support, LM authentication and MS-CHAP v2 support
40 * Vladimir Dubrovin vlad@sandy.ru
42 * ZARAZA 3APA3A@security.nnov.ru
45 /* MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
48 #include "libradius.h"
63 #include "rad_assert.h"
65 #define PW_MSCHAP_RESPONSE ((311 << 16) | 1)
66 #define PW_MSCHAP_CHALLENGE ((311 << 16) | 11)
67 #define PW_MSCHAP2_RESPONSE ((311 << 16) | 25)
76 static void parity_key(char * szOut, const char * szIn);
77 static void des_encrypt(const char *szClear, const char *szKey, char *szOut);
78 static void mschap(const char *szChallenge, struct smb_passwd * smbPasswd, char *szResponse, int bUseNT);
79 static void ntpwdhash (char *szHash, const char *szPassword);
80 static void lmpwdhash (char *szHash, const char *szPassword);
81 static struct smb_passwd *createsmbpw(struct smb_passwd *pw_buf, const char* username, const char *password);
82 static void auth_response(struct smb_passwd * smbPasswd, char *ntresponse,
83 char *peer_challenge, char *auth_challenge,
85 static void challenge_hash( const char* peer_challenge, const char* auth_challenge,
86 const char* user_name, char * challenge );
87 static void mschap2( const char *peer_challenge, const char *auth_challenge,
88 struct smb_passwd * smbPasswd, char *response);
89 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
90 const char* name, const char* value, int len);
92 static void mppe_add_reply(VALUE_PAIR** vp,
93 const char* name, const char* value, int len);
95 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
96 uint8_t *nt_hash,uint8_t *response,
97 uint8_t *sendkey,uint8_t *recvkey);
99 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
100 uint8_t *sendkey,uint8_t *recvkey);
102 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
105 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
106 int keylen,int issend);
109 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
110 uint8_t* salt,uint8_t* enckey,uint8_t* key);
116 * parity_key takes a 7-byte string in szIn and returns an
117 * 8-byte string in szOut. It inserts a 1 into every 8th bit.
118 * DES just strips these back out.
120 static void parity_key(char * szOut, const char * szIn)
123 unsigned char cNext = 0;
124 unsigned char cWorking = 0;
126 for (i = 0; i < 7; i++) {
127 /* Shift operator works in place. Copy the char out */
129 szOut[i] = (cWorking >> i) | cNext | 1;
131 cNext = (cWorking << (7 - i));
133 szOut[i] = cNext | 1;
137 * des_encrypt takes an 8-byte string and a 7-byte key and
138 * returns an 8-byte DES encrypted string in szOut
140 static void des_encrypt(const char *szClear, const char *szKey, char *szOut)
143 unsigned long ulK[16][2];
145 parity_key(szParityKey, szKey); /* Insert parity bits */
146 strncpy(szOut, szClear, 8); /* des encrypts in place */
147 deskey(ulK, (unsigned char *) szParityKey, 0); /* generate keypair */
148 des(ulK, szOut); /* encrypt */
154 * ntpwdhash converts Unicode password to 16-byte NT hash
157 static void ntpwdhash (char *szHash, const char *szPassword)
159 char szUnicodePass[513];
164 * NT passwords are unicode. Convert plain text password
165 * to unicode by inserting a zero every other byte
167 nPasswordLen = strlen(szPassword);
168 for (i = 0; i < nPasswordLen; i++) {
169 szUnicodePass[i << 1] = szPassword[i];
170 szUnicodePass[(i << 1) + 1] = 0;
173 /* Encrypt Unicode password to a 16-byte MD4 hash */
174 md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
180 * lmpwdhash converts 14-byte null-padded uppercase OEM
181 * password to 16-byte DES hash with predefined salt string
183 static void lmpwdhash (char *szHash, const char *szPassword)
186 char stdText[] = "KGS!@#$%";
189 memset(szOEMPass, 0, 14);
190 for (i = 0; i < 14 && szPassword[i]; i++)
191 szOEMPass[i] = toupper((int) szPassword[i]);
193 /* Obtain DES hash of OEM password */
194 des_encrypt(stdText, szOEMPass, szHash);
195 des_encrypt(stdText, szOEMPass+7, szHash+8);
199 * createsmbpw() creates smb_passwd structure from given
200 * user name and cleartext or ntlm-encrypter password
201 * if encrypted flag is not set only cleartext password
204 static struct smb_passwd *createsmbpw(struct smb_passwd *pw_buf, const char * username, const char *password)
209 pdb_init_smb(pw_buf);
210 pw_buf->acct_ctrl = ACB_NORMAL;
211 pw_buf->smb_userid = 0;
212 setsmbname(pw_buf,username);
214 if (pw_buf->smb_passwd==NULL && pw_buf->smb_nt_passwd==NULL) {
215 ntpwdhash(pw_buf->smb_nt_passwd_value, password);
216 lmpwdhash(pw_buf->smb_passwd_value, password);
217 pw_buf->smb_passwd = pw_buf->smb_passwd_value;
218 pw_buf->smb_nt_passwd = pw_buf->smb_nt_passwd_value;
226 * mschap takes an 8-byte challenge string and SMB password
227 * and returns a 24-byte response string in szResponse
229 static void mschap(const char *szChallenge, struct smb_passwd * smbPasswd,
230 char *szResponse, int bUseNT) {
234 /* initialize hash string */
235 memset(szMD4, 0, 21);
237 memcpy(szMD4, (bUseNT)?
238 smbPasswd->smb_nt_passwd : smbPasswd->smb_passwd, 16);
242 * challenge_response takes an 8-byte challenge string and a
243 * 21-byte hash (16-byte hash padded to 21 bytes with zeros) and
244 * returns a 24-byte response in szResponse
246 des_encrypt(szChallenge, szMD4, szResponse);
247 des_encrypt(szChallenge, szMD4 + 7, szResponse + 8);
248 des_encrypt(szChallenge, szMD4 + 14, szResponse + 16);
253 * challenge_hash() is used by mschap2() and auth_response()
254 * implements RFC2759 ChallengeHash()
255 * generates 64 bit challenge
257 static void challenge_hash( const char* peer_challenge, const char* auth_challenge,
258 const char* user_name, char * challenge )
264 SHA1Update(&Context, peer_challenge, 16);
265 SHA1Update(&Context, auth_challenge, 16);
266 SHA1Update(&Context, user_name, strlen(user_name));
267 SHA1Final(hash, &Context);
268 memcpy(challenge, hash, 8);
271 static void mschap2( const char *peer_challenge, const char *auth_challenge,
272 struct smb_passwd * smbPasswd, char *response)
276 challenge_hash(peer_challenge, auth_challenge, smbPasswd->smb_name,
278 mschap(challenge, smbPasswd, response, 1);
282 * auth_response() generates MS-CHAP v2 SUCCESS response
283 * according to RFC 2759 GenerateAuthenticatorResponse()
284 * returns 42-octet response string
286 static void auth_response(struct smb_passwd * smbPasswd, char *ntresponse,
287 char *peer_challenge, char *auth_challenge,
293 {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
294 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
295 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
296 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
299 {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
300 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
301 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
302 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
308 * Hash password hash into hashhash
311 md4_calc(hashhash, smbPasswd->smb_nt_passwd, 16);
314 SHA1Update(&Context, hashhash, 16);
315 SHA1Update(&Context, ntresponse, 24);
316 SHA1Update(&Context, magic1, 39);
317 SHA1Final(digest, &Context);
318 challenge_hash(peer_challenge, auth_challenge, smbPasswd->smb_name,
321 SHA1Update(&Context, digest, 20);
322 SHA1Update(&Context, challenge, 8);
323 SHA1Update(&Context, magic2, 41);
324 SHA1Final(digest, &Context);
327 * Encode the value of 'Digest' as "S=" followed by
328 * 40 ASCII hexadecimal digits and return it in
329 * AuthenticatorResponse.
331 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
336 bin2hex(digest, response + 2, 20);
339 struct mschap_instance {
342 int require_encryption;
348 static CONF_PARSER module_config[] = {
350 * Cache the password by default.
352 { "ignore_password", PW_TYPE_BOOLEAN,
353 offsetof(struct mschap_instance,ignore_password), NULL, "no" },
354 { "use_mppe", PW_TYPE_BOOLEAN,
355 offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
356 { "require_encryption", PW_TYPE_BOOLEAN,
357 offsetof(struct mschap_instance,require_encryption), NULL, "no" },
358 { "require_strong", PW_TYPE_BOOLEAN,
359 offsetof(struct mschap_instance,require_strong), NULL, "no" },
360 { "passwd", PW_TYPE_STRING_PTR,
361 offsetof(struct mschap_instance, passwd_file), NULL, NULL },
362 { "authtype", PW_TYPE_STRING_PTR,
363 offsetof(struct mschap_instance, auth_type), NULL, NULL },
365 { NULL, -1, 0, NULL, NULL } /* end the list */
369 * Create instance for our module. Allocate space for
370 * instance structure and read configuration parameters
372 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
374 struct mschap_instance *inst;
376 inst = *instance = rad_malloc(sizeof(struct mschap_instance));
377 if (cf_section_parse(conf, inst, module_config) < 0) {
385 * deinstantiate module, free all memory allocated during
386 * mschap_instantiate()
388 static int mschap_detach(void *instance){
389 #define inst ((struct mschap_instance *)instance)
390 if (inst->passwd_file) free(inst->passwd_file);
391 if (inst->auth_type) free(inst->auth_type);
398 * add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
399 * attribute to reply packet
401 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
402 const char* name, const char* value, int len)
404 VALUE_PAIR *reply_attr;
405 reply_attr = pairmake(name, "", T_OP_EQ);
407 DEBUG("rlm_mschap: add_reply failed to create attribute %s: %s\n", name, librad_errstr);
411 reply_attr->strvalue[0] = ident;
412 memcpy(reply_attr->strvalue + 1, value, len);
413 reply_attr->length = len + 1;
414 pairadd(vp, reply_attr);
417 static void mppe_add_reply(VALUE_PAIR** vp,
418 const char* name, const char* value, int len)
420 VALUE_PAIR *reply_attr;
421 reply_attr = pairmake(name, "", T_OP_EQ);
423 DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
427 memcpy(reply_attr->strvalue, value, len);
428 reply_attr->length = len;
429 pairadd(vp, reply_attr);
432 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
433 uint8_t *nt_hash,uint8_t *response,
434 uint8_t *sendkey,uint8_t *recvkey)
438 /* uint8_t salt[2]; */
439 uint8_t nt_hashhash[16];
441 md4_calc(nt_hashhash,nt_hash,16);
443 mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
445 salt[0] = (vector[0] ^ vector[1] ^ 0x3A) | 0x80;
446 salt[1] = (vector[2] ^ vector[3] ^ vector[4]);
448 mppe_gen_respkey(secret,vector,salt,enckey1,sendkey);
450 salt[0] = (vector[0] ^ vector[1] ^ 0x4e) | 0x80;
451 salt[1] = (vector[5] ^ vector[6] ^ vector[7]);
453 mppe_gen_respkey(secret,vector,salt,enckey2,recvkey);
455 memcpy (sendkey, enckey1, 16);
456 memcpy (recvkey, enckey2, 16);
459 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
460 uint8_t *sendkey,uint8_t *recvkey)
462 uint8_t masterkey[16];
464 mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
466 mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
467 mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
470 static uint8_t SHSpad1[40] =
471 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
476 static uint8_t SHSpad2[40] =
477 { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
478 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
479 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
480 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
482 static uint8_t magic1[27] =
483 { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
484 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
485 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
487 static uint8_t magic2[84] =
488 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
489 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
490 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
491 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
492 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
493 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
494 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
495 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
496 0x6b, 0x65, 0x79, 0x2e };
498 static uint8_t magic3[84] =
499 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
500 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
501 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
502 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
503 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
504 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
505 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
506 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
507 0x6b, 0x65, 0x79, 0x2e };
510 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
517 SHA1Update(&Context,nt_hashhash,16);
518 SHA1Update(&Context,nt_response,24);
519 SHA1Update(&Context,magic1,27);
520 SHA1Final(digest,&Context);
522 memcpy(masterkey,digest,16);
526 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
527 int keylen,int issend)
542 SHA1Update(&Context,masterkey,16);
543 SHA1Update(&Context,SHSpad1,40);
544 SHA1Update(&Context,s,84);
545 SHA1Update(&Context,SHSpad2,40);
546 SHA1Final(digest,&Context);
548 memcpy(sesskey,digest,keylen);
553 /* Not requiered, because encoding will be performed by
555 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
556 uint8_t* salt,uint8_t* enckey,uint8_t* key)
562 int slen = strlen(secret);
568 memcpy(plain + 1,enckey,16);
571 MD5Update(&Context,secret,slen);
572 MD5Update(&Context,vector,AUTH_VECTOR_LEN);
573 MD5Update(&Context,salt,2);
574 MD5Final(buf,&Context);
576 for(i=0;i < 16;i++) {
581 MD5Update(&Context,secret,slen);
582 MD5Update(&Context,plain,16);
583 MD5Final(buf,&Context);
585 for(i=0;i < 16;i++) {
586 plain[i + 16] ^= buf[i];
590 memcpy(key + 2,plain,32);
596 * mschap_authorize() - authorize user if we can authenticate
597 * it later. Add Auth-Type attribute if present in module
598 * configuration (usually Auth-Type must be "MS-CHAP")
600 static int mschap_authorize(void * instance, REQUEST *request)
602 #define inst ((struct mschap_instance *)instance)
603 VALUE_PAIR *challenge = NULL, *response = NULL;
604 VALUE_PAIR *reply_attr;
605 VALUE_PAIR *password = NULL;
606 struct smb_passwd smbPasswdValue, *smbPasswd = NULL;
609 password = pairfind(request->config_items, PW_PASSWORD);
610 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
612 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
614 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
616 if (password && (!challenge || !response)) {
617 /* We have nothing related to MS-CHAP or NTLM */
618 return RLM_MODULE_NOOP;
620 if (!request->username || *request->username->strvalue == 0) {
621 /* Usernam must present */
622 return RLM_MODULE_NOOP;
624 if (password && !inst->ignore_password)
625 smbPasswd = createsmbpw(&smbPasswdValue, request->username->strvalue, password->strvalue);
626 else if (inst->passwd_file) {
627 smbPasswd = getsmbfilepwname (&smbPasswdValue, inst->passwd_file, request->username->strvalue);
629 if (!smbPasswd || !smbPasswd->acct_ctrl&ACB_NORMAL ||smbPasswd->acct_ctrl&ACB_DISABLED) {
630 if(challenge && response){
631 add_reply( &request->reply->vps, *response->strvalue,
632 "MS-CHAP-Error", "E=691 R=1", 9);
634 return RLM_MODULE_NOTFOUND;
636 if (inst->auth_type){
637 pairdelete(&request->config_items, PW_AUTHTYPE);
638 reply_attr = pairmake("Auth-Type", inst->auth_type, T_OP_EQ);
639 rad_assert(reply_attr != NULL);
640 pairadd(&request->config_items, reply_attr);
642 if (smbPasswd->smb_passwd){
643 reply_attr = pairmake("LM-Password", "", T_OP_EQ);
644 rad_assert(reply_attr != NULL);
645 reply_attr->length = 16;
646 memcpy(reply_attr->strvalue, smbPasswd->smb_passwd, 16);
647 pairadd(&request->config_items, reply_attr);
649 if (smbPasswd->smb_nt_passwd){
650 reply_attr = pairmake("NT-Password", "", T_OP_EQ);
651 rad_assert(reply_attr != NULL);
652 reply_attr->length = 16;
653 memcpy(reply_attr->strvalue, smbPasswd->smb_nt_passwd, 16);
654 pairadd(&request->config_items, reply_attr);
657 reply_attr = pairmake("SMB-Account-CTRL", "0", T_OP_EQ);
658 rad_assert(reply_attr != NULL);
659 reply_attr->lvalue = smbPasswd->acct_ctrl;
660 pairadd(&request->config_items, reply_attr);
661 return RLM_MODULE_OK;
667 * mschap_authenticate() - authenticate user based on given
668 * attributes and configuration.
669 * We will try to find out password in configuration
670 * or in configured passwd file.
671 * If one is found we will check paraneters given by NAS.
673 * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
675 * PAP: PW_PASSWORD or
676 * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
677 * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
678 * In case of password mismatch or locked account we MAY return
679 * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
680 * If MS-CHAP2 succeeds we MUST return
683 static int mschap_authenticate(void * instance, REQUEST *request)
685 #define inst ((struct mschap_instance *)instance)
686 VALUE_PAIR *challenge = NULL, *response = NULL;
687 VALUE_PAIR *password = NULL;
688 VALUE_PAIR *reply_attr;
689 uint8_t calculated[32];
690 uint8_t msch2resp[42];
691 uint8_t mppe_sendkey[34];
692 uint8_t mppe_recvkey[34];
693 struct smb_passwd smbPasswd, smbPasswd1Value, *smbPasswd1 = NULL;
701 pdb_init_smb(&smbPasswd);
702 setsmbname(&smbPasswd,request->username->strvalue);
703 password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
705 smbPasswd.acct_ctrl = password->lvalue;
706 if (smbPasswd.acct_ctrl & ACB_PWNOTREQ) return RLM_MODULE_OK;
709 password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT);
711 smbPasswd.acct_ctrl = pdb_decode_acct_ctrl(password->strvalue);
712 if (smbPasswd.acct_ctrl & ACB_PWNOTREQ) return RLM_MODULE_OK;
715 password = pairfind(request->config_items, PW_LM_PASSWORD);
717 if(password->length == 16) {
718 smbPasswd.smb_passwd = password->strvalue;
721 else if(hex2bin(password->strvalue,smbPasswd.smb_passwd_value,16) != 16) {
722 radlog(L_ERR, "rlm_mschap: Invalid LM Password text");
726 smbPasswd.smb_passwd = smbPasswd.smb_passwd_value;
731 password = pairfind(request->config_items, PW_NT_PASSWORD);
733 if (password->length == 16) {
734 smbPasswd.smb_nt_passwd = password->strvalue;
737 else if(hex2bin(password->strvalue,smbPasswd.smb_nt_passwd_value,16) != 16) {
738 radlog(L_ERR, "rlm_mschap: Invalid NT Password text");
741 smbPasswd.smb_nt_passwd = smbPasswd.smb_nt_passwd_value;
747 * We have neither NT nor LM passwords configured
749 radlog(L_ERR, "rlm_mschap: No LM/NT password configured. Check authorization.");
750 return RLM_MODULE_INVALID;
754 * If NAS sent cleartext password - encode it and check
755 * only against passwd file. If either NT or LM hash match
759 password = pairfind(request->packet->vps, PW_PASSWORD);
760 if (password && request->username &&
761 (request->username->strvalue[0] != '\0')) {
763 smbPasswd1 = createsmbpw(&smbPasswd1Value,request->username->strvalue, password->strvalue);
764 if (smbPasswd.smb_passwd) {
765 if (memcmp(smbPasswd1->smb_passwd, smbPasswd.smb_passwd, 16) == 0) {
766 return RLM_MODULE_OK;
768 DEBUG2("rlm_mschap: Found SMB password, but user supplied password is incorrect");
770 } else if (smbPasswd.smb_nt_passwd) {
771 if (memcmp(smbPasswd1->smb_nt_passwd, smbPasswd.smb_nt_passwd, 16)) {
772 return RLM_MODULE_OK;
774 DEBUG2("rlm_mschap: Found NT Password, but user supplied password is incorrect");
776 DEBUG2("rlm_mschap: No SMB or NT Password was configured for the user: Rejecting them.");
779 return RLM_MODULE_REJECT;
782 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
785 * We need an MS-CHAP-Challenge attribute to calculate
788 res = RLM_MODULE_REJECT;
789 if ( (response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE)) ){
790 if (response->length < 50 || challenge->length < 8) {
791 radlog(L_AUTH, "rlm_mschap: Attribute \"MS-CHAP-Response\" has wrong format.");
792 return RLM_MODULE_INVALID;
795 * We are doing MS-CHAP
796 * Calculate the MS-CHAP response
798 if (smbPasswd.smb_nt_passwd && (response->strvalue[1] & 0x01)) {
800 * Try NT response first if UseNT flag is set
802 DEBUG2("rlm_mschap: doing MS-CHAPv1 with NT-Password");
803 mschap(challenge->strvalue, &smbPasswd, calculated, 1);
804 if (memcmp(response->strvalue + 26, calculated, 24) == 0) {
809 if (res != RLM_MODULE_OK && smbPasswd.smb_passwd) {
813 DEBUG2("rlm_mschap: doing MS-CHAPv1 with LM-Password");
814 mschap(challenge->strvalue, &smbPasswd,
816 if (memcmp(response->strvalue + 2, calculated, 24) == 0) {
822 else if ( (response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) ){
823 if (response->length < 50 || challenge->length < 16) {
824 radlog(L_AUTH, "rlm_mschap: Attribute \"MS-CHAP2-Response\" has wrong format.");
825 return RLM_MODULE_INVALID;
828 * We are doing MS-CHAPv2
829 * We need NT hash for it to calculate response
831 if (smbPasswd.smb_nt_passwd) {
832 DEBUG2("rlm_mschap: doing MS-CHAPv2 with NT-Password");
833 mschap2(response->strvalue + 2, challenge->strvalue,
834 &smbPasswd, calculated);
835 if (memcmp(response->strvalue + 26, calculated, 24) == 0) {
836 auth_response(&smbPasswd, calculated,
837 response->strvalue + 2,
840 add_reply( &request->reply->vps, *response->strvalue,
841 "MS-CHAP2-Success", msch2resp, 42);
848 radlog(L_AUTH, "rlm_mschap: Response attribute is not found");
849 return RLM_MODULE_INVALID;
852 if (res == RLM_MODULE_OK) {
853 if (smbPasswd.acct_ctrl & ACB_AUTOLOCK) {
854 radlog(L_AUTH, "rlm_mschap: Account locked out");
855 add_reply( &request->reply->vps, *response->strvalue,
856 "MS-CHAP-Error", "E=647 R=0", 9);
857 return RLM_MODULE_USERLOCK;
860 /* now create MPPE attributes */
861 if (inst->use_mppe) {
863 DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
864 memset (mppe_sendkey, 0, 32);
865 if (smbPasswd.smb_passwd)
866 memcpy(mppe_sendkey, smbPasswd.smb_passwd, 8);
867 if (smbPasswd.smb_nt_passwd)
869 According to RFC 2548 we should send NT hash.
870 But in practice it doesn't work and we should
871 send nthashhash instead
872 If someone have different information please
873 feel free to feedback.
875 memcpy (mppe_sendkey+8,smbPasswd.smb_nt_passwd,16);
877 md4_calc(mppe_sendkey+8, smbPasswd.smb_nt_passwd,16);
879 rad_pwencode(mppe_sendkey, &len,
880 request->secret, request->packet->vector);
882 mppe_add_reply( &request->reply->vps,
883 "MS-CHAP-MPPE-Keys",mppe_sendkey,32);
886 DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
887 mppe_chap2_gen_keys128(request->secret,request->packet->vector,
888 smbPasswd.smb_nt_passwd,
889 response->strvalue + 26,
890 mppe_sendkey,mppe_recvkey);
892 mppe_add_reply( &request->reply->vps,
893 "MS-MPPE-Recv-Key",mppe_recvkey,16);
894 mppe_add_reply( &request->reply->vps,
895 "MS-MPPE-Send-Key",mppe_sendkey,16);
898 mppe_add_reply( &request->reply->vps,
899 "MS-MPPE-Recv-Key",mppe_recvkey,34);
900 mppe_add_reply( &request->reply->vps,
901 "MS-MPPE-Send-Key",mppe_sendkey,34);
904 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
905 (inst->require_encryption)? "0x00000002":"0x00000001",
907 rad_assert(reply_attr != NULL);
908 pairadd(&request->reply->vps, reply_attr);
909 reply_attr = pairmake("MS-MPPE-Encryption-Types",
910 (inst->require_strong)? "0x00000004":"0x0000006",
912 rad_assert(reply_attr != NULL);
913 pairadd(&request->reply->vps, reply_attr);
919 DEBUG2("rlm_mschap: Authentication failed");
923 add_reply( &request->reply->vps, *response->strvalue,
924 "MS-CHAP-Error", "E=691 R=1", 9);
925 DEBUG2("rlm_mschap: Nothing in the packet I recognise: Rejecting the user");
927 return RLM_MODULE_REJECT;
931 module_t rlm_mschap = {
933 RLM_TYPE_THREAD_SAFE, /* type */
934 NULL, /* initialize */
935 mschap_instantiate, /* instantiation */
937 mschap_authenticate, /* authenticate */
938 mschap_authorize, /* authorize */
939 NULL, /* pre-accounting */
940 NULL, /* accounting */
941 NULL /* checksimul */
943 mschap_detach, /* detach */