c3bad12a1e2e5baaba80e6e13d83c19913844e59
[freeradius.git] / src / modules / rlm_mschap / rlm_mschap.c
1 /*
2  * rlm_mschap.c 
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000,2001  The FreeRADIUS server project
21  */
22
23
24 /*
25  *  mschap.c    MS-CHAP module
26  *
27  *  Jay Miller  jaymiller@socket.net
28  *
29  *  This implements MS-CHAP, as described in RFC 2548
30  *
31  *  http://www.freeradius.org/rfc/rfc2548.txt
32  *
33  */
34
35 /*
36  *  If you have any questions on NTLM (Samba) passwords
37  *  support, LM authentication and MS-CHAP v2 support
38  *  please contact
39  *
40  *  Vladimir Dubrovin   vlad@sandy.ru
41  *  aka
42  *  ZARAZA              3APA3A@security.nnov.ru
43  */
44  
45 /*  MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
46
47 #include        "autoconf.h"
48 #include        "libradius.h"
49
50 #include        <stdio.h>
51 #include        <stdlib.h>
52 #include        <string.h>
53 #include        <ctype.h>
54
55 #include        "radiusd.h"
56 #include        "modules.h"
57
58 #include        "des.h"
59 #include        "md5.h"
60 #include        "sha1.h"
61 #include        "smbpass.h"
62 #include        "rad_assert.h"
63
64 #define PW_MSCHAP_RESPONSE      ((311 << 16) | 1)
65 #define PW_MSCHAP_CHALLENGE     ((311 << 16) | 11)
66 #define PW_MSCHAP2_RESPONSE     ((311 << 16) | 25)
67
68
69 typedef enum {
70         NONE,
71         CLEARTEXT,
72         MSCHAP1,
73         MSCHAP2} AUTHTYPE;
74
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,
83                 char *response);
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);
90
91 static void mppe_add_reply(VALUE_PAIR** vp,
92                const char* name, const char* value, int len);
93
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);
97
98 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
99                                uint8_t *sendkey,uint8_t *recvkey);
100
101 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
102                        uint8_t *masterkey);
103
104 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
105                                int keylen,int issend);
106
107 #if 0
108 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
109                        uint8_t* salt,uint8_t* enckey,uint8_t* key);
110 #endif
111
112 void md4_calc (unsigned char *, unsigned char *, unsigned int);
113
114
115
116 /* 
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.
120  */
121 static void parity_key(char * szOut, const char * szIn)
122 {
123         int i;
124         unsigned char cNext = 0;
125         unsigned char cWorking = 0;
126         
127         for (i = 0; i < 7; i++) {
128                 /* Shift operator works in place.  Copy the char out */
129                 cWorking = szIn[i];
130                 szOut[i] = (cWorking >> i) | cNext | 1;
131                 cWorking = szIn[i];
132                 cNext = (cWorking << (7 - i));
133         }
134         szOut[i] = cNext | 1;
135 }
136
137 /*
138  *      des_encrypt takes an 8-byte string and a 7-byte key and
139  *      returns an 8-byte DES encrypted string in szOut
140  */
141 static void des_encrypt(const char *szClear, const char *szKey, char *szOut)
142 {
143         char szParityKey[9];
144         unsigned long ulK[16][2];
145         
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 */
150 }
151
152
153
154 /*
155  *      ntpwdhash converts Unicode password to 16-byte NT hash
156  *      with MD4
157  */
158 static void ntpwdhash (char *szHash, const char *szPassword)
159 {
160         char szUnicodePass[513];
161         char nPasswordLen;
162         int i;
163
164         /*
165          *      NT passwords are unicode.  Convert plain text password
166          *      to unicode by inserting a zero every other byte
167          */
168         nPasswordLen = strlen(szPassword);
169         for (i = 0; i < nPasswordLen; i++) {
170                 szUnicodePass[i << 1] = szPassword[i];
171                 szUnicodePass[(i << 1) + 1] = 0;
172         }
173
174         /* Encrypt Unicode password to a 16-byte MD4 hash */
175         md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
176 }
177
178
179
180 /*
181  *      lmpwdhash converts 14-byte null-padded uppercase OEM
182  *      password to 16-byte DES hash with predefined salt string
183  */
184 static void lmpwdhash (char *szHash, const char *szPassword)
185 {
186         char szOEMPass[14];
187         char stdText[] = "KGS!@#$%";
188         int i;
189
190         memset(szOEMPass, 0, 14);
191         for (i = 0; i < 14 && szPassword[i]; i++)
192                 szOEMPass[i] = toupper(szPassword[i]);
193
194         /* Obtain DES hash of OEM password */
195         des_encrypt(stdText, szOEMPass, szHash); 
196         des_encrypt(stdText, szOEMPass+7, szHash+8);
197 }
198
199 /*
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
203  *      allowed
204  */
205 static struct smb_passwd *createsmbpw(struct smb_passwd *pw_buf, const char * username, const char *password)
206 {
207         if(pw_buf == NULL) {
208                 return NULL;
209         }
210         pdb_init_smb(pw_buf);
211         pw_buf->acct_ctrl = ACB_NORMAL;
212         pw_buf->smb_userid = 0;
213         setsmbname(pw_buf,username);
214   
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;
220         }
221         return pw_buf;
222 }
223
224
225
226 /*
227  *      mschap takes an 8-byte challenge string and SMB password
228  *      and returns a 24-byte response string in szResponse
229  */
230 static void mschap(const char *szChallenge, struct smb_passwd * smbPasswd,
231         char *szResponse, int bUseNT) {
232
233         char szMD4[21];
234         
235         /* initialize hash string */
236         memset(szMD4, 0, 21);
237         
238         memcpy(szMD4, (bUseNT)?
239                 smbPasswd->smb_nt_passwd : smbPasswd->smb_passwd, 16);
240         
241         /*
242          *
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
246          */
247         des_encrypt(szChallenge, szMD4, szResponse);
248         des_encrypt(szChallenge, szMD4 + 7, szResponse + 8);
249         des_encrypt(szChallenge, szMD4 + 14, szResponse + 16);
250 }   
251
252
253 /*
254  *      challenge_hash() is used by mschap2() and auth_response()
255  *      implements RFC2759 ChallengeHash()
256  *      generates 64 bit challenge
257  */
258 static void challenge_hash( const char* peer_challenge, const char* auth_challenge,
259                      const char* user_name, char * challenge )
260 {
261         SHA1_CTX Context;
262         char hash[20];
263         
264         SHA1Init(&Context);
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);
270 }
271
272 static void mschap2( const char *peer_challenge, const char *auth_challenge,
273                 struct smb_passwd * smbPasswd, char *response)
274 {
275         char challenge[8];
276         
277         challenge_hash(peer_challenge, auth_challenge, smbPasswd->smb_name,
278                 challenge);
279         mschap(challenge, smbPasswd, response, 1);
280 }
281
282 /*
283  *      auth_response() generates MS-CHAP v2 SUCCESS response
284  *      according to RFC 2759 GenerateAuthenticatorResponse()
285  *      returns 42-octet response string
286  */
287 static void auth_response(struct smb_passwd * smbPasswd, char *ntresponse,
288                 char *peer_challenge, char *auth_challenge,
289                 char *response)
290 {
291         SHA1_CTX Context;
292         char hashhash[16];
293         char magic1[39] =
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};
298                                              
299         char magic2[41] =
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,
304                 0x6E};
305         char challenge[8];
306         char digest[20];
307
308         /*
309          * Hash password hash into hashhash
310          */
311
312         md4_calc(hashhash, smbPasswd->smb_nt_passwd, 16);
313
314         SHA1Init(&Context);
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,
320                 challenge);
321         SHA1Init(&Context);
322         SHA1Update(&Context, digest, 20);
323         SHA1Update(&Context, challenge, 8);
324         SHA1Update(&Context, magic2, 41);
325         SHA1Final(digest, &Context);
326
327         /*
328          * Encode the value of 'Digest' as "S=" followed by
329          * 40 ASCII hexadecimal digits and return it in
330          * AuthenticatorResponse.
331          * For example,
332          *   "S=0123456789ABCDEF0123456789ABCDEF01234567"
333          */
334  
335         response[0] = 'S';
336         response[1] = '=';
337         bin2hex(digest, response + 2, 20);
338 }
339
340 struct mschap_instance {
341         int ignore_password;
342         int use_mppe;
343         int require_encryption;
344         int require_strong;
345         char *passwd_file;
346         char *auth_type;
347 };
348
349 static CONF_PARSER module_config[] = {
350         /*
351          *      Cache the password by default.
352          */
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 },
365         
366         { NULL, -1, 0, NULL, NULL }             /* end the list */
367 };
368
369 /*
370  *      Create instance for our module. Allocate space for
371  *      instance structure and read configuration parameters
372  */
373 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
374 {
375         struct mschap_instance *inst;
376
377         inst = *instance = rad_malloc(sizeof(struct mschap_instance));
378         if (cf_section_parse(conf, inst, module_config) < 0) {
379                 free(inst);
380                 return -1;
381         }
382         return 0;
383 }
384
385 /*
386  *      deinstantiate module, free all memory allocated during
387  *      mschap_instantiate()
388  */
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);
393         free(instance);
394         return 0;
395 #undef inst
396 }
397         
398 /*
399  *      add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
400  *      attribute to reply packet
401  */
402 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
403                 const char* name, const char* value, int len)
404 {
405         VALUE_PAIR *reply_attr;
406         reply_attr = pairmake(name, "", T_OP_EQ);
407         if (!reply_attr) {
408                 DEBUG("rlm_mschap: add_reply failed to create attribute %s: %s\n", name, librad_errstr);
409                 return;
410         }
411
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);
416 }
417
418 static void mppe_add_reply(VALUE_PAIR** vp,
419                        const char* name, const char* value, int len)
420 {
421        VALUE_PAIR *reply_attr;
422        reply_attr = pairmake(name, "", T_OP_EQ);
423        if (!reply_attr) {
424                DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
425                return;
426        }
427
428        memcpy(reply_attr->strvalue, value, len);
429        reply_attr->length = len;
430        pairadd(vp, reply_attr);
431 }
432
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)
436 {
437         uint8_t enckey1[16];
438         uint8_t enckey2[16];
439         /* uint8_t salt[2]; */
440         uint8_t nt_hashhash[16];
441
442         md4_calc(nt_hashhash,nt_hash,16);
443
444         mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
445 #if 0
446         salt[0] = (vector[0] ^ vector[1] ^ 0x3A) | 0x80;
447         salt[1] = (vector[2] ^ vector[3] ^ vector[4]);
448
449         mppe_gen_respkey(secret,vector,salt,enckey1,sendkey);
450
451         salt[0] = (vector[0] ^ vector[1] ^ 0x4e) | 0x80;
452         salt[1] = (vector[5] ^ vector[6] ^ vector[7]);
453
454         mppe_gen_respkey(secret,vector,salt,enckey2,recvkey);
455 #endif
456         memcpy (sendkey, enckey1, 16);
457         memcpy (recvkey, enckey2, 16);
458 }
459
460 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
461                                uint8_t *sendkey,uint8_t *recvkey)
462 {
463        uint8_t masterkey[16];
464
465        mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
466
467        mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
468        mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
469 }
470
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 };
476
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 };
482
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 };
487
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 };
498
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 };
509
510
511 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
512                        uint8_t *masterkey)
513 {
514        uint8_t digest[20];
515        SHA1_CTX Context;
516
517        SHA1Init(&Context);
518        SHA1Update(&Context,nt_hashhash,16);
519        SHA1Update(&Context,nt_response,24);
520        SHA1Update(&Context,magic1,27);
521        SHA1Final(digest,&Context);
522
523        memcpy(masterkey,digest,16);
524 }
525
526
527 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
528                                int keylen,int issend)
529 {
530        uint8_t digest[20];
531        uint8_t *s;
532        SHA1_CTX Context;
533
534        memset(digest,0,20);
535
536        if(issend) {
537                s = magic3;
538        } else {
539                s = magic2;
540        }
541
542        SHA1Init(&Context);
543        SHA1Update(&Context,masterkey,16);
544        SHA1Update(&Context,SHSpad1,40);
545        SHA1Update(&Context,s,84);
546        SHA1Update(&Context,SHSpad2,40);
547        SHA1Final(digest,&Context);
548
549        memcpy(sesskey,digest,keylen);
550 }
551
552
553 #if 0
554 /*      Not requiered, because encoding will be performed by
555         tunnel_pwencode */
556 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
557                        uint8_t* salt,uint8_t* enckey,uint8_t* key)
558 {
559        uint8_t plain[32];
560        uint8_t buf[16];
561        int i;
562        MD5_CTX Context;
563        int slen = strlen(secret);
564
565        memset(key,0,34);
566
567        memset(plain,0,32);
568        plain[0] = 16;
569        memcpy(plain + 1,enckey,16);
570
571        MD5Init(&Context);
572        MD5Update(&Context,secret,slen);
573        MD5Update(&Context,vector,AUTH_VECTOR_LEN);
574        MD5Update(&Context,salt,2);
575        MD5Final(buf,&Context);
576
577        for(i=0;i < 16;i++) {
578                plain[i] ^= buf[i];
579        }
580
581        MD5Init(&Context);
582        MD5Update(&Context,secret,slen);
583        MD5Update(&Context,plain,16);
584        MD5Final(buf,&Context);
585
586        for(i=0;i < 16;i++) {
587                plain[i + 16] ^= buf[i];
588        }
589
590        memcpy(key,salt,2);
591        memcpy(key + 2,plain,32);
592 }
593 #endif /* 0 */
594
595
596 /*
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")
600  */
601 static int mschap_authorize(void * instance, REQUEST *request)
602 {
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;
608
609         
610         password = pairfind(request->config_items, PW_PASSWORD);
611         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
612         if (challenge) {
613                 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
614                 if (!response)
615                         response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
616         }
617         if (password && (!challenge || !response)) {
618                 /*  We have nothing related to MS-CHAP or NTLM */
619                 return RLM_MODULE_NOOP;
620         }
621         if (!request->username || *request->username->strvalue == 0) {
622                 /* Usernam must present */
623                 return RLM_MODULE_NOOP;
624         }
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);
629         }
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);
634                 }
635                 return RLM_MODULE_NOTFOUND;
636         }
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);
642         }
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);
649         }
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);
656         }
657
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;
663 #undef inst
664 }
665
666
667 /*
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.
673  *
674  *      If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have 
675  *      one of:
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
682  *      PW_MSCHAP2_SUCCESS
683  */
684 static int mschap_authenticate(void * instance, REQUEST *request)
685 {
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;
695         AUTHTYPE at = NONE;
696         int res = 0;
697         int len = 0;
698         int chap = 0;
699         
700         
701         
702         pdb_init_smb(&smbPasswd);
703         setsmbname(&smbPasswd,request->username->strvalue);
704         password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
705         if(password){
706                 smbPasswd.acct_ctrl = password->lvalue;
707                 if (smbPasswd.acct_ctrl&ACB_PWNOTREQ) return RLM_MODULE_OK;
708         }
709         password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT);
710         if(password) {
711                 smbPasswd.acct_ctrl = pdb_decode_acct_ctrl(password->strvalue);
712                 if (smbPasswd.acct_ctrl&ACB_PWNOTREQ) return RLM_MODULE_OK;
713         }
714         password = pairfind(request->config_items, PW_LM_PASSWORD);
715         if(password){
716                 if(password->length == 16) {
717                         smbPasswd.smb_passwd = password->strvalue;
718                         res++;
719                 }
720                 else if(hex2bin(password->strvalue,smbPasswd.smb_passwd_value,16) != 16) {
721                         radlog(L_ERR, "rlm_mschap: Invalid LM Password text");
722                 } 
723                 else {
724                         res++;
725                         smbPasswd.smb_passwd = smbPasswd.smb_passwd_value;
726                 }
727                 
728         }
729         password = pairfind(request->config_items, PW_NT_PASSWORD);
730         if(password){
731                 if(password->length == 16){
732                         smbPasswd.smb_nt_passwd = password->strvalue;
733                         res++;
734                 }
735                 else if(hex2bin(password->strvalue,smbPasswd.smb_nt_passwd_value,16) != 16) {
736                         radlog(L_ERR, "rlm_mschap: Invalid NT Password text");
737                 }
738                 else {
739                         smbPasswd.smb_nt_passwd = smbPasswd.smb_nt_passwd_value;
740                         res++;
741                 }
742         }
743         if (!res) {
744         /*
745          * We have neither NT nor LM passwords configured
746          */
747                 radlog(L_ERR, "rlm_mschap: No LM/NT password configured. Check authorization.");
748                 return RLM_MODULE_INVALID;
749          }
750         
751         /*
752          *      If NAS sent cleartext password - encode it and check
753          *      only against passwd file. If either NT or LM hash match
754          *      return OK.
755          */
756
757         password = pairfind(request->packet->vps, PW_PASSWORD);
758         if (password && request->username && *request->username->strvalue!= 0) {
759                 at = CLEARTEXT;
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;
765         }
766         else if ( (challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE)) ){
767                 /*
768                  *      We need an MS-CHAP-Challenge attribute to calculate
769                  *      the response.
770                  */
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;
776                         }
777                         /*
778                          *      We are doing MS-CHAP
779                          *      Calculate the MS-CHAP response
780                          */
781                         if (smbPasswd.smb_nt_passwd && (response->strvalue[1] & 0x01)) {
782                         /*
783                          * Try NT response first if UseNT flag is set
784                          */
785                                 mschap(challenge->strvalue, &smbPasswd, calculated, 1);
786                                 if (memcmp(response->strvalue + 26, calculated, 24) == 0) {
787                                         res = RLM_MODULE_OK;
788                                 }
789                          }
790
791                         if (res != RLM_MODULE_OK && smbPasswd.smb_passwd) {
792                         /*
793                          *      Use LM response.
794                          */
795                                 mschap(challenge->strvalue, &smbPasswd, 
796                                         calculated, 0);
797                                 if (memcmp(response->strvalue + 2, calculated, 24) == 0) {
798                                         res = RLM_MODULE_OK;
799                                 }
800                         }
801                         chap = 1;
802                 }
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;
807                         }
808                         /*
809                          *      We are doing MS-CHAPv2
810                          *      We need NT hash for it to calculate response
811                          */
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,
818                                                 challenge->strvalue,
819                                                 msch2resp);
820                                         add_reply( &request->reply->vps, *response->strvalue,
821                                                 "MS-CHAP2-Success", msch2resp, 42);
822                                         res = RLM_MODULE_OK;
823                                         chap = 2;
824                                 }
825                         }
826                 }
827                 else {
828                         radlog(L_AUTH, "rlm_mschap: Response attribute is not found");
829                         return RLM_MODULE_INVALID;
830                 }
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;
836                         }
837
838                         /* now create MPPE attributes */
839                         if (inst->use_mppe) {
840                                 if (chap == 1){
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)
845                                         /* 
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.
851
852                                                 memcpy (mppe_sendkey+8,smbPasswd.smb_nt_passwd,16);   
853                                         */
854                                                 md4_calc(mppe_sendkey+8, smbPasswd.smb_nt_passwd,16);
855                                         len = 32;
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);
860                                 }
861                                 else if (chap == 2){
862                                         mppe_chap2_gen_keys128(request->secret,request->packet->vector,
863                                                 smbPasswd.smb_nt_passwd,
864                                                 response->strvalue + 26,
865                                                 mppe_sendkey,mppe_recvkey);
866
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);
871
872 /*
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);
877 */
878                                 }
879                                 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
880                                         (inst->require_encryption)? "0x00000002":"0x00000001",
881                                         T_OP_EQ);
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",
886                                         T_OP_EQ);
887                                 rad_assert(reply_attr != NULL);
888                                 pairadd(&request->reply->vps, reply_attr);
889                         
890                         }
891                         return res;
892                 }
893         }
894         
895         add_reply( &request->reply->vps, *response->strvalue,
896                 "MS-CHAP-Error", "E=691 R=1", 9);
897         return RLM_MODULE_REJECT;
898 #undef inst
899 }
900
901 module_t rlm_mschap = {
902   "MS-CHAP",
903   RLM_TYPE_THREAD_SAFE,                         /* type */
904   NULL,                         /* initialize */
905   mschap_instantiate,           /* instantiation */
906   {
907           mschap_authenticate,  /* authenticate */
908           mschap_authorize,     /* authorize */
909           NULL,                 /* pre-accounting */
910           NULL,                 /* accounting */
911           NULL                  /* checksimul */
912   },
913   mschap_detach,                /* detach */
914   NULL,                         /* destroy */
915 };