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"
62 #include "rad_assert.h"
64 #define PW_MSCHAP_RESPONSE ((311 << 16) | 1)
65 #define PW_MSCHAP_CHALLENGE ((311 << 16) | 11)
66 #define PW_MSCHAP2_RESPONSE ((311 << 16) | 25)
75 static void parity_key(char * szOut, const char * szIn);
76 static void des_encrypt(const char *szClear, const char *szKey, char *szOut);
77 static void mschap(const char *szChallenge, struct smb_passwd * smbPasswd, char *szResponse, int bUseNT);
78 static void ntpwdhash (char *szHash, const char *szPassword);
79 static void lmpwdhash (char *szHash, const char *szPassword);
80 static struct smb_passwd *createsmbpw(struct smb_passwd *pw_buf, const char* username, const char *password);
81 static void auth_response(struct smb_passwd * smbPasswd, char *ntresponse,
82 char *peer_challenge, char *auth_challenge,
84 static void challenge_hash( const char* peer_challenge, const char* auth_challenge,
85 const char* user_name, char * challenge );
86 static void mschap2( const char *peer_challenge, const char *auth_challenge,
87 struct smb_passwd * smbPasswd, char *response);
88 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
89 const char* name, const char* value, int len);
91 static void mppe_add_reply(VALUE_PAIR** vp,
92 const char* name, const char* value, int len);
94 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
95 uint8_t *nt_hash,uint8_t *response,
96 uint8_t *sendkey,uint8_t *recvkey);
98 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
99 uint8_t *sendkey,uint8_t *recvkey);
101 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
104 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
105 int keylen,int issend);
108 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
109 uint8_t* salt,uint8_t* enckey,uint8_t* key);
112 void md4_calc (unsigned char *, unsigned char *, unsigned int);
117 * parity_key takes a 7-byte string in szIn and returns an
118 * 8-byte string in szOut. It inserts a 1 into every 8th bit.
119 * DES just strips these back out.
121 static void parity_key(char * szOut, const char * szIn)
124 unsigned char cNext = 0;
125 unsigned char cWorking = 0;
127 for (i = 0; i < 7; i++) {
128 /* Shift operator works in place. Copy the char out */
130 szOut[i] = (cWorking >> i) | cNext | 1;
132 cNext = (cWorking << (7 - i));
134 szOut[i] = cNext | 1;
138 * des_encrypt takes an 8-byte string and a 7-byte key and
139 * returns an 8-byte DES encrypted string in szOut
141 static void des_encrypt(const char *szClear, const char *szKey, char *szOut)
144 unsigned long ulK[16][2];
146 parity_key(szParityKey, szKey); /* Insert parity bits */
147 strncpy(szOut, szClear, 8); /* des encrypts in place */
148 deskey(ulK, (unsigned char *) szParityKey, 0); /* generate keypair */
149 des(ulK, szOut); /* encrypt */
155 * ntpwdhash converts Unicode password to 16-byte NT hash
158 static void ntpwdhash (char *szHash, const char *szPassword)
160 char szUnicodePass[513];
165 * NT passwords are unicode. Convert plain text password
166 * to unicode by inserting a zero every other byte
168 nPasswordLen = strlen(szPassword);
169 for (i = 0; i < nPasswordLen; i++) {
170 szUnicodePass[i << 1] = szPassword[i];
171 szUnicodePass[(i << 1) + 1] = 0;
174 /* Encrypt Unicode password to a 16-byte MD4 hash */
175 md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
181 * lmpwdhash converts 14-byte null-padded uppercase OEM
182 * password to 16-byte DES hash with predefined salt string
184 static void lmpwdhash (char *szHash, const char *szPassword)
187 char stdText[] = "KGS!@#$%";
190 memset(szOEMPass, 0, 14);
191 for (i = 0; i < 14 && szPassword[i]; i++)
192 szOEMPass[i] = toupper(szPassword[i]);
194 /* Obtain DES hash of OEM password */
195 des_encrypt(stdText, szOEMPass, szHash);
196 des_encrypt(stdText, szOEMPass+7, szHash+8);
200 * createsmbpw() creates smb_passwd structure from given
201 * user name and cleartext or ntlm-encrypter password
202 * if encrypted flag is not set only cleartext password
205 static struct smb_passwd *createsmbpw(struct smb_passwd *pw_buf, const char * username, const char *password)
210 pdb_init_smb(pw_buf);
211 pw_buf->acct_ctrl = ACB_NORMAL;
212 pw_buf->smb_userid = 0;
213 setsmbname(pw_buf,username);
215 if (pw_buf->smb_passwd==NULL && pw_buf->smb_nt_passwd==NULL) {
216 ntpwdhash(pw_buf->smb_nt_passwd_value, password);
217 lmpwdhash(pw_buf->smb_passwd_value, password);
218 pw_buf->smb_passwd = pw_buf->smb_passwd_value;
219 pw_buf->smb_nt_passwd = pw_buf->smb_nt_passwd_value;
227 * mschap takes an 8-byte challenge string and SMB password
228 * and returns a 24-byte response string in szResponse
230 static void mschap(const char *szChallenge, struct smb_passwd * smbPasswd,
231 char *szResponse, int bUseNT) {
235 /* initialize hash string */
236 memset(szMD4, 0, 21);
238 memcpy(szMD4, (bUseNT)?
239 smbPasswd->smb_nt_passwd : smbPasswd->smb_passwd, 16);
243 * challenge_response takes an 8-byte challenge string and a
244 * 21-byte hash (16-byte hash padded to 21 bytes with zeros) and
245 * returns a 24-byte response in szResponse
247 des_encrypt(szChallenge, szMD4, szResponse);
248 des_encrypt(szChallenge, szMD4 + 7, szResponse + 8);
249 des_encrypt(szChallenge, szMD4 + 14, szResponse + 16);
254 * challenge_hash() is used by mschap2() and auth_response()
255 * implements RFC2759 ChallengeHash()
256 * generates 64 bit challenge
258 static void challenge_hash( const char* peer_challenge, const char* auth_challenge,
259 const char* user_name, char * challenge )
265 SHA1Update(&Context, peer_challenge, 16);
266 SHA1Update(&Context, auth_challenge, 16);
267 SHA1Update(&Context, user_name, strlen(user_name));
268 SHA1Final(hash, &Context);
269 memcpy(challenge, hash, 8);
272 static void mschap2( const char *peer_challenge, const char *auth_challenge,
273 struct smb_passwd * smbPasswd, char *response)
277 challenge_hash(peer_challenge, auth_challenge, smbPasswd->smb_name,
279 mschap(challenge, smbPasswd, response, 1);
283 * auth_response() generates MS-CHAP v2 SUCCESS response
284 * according to RFC 2759 GenerateAuthenticatorResponse()
285 * returns 42-octet response string
287 static void auth_response(struct smb_passwd * smbPasswd, char *ntresponse,
288 char *peer_challenge, char *auth_challenge,
294 {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
295 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
296 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
297 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
300 {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
301 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
302 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
303 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
309 * Hash password hash into hashhash
312 md4_calc(hashhash, smbPasswd->smb_nt_passwd, 16);
315 SHA1Update(&Context, hashhash, 16);
316 SHA1Update(&Context, ntresponse, 24);
317 SHA1Update(&Context, magic1, 39);
318 SHA1Final(digest, &Context);
319 challenge_hash(peer_challenge, auth_challenge, smbPasswd->smb_name,
322 SHA1Update(&Context, digest, 20);
323 SHA1Update(&Context, challenge, 8);
324 SHA1Update(&Context, magic2, 41);
325 SHA1Final(digest, &Context);
328 * Encode the value of 'Digest' as "S=" followed by
329 * 40 ASCII hexadecimal digits and return it in
330 * AuthenticatorResponse.
332 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
337 bin2hex(digest, response + 2, 20);
340 struct mschap_instance {
343 int require_encryption;
349 static CONF_PARSER module_config[] = {
351 * Cache the password by default.
353 { "ignore_password", PW_TYPE_BOOLEAN,
354 offsetof(struct mschap_instance,ignore_password), NULL, "no" },
355 { "use_mppe", PW_TYPE_BOOLEAN,
356 offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
357 { "require_encryption", PW_TYPE_BOOLEAN,
358 offsetof(struct mschap_instance,require_encryption), NULL, "no" },
359 { "require_strong", PW_TYPE_BOOLEAN,
360 offsetof(struct mschap_instance,require_strong), NULL, "no" },
361 { "passwd", PW_TYPE_STRING_PTR,
362 offsetof(struct mschap_instance, passwd_file), NULL, NULL },
363 { "authtype", PW_TYPE_STRING_PTR,
364 offsetof(struct mschap_instance, auth_type), NULL, NULL },
366 { NULL, -1, 0, NULL, NULL } /* end the list */
370 * Create instance for our module. Allocate space for
371 * instance structure and read configuration parameters
373 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
375 struct mschap_instance *inst;
377 inst = *instance = rad_malloc(sizeof(struct mschap_instance));
378 if (cf_section_parse(conf, inst, module_config) < 0) {
386 * deinstantiate module, free all memory allocated during
387 * mschap_instantiate()
389 static int mschap_detach(void *instance){
390 #define inst ((struct mschap_instance *)instance)
391 if (inst->passwd_file) free(inst->passwd_file);
392 if (inst->auth_type) free(inst->auth_type);
399 * add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
400 * attribute to reply packet
402 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
403 const char* name, const char* value, int len)
405 VALUE_PAIR *reply_attr;
406 reply_attr = pairmake(name, "", T_OP_EQ);
408 DEBUG("rlm_mschap: add_reply failed to create attribute %s: %s\n", name, librad_errstr);
412 reply_attr->strvalue[0] = ident;
413 memcpy(reply_attr->strvalue + 1, value, len);
414 reply_attr->length = len + 1;
415 pairadd(vp, reply_attr);
418 static void mppe_add_reply(VALUE_PAIR** vp,
419 const char* name, const char* value, int len)
421 VALUE_PAIR *reply_attr;
422 reply_attr = pairmake(name, "", T_OP_EQ);
424 DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
428 memcpy(reply_attr->strvalue, value, len);
429 reply_attr->length = len;
430 pairadd(vp, reply_attr);
433 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
434 uint8_t *nt_hash,uint8_t *response,
435 uint8_t *sendkey,uint8_t *recvkey)
439 /* uint8_t salt[2]; */
440 uint8_t nt_hashhash[16];
442 md4_calc(nt_hashhash,nt_hash,16);
444 mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
446 salt[0] = (vector[0] ^ vector[1] ^ 0x3A) | 0x80;
447 salt[1] = (vector[2] ^ vector[3] ^ vector[4]);
449 mppe_gen_respkey(secret,vector,salt,enckey1,sendkey);
451 salt[0] = (vector[0] ^ vector[1] ^ 0x4e) | 0x80;
452 salt[1] = (vector[5] ^ vector[6] ^ vector[7]);
454 mppe_gen_respkey(secret,vector,salt,enckey2,recvkey);
456 memcpy (sendkey, enckey1, 16);
457 memcpy (recvkey, enckey2, 16);
460 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
461 uint8_t *sendkey,uint8_t *recvkey)
463 uint8_t masterkey[16];
465 mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
467 mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
468 mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
471 static uint8_t SHSpad1[40] =
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,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
477 static uint8_t SHSpad2[40] =
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,
481 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
483 static uint8_t magic1[27] =
484 { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
485 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
486 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
488 static uint8_t magic2[84] =
489 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
490 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
491 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
492 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
493 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
494 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
495 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
496 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
497 0x6b, 0x65, 0x79, 0x2e };
499 static uint8_t magic3[84] =
500 { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
501 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
502 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
503 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
504 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
505 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
506 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
507 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
508 0x6b, 0x65, 0x79, 0x2e };
511 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
518 SHA1Update(&Context,nt_hashhash,16);
519 SHA1Update(&Context,nt_response,24);
520 SHA1Update(&Context,magic1,27);
521 SHA1Final(digest,&Context);
523 memcpy(masterkey,digest,16);
527 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
528 int keylen,int issend)
543 SHA1Update(&Context,masterkey,16);
544 SHA1Update(&Context,SHSpad1,40);
545 SHA1Update(&Context,s,84);
546 SHA1Update(&Context,SHSpad2,40);
547 SHA1Final(digest,&Context);
549 memcpy(sesskey,digest,keylen);
554 /* Not requiered, because encoding will be performed by
556 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
557 uint8_t* salt,uint8_t* enckey,uint8_t* key)
563 int slen = strlen(secret);
569 memcpy(plain + 1,enckey,16);
572 MD5Update(&Context,secret,slen);
573 MD5Update(&Context,vector,AUTH_VECTOR_LEN);
574 MD5Update(&Context,salt,2);
575 MD5Final(buf,&Context);
577 for(i=0;i < 16;i++) {
582 MD5Update(&Context,secret,slen);
583 MD5Update(&Context,plain,16);
584 MD5Final(buf,&Context);
586 for(i=0;i < 16;i++) {
587 plain[i + 16] ^= buf[i];
591 memcpy(key + 2,plain,32);
597 * mschap_authorize() - authorize user if we can authenticate
598 * it later. Add Auth-Type attribute if present in module
599 * configuration (usually Auth-Type must be "MS-CHAP")
601 static int mschap_authorize(void * instance, REQUEST *request)
603 #define inst ((struct mschap_instance *)instance)
604 VALUE_PAIR *challenge = NULL, *response = NULL;
605 VALUE_PAIR *reply_attr;
606 VALUE_PAIR *password = NULL;
607 struct smb_passwd smbPasswdValue, *smbPasswd = NULL;
610 password = pairfind(request->config_items, PW_PASSWORD);
611 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
613 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
615 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
617 if (password && (!challenge || !response)) {
618 /* We have nothing related to MS-CHAP or NTLM */
619 return RLM_MODULE_NOOP;
621 if (!request->username || *request->username->strvalue == 0) {
622 /* Usernam must present */
623 return RLM_MODULE_NOOP;
625 if (password && !inst->ignore_password)
626 smbPasswd = createsmbpw(&smbPasswdValue, request->username->strvalue, password->strvalue);
627 else if (inst->passwd_file) {
628 smbPasswd = getsmbfilepwname (&smbPasswdValue, inst->passwd_file, request->username->strvalue);
630 if (!smbPasswd || !smbPasswd->acct_ctrl&ACB_NORMAL ||smbPasswd->acct_ctrl&ACB_DISABLED) {
631 if(challenge && response){
632 add_reply( &request->reply->vps, *response->strvalue,
633 "MS-CHAP-Error", "E=691 R=1", 9);
635 return RLM_MODULE_NOTFOUND;
637 if (inst->auth_type){
638 pairdelete(&request->config_items, PW_AUTHTYPE);
639 reply_attr = pairmake("Auth-Type", inst->auth_type, T_OP_EQ);
640 rad_assert(reply_attr != NULL);
641 pairadd(&request->config_items, reply_attr);
643 if (smbPasswd->smb_passwd){
644 reply_attr = pairmake("LM-Password", "", T_OP_EQ);
645 rad_assert(reply_attr != NULL);
646 reply_attr->length = 16;
647 memcpy(reply_attr->strvalue, smbPasswd->smb_passwd, 16);
648 pairadd(&request->config_items, reply_attr);
650 if (smbPasswd->smb_nt_passwd){
651 reply_attr = pairmake("NT-Password", "", T_OP_EQ);
652 rad_assert(reply_attr != NULL);
653 reply_attr->length = 16;
654 memcpy(reply_attr->strvalue, smbPasswd->smb_nt_passwd, 16);
655 pairadd(&request->config_items, reply_attr);
658 reply_attr = pairmake("SMB-Account-CTRL", "0", T_OP_EQ);
659 rad_assert(reply_attr != NULL);
660 reply_attr->lvalue = smbPasswd->acct_ctrl;
661 pairadd(&request->config_items, reply_attr);
662 return RLM_MODULE_OK;
668 * mschap_authenticate() - authenticate user based on given
669 * attributes and configuration.
670 * We will try to find out password in configuration
671 * or in configured passwd file.
672 * If one is found we will check paraneters given by NAS.
674 * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
676 * PAP: PW_PASSWORD or
677 * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
678 * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
679 * In case of password mismatch or locked account we MAY return
680 * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
681 * If MS-CHAP2 succeeds we MUST return
684 static int mschap_authenticate(void * instance, REQUEST *request)
686 #define inst ((struct mschap_instance *)instance)
687 VALUE_PAIR *challenge = NULL, *response = NULL;
688 VALUE_PAIR *password = NULL;
689 VALUE_PAIR *reply_attr;
690 uint8_t calculated[32];
691 uint8_t msch2resp[42];
692 uint8_t mppe_sendkey[34];
693 uint8_t mppe_recvkey[34];
694 struct smb_passwd smbPasswd, smbPasswd1Value, *smbPasswd1 = NULL;
702 pdb_init_smb(&smbPasswd);
703 setsmbname(&smbPasswd,request->username->strvalue);
704 password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
706 smbPasswd.acct_ctrl = password->lvalue;
707 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;
714 password = pairfind(request->config_items, PW_LM_PASSWORD);
716 if(password->length == 16) {
717 smbPasswd.smb_passwd = password->strvalue;
720 else if(hex2bin(password->strvalue,smbPasswd.smb_passwd_value,16) != 16) {
721 radlog(L_ERR, "rlm_mschap: Invalid LM Password text");
725 smbPasswd.smb_passwd = smbPasswd.smb_passwd_value;
729 password = pairfind(request->config_items, PW_NT_PASSWORD);
731 if(password->length == 16){
732 smbPasswd.smb_nt_passwd = password->strvalue;
735 else if(hex2bin(password->strvalue,smbPasswd.smb_nt_passwd_value,16) != 16) {
736 radlog(L_ERR, "rlm_mschap: Invalid NT Password text");
739 smbPasswd.smb_nt_passwd = smbPasswd.smb_nt_passwd_value;
745 * We have neither NT nor LM passwords configured
747 radlog(L_ERR, "rlm_mschap: No LM/NT password configured. Check authorization.");
748 return RLM_MODULE_INVALID;
752 * If NAS sent cleartext password - encode it and check
753 * only against passwd file. If either NT or LM hash match
757 password = pairfind(request->packet->vps, PW_PASSWORD);
758 if (password && request->username && *request->username->strvalue!= 0) {
760 smbPasswd1 = createsmbpw(&smbPasswd1Value,request->username->strvalue, password->strvalue);
761 if ( (smbPasswd.smb_passwd && !memcmp(smbPasswd1->smb_passwd, smbPasswd.smb_passwd, 16)) ||
762 (smbPasswd.smb_nt_passwd && !memcmp(smbPasswd1->smb_nt_passwd, smbPasswd.smb_nt_passwd, 16)) )
763 return RLM_MODULE_OK;
764 else return RLM_MODULE_REJECT;
766 else if ( (challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE)) ){
768 * We need an MS-CHAP-Challenge attribute to calculate
771 res = RLM_MODULE_REJECT;
772 if ( (response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE)) ){
773 if (response->length < 50 || challenge->length < 8) {
774 radlog(L_AUTH, "rlm_mschap: Attribute \"MS-CHAP-Response\" has wrong format.");
775 return RLM_MODULE_INVALID;
778 * We are doing MS-CHAP
779 * Calculate the MS-CHAP response
781 if (smbPasswd.smb_nt_passwd && (response->strvalue[1] & 0x01)) {
783 * Try NT response first if UseNT flag is set
785 mschap(challenge->strvalue, &smbPasswd, calculated, 1);
786 if (memcmp(response->strvalue + 26, calculated, 24) == 0) {
791 if (res != RLM_MODULE_OK && smbPasswd.smb_passwd) {
795 mschap(challenge->strvalue, &smbPasswd,
797 if (memcmp(response->strvalue + 2, calculated, 24) == 0) {
803 else if ( (response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) ){
804 if (response->length < 50 || challenge->length < 16) {
805 radlog(L_AUTH, "rlm_mschap: Attribute \"MS-CHAP2-Response\" has wrong format.");
806 return RLM_MODULE_INVALID;
809 * We are doing MS-CHAPv2
810 * We need NT hash for it to calculate response
812 if (smbPasswd.smb_nt_passwd) {
813 mschap2(response->strvalue + 2, challenge->strvalue,
814 &smbPasswd, calculated);
815 if (memcmp(response->strvalue + 26, calculated, 24) == 0) {
816 auth_response(&smbPasswd, calculated,
817 response->strvalue + 2,
820 add_reply( &request->reply->vps, *response->strvalue,
821 "MS-CHAP2-Success", msch2resp, 42);
828 radlog(L_AUTH, "rlm_mschap: Response attribute is not found");
829 return RLM_MODULE_INVALID;
831 if (res == RLM_MODULE_OK){
832 if (smbPasswd.acct_ctrl&ACB_AUTOLOCK) {
833 add_reply( &request->reply->vps, *response->strvalue,
834 "MS-CHAP-Error", "E=647 R=0", 9);
835 return RLM_MODULE_USERLOCK;
838 /* now create MPPE attributes */
839 if (inst->use_mppe) {
841 memset (mppe_sendkey, 0, 32);
842 if (smbPasswd.smb_passwd)
843 memcpy(mppe_sendkey, smbPasswd.smb_passwd, 8);
844 if (smbPasswd.smb_nt_passwd)
846 According to RFC 2548 we should send NT hash.
847 But in practice it doesn't work and we should
848 send nthashhash instead
849 If someone have different information please
850 feel free to feedback.
852 memcpy (mppe_sendkey+8,smbPasswd.smb_nt_passwd,16);
854 md4_calc(mppe_sendkey+8, smbPasswd.smb_nt_passwd,16);
856 rad_pwencode(mppe_sendkey, &len,
857 request->secret, request->packet->vector);
858 mppe_add_reply( &request->reply->vps,
859 "MS-CHAP-MPPE-Keys",mppe_sendkey,len);
862 mppe_chap2_gen_keys128(request->secret,request->packet->vector,
863 smbPasswd.smb_nt_passwd,
864 response->strvalue + 26,
865 mppe_sendkey,mppe_recvkey);
867 mppe_add_reply( &request->reply->vps,
868 "MS-MPPE-Recv-Key",mppe_recvkey,16);
869 mppe_add_reply( &request->reply->vps,
870 "MS-MPPE-Send-Key",mppe_sendkey,16);
873 mppe_add_reply( &request->reply->vps,
874 "MS-MPPE-Recv-Key",mppe_recvkey,34);
875 mppe_add_reply( &request->reply->vps,
876 "MS-MPPE-Send-Key",mppe_sendkey,34);
879 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
880 (inst->require_encryption)? "0x00000002":"0x00000001",
882 rad_assert(reply_attr != NULL);
883 pairadd(&request->reply->vps, reply_attr);
884 reply_attr = pairmake("MS-MPPE-Encryption-Types",
885 (inst->require_strong)? "0x00000004":"0x0000006",
887 rad_assert(reply_attr != NULL);
888 pairadd(&request->reply->vps, reply_attr);
895 add_reply( &request->reply->vps, *response->strvalue,
896 "MS-CHAP-Error", "E=691 R=1", 9);
897 return RLM_MODULE_REJECT;
901 module_t rlm_mschap = {
903 RLM_TYPE_THREAD_SAFE, /* type */
904 NULL, /* initialize */
905 mschap_instantiate, /* instantiation */
907 mschap_authenticate, /* authenticate */
908 mschap_authorize, /* authorize */
909 NULL, /* pre-accounting */
910 NULL, /* accounting */
911 NULL /* checksimul */
913 mschap_detach, /* detach */